/* * Convert a previously str-ified and escaped list of tapes back into a * tapelist structure. */ tapelist_t * unmarshal_tapelist_str( char * tapelist_str, int with_storage) { char *temp_storage, *temp_label, *temp_filenum; int l_idx, n_idx; size_t input_length; tapelist_t *tapelist = NULL; if(!tapelist_str) return(NULL); input_length = strlen(tapelist_str); temp_label = g_malloc(input_length+1); temp_storage = g_malloc(input_length+1); temp_filenum = g_malloc(input_length+1); do { /* first, read the storage part */ if (with_storage) { memset(temp_storage, '\0', input_length+1); l_idx = 0; while(*tapelist_str != ':' && *tapelist_str != '\0'){ if(*tapelist_str == '\\') tapelist_str++; /* skip escapes */ temp_storage[l_idx] = *tapelist_str; if(*tapelist_str == '\0') break; /* bad format, should kvetch */ tapelist_str++; l_idx++; } if(*tapelist_str != '\0') tapelist_str++; } /* then, read the label part */ memset(temp_label, '\0', input_length+1); l_idx = 0; while(*tapelist_str != ':' && *tapelist_str != '\0'){ if(*tapelist_str == '\\') tapelist_str++; /* skip escapes */ temp_label[l_idx] = *tapelist_str; if(*tapelist_str == '\0') break; /* bad format, should kvetch */ tapelist_str++; l_idx++; } if(*tapelist_str != '\0') tapelist_str++; tapelist = append_to_tapelist(tapelist, temp_storage, temp_label, (off_t)-1, -1, 0); /* now read the list of file numbers */ while(*tapelist_str != ';' && *tapelist_str != '\0'){ off_t filenum; memset(temp_filenum, '\0', input_length+1); n_idx = 0; while(*tapelist_str != ';' && *tapelist_str != ',' && *tapelist_str != '\0'){ temp_filenum[n_idx] = *tapelist_str; tapelist_str++; n_idx++; } filenum = OFF_T_ATOI(temp_filenum); tapelist = append_to_tapelist(tapelist, temp_storage, temp_label, filenum, -1, 0); if(*tapelist_str != '\0' && *tapelist_str != ';') tapelist_str++; } if(*tapelist_str != '\0') tapelist_str++; } while(*tapelist_str != '\0'); amfree(temp_label); amfree(temp_storage); amfree(temp_filenum); return(tapelist); }
/* * Write out the buffer to the backing file */ static int databuf_flush( struct databuf * db) { struct cmdargs *cmdargs = NULL; int rc = 1; size_t size_to_write; size_t written; off_t left_in_chunk; char *arg_filename = NULL; char *new_filename = NULL; char *tmp_filename = NULL; char sequence[NUM_STR_SIZE]; int newfd; filetype_t save_type; char *q; int a; char *pc; /* * If there's no data, do nothing. */ if (db->dataout >= db->datain) { goto common_exit; } /* * See if we need to split this file. */ while (db->split_size > (off_t)0 && dumpsize >= db->split_size) { if( db->use == (off_t)0 ) { /* * Probably no more space on this disk. Request some more. */ putresult(RQ_MORE_DISK, "%s\n", handle); cmdargs = getcmd(); if(command_in_transit == NULL && (cmdargs->cmd == DONE || cmdargs->cmd == TRYAGAIN || cmdargs->cmd == FAILED)) { command_in_transit = cmdargs; cmdargs = getcmd(); } if(cmdargs->cmd == CONTINUE) { /* * CONTINUE * serial * filename * chunksize * use */ a = 2; /* skip CONTINUE and serial */ if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: filename]")); /*NOTREACHED*/ } arg_filename = newstralloc(arg_filename, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: chunksize]")); /*NOTREACHED*/ } db->chunk_size = OFF_T_ATOI(cmdargs->argv[a++]); db->chunk_size = am_floor(db->chunk_size, (off_t)DISK_BLOCK_KB); if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: use]")); /*NOTREACHED*/ } db->use = OFF_T_ATOI(cmdargs->argv[a++]); if(a != cmdargs->argc) { error(_("error [chunker CONTINUE: too many args: %d != %d]"), cmdargs->argc, a); /*NOTREACHED*/ } if(strcmp(db->filename, arg_filename) == 0) { /* * Same disk, so use what room is left up to the * next chunk boundary or the amount we were given, * whichever is less. */ left_in_chunk = db->chunk_size - filesize; if(left_in_chunk > db->use) { db->split_size += db->use; db->use = (off_t)0; } else { db->split_size += left_in_chunk; db->use -= left_in_chunk; } if(left_in_chunk > (off_t)0) { /* * We still have space in this chunk. */ break; } } else { /* * Different disk, so use new file. */ db->filename = newstralloc(db->filename, arg_filename); } } else if(cmdargs->cmd == ABORT) { abort_pending = 1; errstr = newstralloc(errstr, cmdargs->argv[1]); putresult(ABORT_FINISHED, "%s\n", handle); rc = 0; goto common_exit; } else { if(cmdargs->argc >= 1) { q = quote_string(cmdargs->argv[0]); } else { q = stralloc(_("(no input?)")); } error(_("error [bad command after RQ-MORE-DISK: \"%s\"]"), q); /*NOTREACHED*/ } } /* * Time to use another file. */ /* * First, open the new chunk file, and give it a new header * that has no cont_filename pointer. */ g_snprintf(sequence, SIZEOF(sequence), "%d", db->filename_seq); new_filename = newvstralloc(new_filename, db->filename, ".", sequence, NULL); tmp_filename = newvstralloc(tmp_filename, new_filename, ".tmp", NULL); pc = strrchr(tmp_filename, '/'); g_assert(pc != NULL); /* Only a problem if db->filename has no /. */ *pc = '\0'; mkholdingdir(tmp_filename); *pc = '/'; newfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (newfd == -1) { int save_errno = errno; char *m; if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE_DISK */ db->split_size = dumpsize; continue; } m = vstrallocf(_("creating chunk holding file \"%s\": %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(db->fd); rc = 0; goto common_exit; } save_type = file.type; file.type = F_CONT_DUMPFILE; file.cont_filename[0] = '\0'; if(write_tapeheader(newfd, &file)) { int save_errno = errno; char *m; aclose(newfd); if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE DISK */ db->split_size = dumpsize; continue; } m = vstrallocf(_("write_tapeheader file %s: %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); rc = 0; goto common_exit; } /* * Now, update the header of the current file to point * to the next chunk, and then close it. */ if (lseek(db->fd, (off_t)0, SEEK_SET) < (off_t)0) { char *m = vstrallocf(_("lseek holding file %s: %s"), db->filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(newfd); rc = 0; goto common_exit; } file.type = save_type; strncpy(file.cont_filename, new_filename, SIZEOF(file.cont_filename)); file.cont_filename[SIZEOF(file.cont_filename)-1] = '\0'; if(write_tapeheader(db->fd, &file)) { char * m = vstrallocf(_("write_tapeheader file \"%s\": %s"), db->filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(newfd); unlink(tmp_filename); rc = 0; goto common_exit; } file.type = F_CONT_DUMPFILE; /* * Now shift the file descriptor. */ aclose(db->fd); db->fd = newfd; newfd = -1; /* * Update when we need to chunk again */ if(db->use <= (off_t)DISK_BLOCK_KB) { /* * Cheat and use one more block than allowed so we can make * some progress. */ db->split_size += (off_t)(2 * DISK_BLOCK_KB); db->use = (off_t)0; } else if(db->chunk_size > db->use) { db->split_size += db->use; db->use = (off_t)0; } else { db->split_size += db->chunk_size; db->use -= db->chunk_size; } amfree(tmp_filename); amfree(new_filename); dumpsize += (off_t)DISK_BLOCK_KB; filesize = (off_t)DISK_BLOCK_KB; headersize += DISK_BLOCK_KB; db->filename_seq++; } /* * Write out the buffer */ size_to_write = (size_t)(db->datain - db->dataout); written = full_write(db->fd, db->dataout, size_to_write); if (written > 0) { db->dataout += written; dumpbytes += (off_t)written; } dumpsize += (dumpbytes / (off_t)1024); filesize += (dumpbytes / (off_t)1024); dumpbytes %= 1024; if (written < size_to_write) { if (errno != ENOSPC) { char *m = vstrallocf(_("data write: %s"), strerror(errno)); errstr = quote_string(m); amfree(m); rc = 0; goto common_exit; } /* * NO-ROOM is informational only. Later, RQ_MORE_DISK will be * issued to use another holding disk. */ putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE_DISK */ db->split_size = dumpsize; goto common_exit; } if (db->datain == db->dataout) { /* * We flushed the whole buffer so reset to use it all. */ db->datain = db->dataout = db->buf; } common_exit: if (cmdargs) free_cmdargs(cmdargs); amfree(new_filename); /*@i@*/ amfree(tmp_filename); amfree(arg_filename); return rc; }
void parse_file_header( const char *buffer, dumpfile_t *file, size_t buflen) { char *buf, *line, *tok, *line1; size_t lsize; char *uqname; int in_quotes; char *saveptr = NULL; /* put the buffer into a writable chunk of memory and nul-term it */ buf = g_malloc(buflen + 1); memcpy(buf, buffer, buflen); buf[buflen] = '\0'; fh_init(file); /* extract the first unquoted line */ in_quotes = 0; for (line = buf, lsize = 0; lsize < buflen; line++) { if ((*line == '\n') && !in_quotes) break; if (*line == '"') { in_quotes = !in_quotes; } else if ((*line == '\\') && (*(line + 1) == '"')) { line++; lsize++; } lsize++; } *line = '\0'; line1 = g_malloc(lsize + 1); strncpy(line1, buf, lsize); line1[lsize] = '\0'; *line = '\n'; tok = strtok_r(line1, " ", &saveptr); if (tok == NULL) { g_debug("Empty amanda header: buflen=%zu lsize=%zu buf='%s'", buflen, lsize, buf); strange_header(file, buffer, buflen, _("<Non-empty line>"), tok); goto out; } if (!g_str_equal(tok, "NETDUMP:") && !g_str_equal(tok, "AMANDA:")) { amfree(buf); file->type = F_WEIRD; amfree(line1); return; } tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<file type>"), tok); goto out; } file->type = str2filetype(tok); switch (file->type) { case F_TAPESTART: tok = strtok_r(NULL, " ", &saveptr); if ((tok == NULL) || (!g_str_equal(tok, "DATE"))) { strange_header(file, buffer, buflen, "DATE", tok); goto out; } tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<date stamp>"), tok); goto out; } strncpy(file->datestamp, tok, sizeof(file->datestamp) - 1); file->datestamp[sizeof(file->datestamp) - 1] = '\0'; tok = strtok_r(NULL, " ", &saveptr); if ((tok == NULL) || (!g_str_equal(tok, "TAPE"))) { strange_header(file, buffer, buflen, "TAPE", tok); goto out; } tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<file type>"), tok); goto out; } strncpy(file->name, tok, sizeof(file->name) - 1); file->name[sizeof(file->name) - 1] = '\0'; break; case F_DUMPFILE: case F_CONT_DUMPFILE: case F_SPLIT_DUMPFILE: tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<date stamp>"), tok); goto out; } strncpy(file->datestamp, tok, sizeof(file->datestamp) - 1); file->datestamp[sizeof(file->datestamp) - 1] = '\0'; tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<file name>"), tok); goto out; } strncpy(file->name, tok, sizeof(file->name) - 1); file->name[sizeof(file->name) - 1] = '\0'; tok = strquotedstr(&saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<disk name>"), tok); goto out; } uqname = unquote_string(tok); strncpy(file->disk, uqname, sizeof(file->disk) - 1); file->disk[sizeof(file->disk) - 1] = '\0'; amfree(uqname); if(file->type == F_SPLIT_DUMPFILE) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL || !g_str_equal(tok, "part")) { strange_header(file, buffer, buflen, "part", tok); goto out; } tok = strtok_r(NULL, "/", &saveptr); if ((tok == NULL) || (sscanf(tok, "%d", &file->partnum) != 1)) { strange_header(file, buffer, buflen, _("<part num param>"), tok); goto out; } /* If totalparts == -1, then the original dump was done in streaming mode (no holding disk), thus we don't know how many parts there are. */ tok = strtok_r(NULL, " ", &saveptr); if((tok == NULL) || (sscanf(tok, "%d", &file->totalparts) != 1)) { strange_header(file, buffer, buflen, _("<total parts param>"), tok); goto out; } } else if (file->type == F_DUMPFILE) { /* only one part in this dump, so call it partnum 1 */ file->partnum = 1; file->totalparts = 1; } else { file->partnum = 0; file->totalparts = 0; } tok = strtok_r(NULL, " ", &saveptr); if ((tok == NULL) || (!g_str_equal(tok, "lev"))) { strange_header(file, buffer, buflen, "lev", tok); goto out; } tok = strtok_r(NULL, " ", &saveptr); if ((tok == NULL) || (sscanf(tok, "%d", &file->dumplevel) != 1)) { strange_header(file, buffer, buflen, _("<dump level param>"), tok); goto out; } tok = strtok_r(NULL, " ", &saveptr); if ((tok == NULL) || (!g_str_equal(tok, "comp"))) { strange_header(file, buffer, buflen, "comp", tok); goto out; } tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<comp param>"), tok); goto out; } strncpy(file->comp_suffix, tok, sizeof(file->comp_suffix) - 1); file->compressed = (!g_str_equal(file->comp_suffix, "N")); if (file->compressed) { /* compatibility with pre-2.2 amanda */ if (g_str_equal(file->comp_suffix, "C")) strncpy(file->comp_suffix, ".Z", sizeof(file->comp_suffix) - 1); } else { strcpy(file->comp_suffix, ""); } file->comp_suffix[sizeof(file->comp_suffix) - 1] = '\0'; tok = strtok_r(NULL, " ", &saveptr); /* "program" is optional */ if (tok == NULL || !g_str_equal(tok, "program")) { break; } tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<program name>"), tok); goto out; } strncpy(file->program, tok, sizeof(file->program) - 1); file->program[sizeof(file->program) - 1] = '\0'; if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; /* reached the end of the buffer */ /* encryption is optional */ if (BSTRNCMP(tok, "crypt") == 0) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<crypt param>"), tok); goto out; } strncpy(file->encrypt_suffix, tok, sizeof(file->encrypt_suffix) - 1); file->encrypt_suffix[sizeof(file->encrypt_suffix) - 1] = '\0'; file->encrypted = 1; /* for compatibility with who-knows-what, allow "comp N" to be * equivalent to no compression */ if (0 == BSTRNCMP(file->encrypt_suffix, "N")) { file->encrypted = 0; strcpy(file->encrypt_suffix, ""); } if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; } /* "srvcompprog" is optional */ if (BSTRNCMP(tok, "server_custom_compress") == 0) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<server custom compress param>"), tok); goto out; } strncpy(file->srvcompprog, tok, sizeof(file->srvcompprog) - 1); file->srvcompprog[sizeof(file->srvcompprog) - 1] = '\0'; if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; } /* "clntcompprog" is optional */ if (BSTRNCMP(tok, "client_custom_compress") == 0) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<client custom compress param>"), tok); goto out; } strncpy(file->clntcompprog, tok, sizeof(file->clntcompprog) - 1); file->clntcompprog[sizeof(file->clntcompprog) - 1] = '\0'; if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; } /* "srv_encrypt" is optional */ if (BSTRNCMP(tok, "server_encrypt") == 0) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<server encrypt param>"), tok); goto out; } strncpy(file->srv_encrypt, tok, sizeof(file->srv_encrypt) - 1); file->srv_encrypt[sizeof(file->srv_encrypt) - 1] = '\0'; if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; } /* "clnt_encrypt" is optional */ if (BSTRNCMP(tok, "client_encrypt") == 0) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<client encrypt param>"), tok); goto out; } strncpy(file->clnt_encrypt, tok, sizeof(file->clnt_encrypt) - 1); file->clnt_encrypt[sizeof(file->clnt_encrypt) - 1] = '\0'; if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; } /* "srv_decrypt_opt" is optional */ if (BSTRNCMP(tok, "server_decrypt_option") == 0) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<server decrypt param>"), tok); goto out; } strncpy(file->srv_decrypt_opt, tok, sizeof(file->srv_decrypt_opt) - 1); file->srv_decrypt_opt[sizeof(file->srv_decrypt_opt) - 1] = '\0'; if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; } /* "clnt_decrypt_opt" is optional */ if (BSTRNCMP(tok, "client_decrypt_option") == 0) { tok = strtok_r(NULL, " ", &saveptr); if (tok == NULL) { strange_header(file, buffer, buflen, _("<client decrypt param>"), tok); goto out; } strncpy(file->clnt_decrypt_opt, tok, sizeof(file->clnt_decrypt_opt) - 1); file->clnt_decrypt_opt[sizeof(file->clnt_decrypt_opt) - 1] = '\0'; if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) break; } break; case F_TAPEEND: tok = strtok_r(NULL, " ", &saveptr); /* DATE is optional */ if (tok != NULL) { if (g_str_equal(tok, "DATE")) { tok = strtok_r(NULL, " ", &saveptr); if(tok == NULL) file->datestamp[0] = '\0'; else { strncpy(file->datestamp, tok, sizeof(file->datestamp) - 1); file->datestamp[sizeof(file->datestamp) - 1] = '\0'; } } else { strange_header(file, buffer, buflen, _("<DATE>"), tok); } } else { file->datestamp[0] = '\0'; } break; case F_NOOP: /* nothing follows */ break; default: strange_header(file, buffer, buflen, _("TAPESTART|DUMPFILE|CONT_DUMPFILE|SPLIT_DUMPFILE|TAPEEND|NOOP"), tok); goto out; } (void)strtok_r(buf, "\n", &saveptr); /* this is the first line */ /* iterate through the rest of the lines */ while ((line = strtok_r(NULL, "\n", &saveptr)) != NULL) { #define SC "CONT_FILENAME=" if (g_str_has_prefix(line, SC)) { line += sizeof(SC) - 1; strncpy(file->cont_filename, line, sizeof(file->cont_filename) - 1); file->cont_filename[sizeof(file->cont_filename) - 1] = '\0'; continue; } #undef SC #define SC "PARTIAL=" if (g_str_has_prefix(line, SC)) { line += sizeof(SC) - 1; file->is_partial = !strcasecmp(line, "yes"); continue; } #undef SC #define SC "APPLICATION=" if (g_str_has_prefix(line, SC)) { line += sizeof(SC) - 1; strncpy(file->application, line, sizeof(file->application) - 1); file->application[sizeof(file->application) - 1] = '\0'; continue; } #undef SC #define SC "ORIGSIZE=" if (g_str_has_prefix(line, SC)) { line += sizeof(SC) - 1; file->orig_size = OFF_T_ATOI(line); } #undef SC #define SC "DLE=" if (g_str_has_prefix(line, SC)) { line += sizeof(SC) - 1; amfree(file->dle_str); file->dle_str = parse_heredoc(line, &saveptr); } #undef SC #define SC _("To restore, position tape at start of file and run:") if (g_str_has_prefix(line, SC)) continue; #undef SC #define SC "\tdd if=<tape> " if (g_str_has_prefix(line, SC)) { char *cmd1, *cmd2, *cmd3=NULL; /* skip over dd command */ if ((cmd1 = strchr(line, '|')) == NULL) { strncpy(file->recover_cmd, "BUG\0", sizeof(file->recover_cmd) - 1); continue; } *cmd1++ = '\0'; /* block out first pipeline command */ if ((cmd2 = strchr(cmd1, '|')) != NULL) { *cmd2++ = '\0'; if ((cmd3 = strchr(cmd2, '|')) != NULL) *cmd3++ = '\0'; } /* clean up some extra spaces in various fields */ chomp(cmd1); chomp(cmd2); chomp(cmd3); /* three cmds: decrypt | uncompress | recover * two cmds: uncompress | recover * XXX note that if there are two cmds, the first one * XXX could be either uncompress or decrypt. Since no * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok * XXX for header information. * one cmds: recover */ if ( cmd3 == NULL) { if (cmd2 == NULL) { strncpy(file->recover_cmd, cmd1, sizeof(file->recover_cmd) - 1); } else { g_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd), "%s |", cmd1); strncpy(file->recover_cmd, cmd2, sizeof(file->recover_cmd) - 1); } } else { /* cmd3 presents: decrypt | uncompress | recover */ g_snprintf(file->decrypt_cmd, sizeof(file->decrypt_cmd), "%s |", cmd1); g_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd), "%s |", cmd2); strncpy(file->recover_cmd, cmd3, sizeof(file->recover_cmd) - 1); } file->recover_cmd[sizeof(file->recover_cmd) - 1] = '\0'; file->uncompress_cmd[sizeof(file->uncompress_cmd) - 1] = '\0'; continue; } #undef SC /* XXX complain about weird lines? */ } out: amfree(buf); amfree(line1); }
int main( int argc, char ** argv) { static struct databuf db; struct cmdargs *cmdargs; int infd; char *q = NULL; char *filename = NULL; off_t chunksize, use; times_t runtime; am_feature_t *their_features = NULL; int a; config_overrides_t *cfg_ovr = NULL; char *cfg_opt = NULL; char *m; /* * Configure program for internationalization: * 1) Only set the message locale for now. * 2) Set textdomain for all amanda related programs to "amanda" * We don't want to be forced to support dozens of message catalogs. */ setlocale(LC_MESSAGES, "C"); textdomain("amanda"); safe_fd(-1, 0); set_pname("chunker"); dbopen(DBG_SUBDIR_SERVER); /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); add_amanda_log_handler(amanda_log_stderr); add_amanda_log_handler(amanda_log_trace_log); cfg_ovr = extract_commandline_config_overrides(&argc, &argv); if (argc > 1) cfg_opt = argv[1]; config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt); apply_config_overrides(cfg_ovr); if (config_errors(NULL) >= CFGERR_WARNINGS) { config_print_errors(); if (config_errors(NULL) >= CFGERR_ERRORS) { g_critical(_("errors processing config file")); } } safe_cd(); /* do this *after* config_init() */ check_running_as(RUNNING_AS_DUMPUSER); dbrename(get_config_name(), DBG_SUBDIR_SERVER); log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"), get_pname(), (long) getpid(), argv[0], VERSION); fflush(stderr); /* now, make sure we are a valid user */ signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); cmdargs = getcmd(); if(cmdargs->cmd == START) { if(cmdargs->argc <= 1) error(_("error [dumper START: not enough args: timestamp]")); chunker_timestamp = newstralloc(chunker_timestamp, cmdargs->argv[1]); } else { log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); error(_("Didn't get START command")); } /* do {*/ cmdargs = getcmd(); switch(cmdargs->cmd) { case QUIT: break; case PORT_WRITE: /* * PORT-WRITE * handle * filename * host * features * disk * level * dumpdate * chunksize * progname * use * options */ a = 1; if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: handle]")); /*NOTREACHED*/ } handle = newstralloc(handle, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: filename]")); /*NOTREACHED*/ } filename = newstralloc(filename, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: hostname]")); /*NOTREACHED*/ } hostname = newstralloc(hostname, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: features]")); /*NOTREACHED*/ } am_release_feature_set(their_features); their_features = am_string_to_feature(cmdargs->argv[a++]); if (!their_features) { error(_("error [chunker PORT-WRITE: invalid feature string]")); /*NOTREACHED*/ } if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: diskname]")); /*NOTREACHED*/ } diskname = newstralloc(diskname, cmdargs->argv[a++]); if (qdiskname) amfree(qdiskname); qdiskname = quote_string(diskname); /* qdiskname is a global */ if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: level]")); /*NOTREACHED*/ } level = atoi(cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: dumpdate]")); /*NOTREACHED*/ } dumpdate = newstralloc(dumpdate, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: chunksize]")); /*NOTREACHED*/ } chunksize = OFF_T_ATOI(cmdargs->argv[a++]); chunksize = am_floor(chunksize, (off_t)DISK_BLOCK_KB); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: progname]")); /*NOTREACHED*/ } progname = newstralloc(progname, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: use]")); /*NOTREACHED*/ } use = am_floor(OFF_T_ATOI(cmdargs->argv[a++]), DISK_BLOCK_KB); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: options]")); /*NOTREACHED*/ } options = newstralloc(options, cmdargs->argv[a++]); if(a != cmdargs->argc) { error(_("error [chunker PORT-WRITE: too many args: %d != %d]"), cmdargs->argc, a); /*NOTREACHED*/ } if((infd = startup_chunker(filename, use, chunksize, &db)) < 0) { q = quote_string(vstrallocf(_("[chunker startup failed: %s]"), errstr)); putresult(TRYAGAIN, "%s %s\n", handle, q); error("startup_chunker failed: %s", errstr); } command_in_transit = NULL; if(infd >= 0 && do_chunk(infd, &db)) { char kb_str[NUM_STR_SIZE]; char kps_str[NUM_STR_SIZE]; double rt; runtime = stopclock(); rt = g_timeval_to_double(runtime); g_snprintf(kb_str, SIZEOF(kb_str), "%lld", (long long)(dumpsize - (off_t)headersize)); g_snprintf(kps_str, SIZEOF(kps_str), "%3.1lf", isnormal(rt) ? (double)dumpsize / rt : 0.0); errstr = newvstrallocf(errstr, "sec %s kb %s kps %s", walltime_str(runtime), kb_str, kps_str); m = vstrallocf("[%s]", errstr); q = quote_string(m); amfree(m); if(command_in_transit != NULL) { cmdargs = command_in_transit; command_in_transit = NULL; } else { cmdargs = getcmd(); } switch(cmdargs->cmd) { case DONE: putresult(DONE, "%s %lld %s\n", handle, (long long)(dumpsize - (off_t)headersize), q); log_add(L_SUCCESS, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); break; case BOGUS: case TRYAGAIN: case FAILED: case ABORT_FINISHED: if(dumpsize > (off_t)DISK_BLOCK_KB) { putresult(PARTIAL, "%s %lld %s\n", handle, (long long)(dumpsize - (off_t)headersize), q); log_add(L_PARTIAL, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); } else { errstr = newvstrallocf(errstr, _("dumper returned %s"), cmdstr[cmdargs->cmd]); amfree(q); m = vstrallocf("[%s]",errstr); q = quote_string(m); amfree(m); putresult(FAILED, "%s %s\n", handle, q); log_add(L_FAIL, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); } default: break; } amfree(q); } else if(infd != -2) { if(q == NULL) { m = vstrallocf("[%s]", errstr); q = quote_string(m); amfree(m); } if(!abort_pending) { putresult(FAILED, "%s %s\n", handle, q); } log_add(L_FAIL, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); amfree(q); } amfree(filename); amfree(db.filename); break; default: if(cmdargs->argc >= 1) { q = quote_string(cmdargs->argv[0]); } else { q = stralloc(_("(no input?)")); } putresult(BAD_COMMAND, "%s\n", q); amfree(q); break; } /* } while(cmdargs->cmd != QUIT); */ log_add(L_INFO, "pid-done %ld", (long)getpid()); amfree(errstr); amfree(chunker_timestamp); amfree(handle); amfree(hostname); amfree(diskname); amfree(qdiskname); amfree(dumpdate); amfree(progname); amfree(options); free_cmdargs(cmdargs); if (command_in_transit) free_cmdargs(command_in_transit); am_release_feature_set(their_features); their_features = NULL; dbclose(); return (0); /* exit */ }