X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Fdumper.c;h=13c8b65fdc8d6a7799aafc76645fe7536a6bab80;hb=011a59f5a54864108a16af570a6b287410597cc2;hp=18384c738394b4f117b9b4381fb5efe1b915b918;hpb=a6127998ee6dcab6bb034f6ca985b07804a86f9a;p=debian%2Famanda diff --git a/server-src/dumper.c b/server-src/dumper.c index 18384c7..13c8b65 100644 --- a/server-src/dumper.c +++ b/server-src/dumper.c @@ -38,13 +38,12 @@ #include "protocol.h" #include "security.h" #include "stream.h" -#include "token.h" -#include "version.h" #include "fileheader.h" #include "amfeatures.h" #include "server_util.h" #include "util.h" #include "timestamp.h" +#include "amxml.h" #define dumper_debug(i,x) do { \ if ((i) <= debug_dumper) { \ @@ -74,6 +73,16 @@ struct databuf { pid_t encryptpid; /* valid if fd is pipe to encrypt */ }; +typedef struct filter_s { + int fd; + char *name; + char *buffer; + gint64 first; /* first byte used */ + gint64 size; /* number of byte use in the buffer */ + gint64 allocated_size ; /* allocated size of the buffer */ + event_handle_t *event; +} filter_t; + static char *handle = NULL; static char *errstr = NULL; @@ -95,19 +104,26 @@ static FILE *errf = NULL; static char *hostname = NULL; am_feature_t *their_features = NULL; static char *diskname = NULL; -static char *qdiskname = NULL; -static char *device = NULL; +static char *qdiskname = NULL, *b64disk; +static char *device = NULL, *b64device; static char *options = NULL; static char *progname = NULL; static char *amandad_path=NULL; static char *client_username=NULL; +static char *client_port=NULL; static char *ssh_keys=NULL; +static char *auth=NULL; +static data_path_t data_path=DATA_PATH_AMANDA; +static char *dataport_list = NULL; static int level; static char *dumpdate = NULL; static char *dumper_timestamp = NULL; static time_t conf_dtimeout; static int indexfderror; static int set_datafd; +static char *dle_str = NULL; +static char *errfname = NULL; +static int errf_lines = 0; static dumpfile_t file; @@ -127,10 +143,18 @@ static struct { static am_feature_t *our_features = NULL; static char *our_feature_string = NULL; +/* buffer to keep partial line from the MESG stream */ +static struct { + char *buf; /* buffer holding msg data */ + size_t size; /* size of alloced buffer */ +} msg = { NULL, 0 }; + + /* local functions */ int main(int, char **); static int do_dump(struct databuf *); static void check_options(char *); +static void xml_check_options(char *optionstr); static void finish_tapeheader(dumpfile_t *); static ssize_t write_tapeheader(int, dumpfile_t *); static void databuf_init(struct databuf *, int); @@ -140,16 +164,17 @@ static void process_dumpeof(void); static void process_dumpline(const char *); static void add_msg_data(const char *, size_t); static void parse_info_line(char *); -static void log_msgout(logtype_t); +static int log_msgout(logtype_t); static char * dumper_get_security_conf (char *, void *); -static int runcompress(int, pid_t *, comp_t); +static int runcompress(int, pid_t *, comp_t, char *); static int runencrypt(int, pid_t *, encrypt_t); static void sendbackup_response(void *, pkt_t *, security_handle_t *); static int startup_dump(const char *, const char *, const char *, int, const char *, const char *, const char *, - const char *, const char *, const char *); + const char *, const char *, const char *, + const char *, const char *); static void stop_dump(void); static void read_indexfd(void *, void *, ssize_t); @@ -170,7 +195,7 @@ check_options( char *decryptend = NULL; /* parse the compression option */ - if (strstr(options, "srvcomp-best;") != NULL) + if (strstr(options, "srvcomp-best;") != NULL) srvcompress = COMP_BEST; else if (strstr(options, "srvcomp-fast;") != NULL) srvcompress = COMP_FAST; @@ -241,22 +266,67 @@ check_options( } +static void +xml_check_options( + char *optionstr) +{ + char *o, *oo; + char *errmsg = NULL; + dle_t *dle; + + o = oo = vstralloc("", strchr(optionstr,'<'), "", NULL); + + dle = amxml_parse_node_CHAR(o, &errmsg); + if (dle == NULL) { + error("amxml_parse_node_CHAR failed: %s\n", errmsg); + } + + if (dle->compress == COMP_SERVER_FAST) { + srvcompress = COMP_FAST; + } else if (dle->compress == COMP_SERVER_BEST) { + srvcompress = COMP_BEST; + } else if (dle->compress == COMP_SERVER_CUST) { + srvcompress = COMP_SERVER_CUST; + srvcompprog = g_strdup(dle->compprog); + } else if (dle->compress == COMP_CUST) { + srvcompress = COMP_CUST; + clntcompprog = g_strdup(dle->compprog); + } else { + srvcompress = COMP_NONE; + } + + if (dle->encrypt == ENCRYPT_CUST) { + srvencrypt = ENCRYPT_CUST; + clnt_encrypt = g_strdup(dle->clnt_encrypt); + clnt_decrypt_opt = g_strdup(dle->clnt_decrypt_opt); + } else if (dle->encrypt == ENCRYPT_SERV_CUST) { + srvencrypt = ENCRYPT_SERV_CUST; + srv_encrypt = g_strdup(dle->srv_encrypt); + srv_decrypt_opt = g_strdup(dle->srv_decrypt_opt); + } else { + srvencrypt = ENCRYPT_NONE; + } + free_dle(dle); + amfree(o); +} + + int main( int argc, char ** argv) { static struct databuf db; - struct cmdargs cmdargs; - cmd_t cmd; + struct cmdargs *cmdargs = NULL; int outfd = -1; int rc; - in_port_t taper_port; + in_port_t header_port; char *q = NULL; int a; int res; - config_overwrites_t *cfg_ovr = NULL; + config_overrides_t *cfg_ovr = NULL; char *cfg_opt = NULL; + int dumper_setuid; /* * Configure program for internationalization: @@ -268,9 +338,7 @@ main( textdomain("amanda"); /* drop root privileges */ - if (!set_root_privs(0)) { - error(_("dumper must be run setuid root")); - } + dumper_setuid = set_root_privs(0); safe_fd(-1, 0); @@ -281,29 +349,37 @@ main( /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); - erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE); - set_logerror(logerror); + add_amanda_log_handler(amanda_log_stderr); + add_amanda_log_handler(amanda_log_trace_log); - cfg_ovr = extract_commandline_config_overwrites(&argc, &argv); + 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 | CONFIG_INIT_FATAL, - cfg_opt); - apply_config_overwrites(cfg_ovr); + set_config_overrides(cfg_ovr); + config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt); + + if (!dumper_setuid) { + error(_("dumper must be run setuid root")); + } + + 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); + check_running_as(RUNNING_AS_ROOT | RUNNING_AS_UID_ONLY); - dbrename(config_name, DBG_SUBDIR_SERVER); + dbrename(get_config_name(), DBG_SUBDIR_SERVER); our_features = am_init_feature_set(); our_feature_string = am_feature_to_string(our_features); + 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()); + argv[0], VERSION); fflush(stderr); /* now, make sure we are a valid user */ @@ -315,13 +391,16 @@ main( protocol_init(); do { - cmd = getcmd(&cmdargs); + if (cmdargs) + free_cmdargs(cmdargs); + cmdargs = getcmd(); - switch(cmd) { + amfree(errstr); + switch(cmdargs->cmd) { case START: - if(cmdargs.argc < 2) + if(cmdargs->argc < 2) error(_("error [dumper START: not enough args: timestamp]")); - dumper_timestamp = newstralloc(dumper_timestamp, cmdargs.argv[2]); + dumper_timestamp = newstralloc(dumper_timestamp, cmdargs->argv[1]); break; case ABORT: @@ -344,96 +423,121 @@ main( * progname * amandad_path * client_username + * client_port * ssh_keys + * security_driver + * data_path + * dataport_list * options */ - cmdargs.argc++; /* true count of args */ - a = 2; + a = 1; /* skip "PORT-DUMP" */ - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: handle]")); /*NOTREACHED*/ } - handle = newstralloc(handle, cmdargs.argv[a++]); + handle = newstralloc(handle, cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: port]")); /*NOTREACHED*/ } - taper_port = (in_port_t)atoi(cmdargs.argv[a++]); + header_port = (in_port_t)atoi(cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: hostname]")); /*NOTREACHED*/ } - hostname = newstralloc(hostname, cmdargs.argv[a++]); + hostname = newstralloc(hostname, cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: features]")); /*NOTREACHED*/ } am_release_feature_set(their_features); - their_features = am_string_to_feature(cmdargs.argv[a++]); + their_features = am_string_to_feature(cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: diskname]")); /*NOTREACHED*/ } - qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]); - if (diskname != NULL) - amfree(diskname); - diskname = unquote_string(qdiskname); + diskname = newstralloc(diskname, cmdargs->argv[a++]); + if (qdiskname != NULL) + amfree(qdiskname); + qdiskname = quote_string(diskname); + b64disk = amxml_format_tag("disk", diskname); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: device]")); /*NOTREACHED*/ } - device = newstralloc(device, cmdargs.argv[a++]); + device = newstralloc(device, cmdargs->argv[a++]); + b64device = amxml_format_tag("diskdevice", device); if(strcmp(device,"NODEVICE") == 0) amfree(device); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: level]")); /*NOTREACHED*/ } - level = atoi(cmdargs.argv[a++]); + level = atoi(cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: dumpdate]")); /*NOTREACHED*/ } - dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]); + dumpdate = newstralloc(dumpdate, cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: program]")); /*NOTREACHED*/ } - progname = newstralloc(progname, cmdargs.argv[a++]); + progname = newstralloc(progname, cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: amandad_path]")); /*NOTREACHED*/ } - amandad_path = newstralloc(amandad_path, cmdargs.argv[a++]); + amandad_path = newstralloc(amandad_path, cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: client_username]")); } - client_username = newstralloc(client_username, cmdargs.argv[a++]); + client_username = newstralloc(client_username, cmdargs->argv[a++]); + + if(a >= cmdargs->argc) { + error(_("error [dumper PORT-DUMP: not enough args: client_port]")); + } + client_port = newstralloc(client_port, cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: ssh_keys]")); } - ssh_keys = newstralloc(ssh_keys, cmdargs.argv[a++]); + ssh_keys = newstralloc(ssh_keys, cmdargs->argv[a++]); + + if(a >= cmdargs->argc) { + error(_("error [dumper PORT-DUMP: not enough args: auth]")); + } + auth = newstralloc(auth, cmdargs->argv[a++]); + + if(a >= cmdargs->argc) { + error(_("error [dumper PORT-DUMP: not enough args: data_path]")); + } + data_path = data_path_from_string(cmdargs->argv[a++]); - if(a >= cmdargs.argc) { + if(a >= cmdargs->argc) { + error(_("error [dumper PORT-DUMP: not enough args: dataport_list]")); + } + dataport_list = newstralloc(dataport_list, cmdargs->argv[a++]); + + if(a >= cmdargs->argc) { error(_("error [dumper PORT-DUMP: not enough args: options]")); } - options = newstralloc(options, cmdargs.argv[a++]); + options = newstralloc(options, cmdargs->argv[a++]); - if(a != cmdargs.argc) { + if(a != cmdargs->argc) { error(_("error [dumper PORT-DUMP: too many args: %d != %d]"), - cmdargs.argc, a); + cmdargs->argc, a); /*NOTREACHED*/ } @@ -442,7 +546,7 @@ main( errstr = newvstrallocf(errstr, _("could not resolve localhost: %s"), gai_strerror(res)); - q = squotef(errstr); + q = quote_string(errstr); putresult(FAILED, "%s %s\n", handle, q); log_add(L_FAIL, "%s %s %s %d [%s]", hostname, qdiskname, dumper_timestamp, level, errstr); @@ -452,13 +556,14 @@ main( /* connect outf to chunker/taper port */ - outfd = stream_client("localhost", taper_port, + g_debug(_("Sending header to localhost:%d\n"), header_port); + outfd = stream_client("localhost", header_port, STREAM_BUFSIZE, 0, NULL, 0); if (outfd == -1) { errstr = newvstrallocf(errstr, _("port open: %s"), strerror(errno)); - q = squotef(errstr); + q = quote_string(errstr); putresult(FAILED, "%s %s\n", handle, q); log_add(L_FAIL, "%s %s %s %d [%s]", hostname, qdiskname, dumper_timestamp, level, errstr); @@ -467,7 +572,10 @@ main( } databuf_init(&db, outfd); - check_options(options); + if (am_has_feature(their_features, fe_req_xml)) + xml_check_options(options); /* note: modifies globals */ + else + check_options(options); /* note: modifies globals */ rc = startup_dump(hostname, diskname, @@ -477,10 +585,12 @@ main( progname, amandad_path, client_username, + client_port, ssh_keys, + auth, options); if (rc != 0) { - q = squote(errstr); + q = quote_string(errstr); putresult(rc == 2? FAILED : TRYAGAIN, "%s %s\n", handle, q); if (rc == 2) @@ -496,14 +606,17 @@ main( amfree(amandad_path); amfree(client_username); + amfree(client_port); + amfree(device); + amfree(b64device); + amfree(qdiskname); + amfree(b64disk); break; default: - if(cmdargs.argc >= 1) { - q = squote(cmdargs.argv[1]); - } else if(cmdargs.argc >= 0) { - q = squote(cmdargs.argv[0]); + if(cmdargs->argc >= 1) { + q = quote_string(cmdargs->argv[0]); } else { q = stralloc(_("(no input?)")); } @@ -514,7 +627,10 @@ main( if (outfd != -1) aclose(outfd); - } while(cmd != QUIT); + } while(cmdargs->cmd != QUIT); + free_cmdargs(cmdargs); + + log_add(L_INFO, "pid-done %ld", (long)getpid()); am_release_feature_set(our_features); amfree(our_feature_string); @@ -581,7 +697,8 @@ static int databuf_flush( struct databuf * db) { - ssize_t written; + size_t written; + char *m; /* * If there's no data, do nothing. @@ -593,7 +710,7 @@ databuf_flush( /* * Write out the buffer */ - written = fullwrite(db->fd, db->dataout, + written = full_write(db->fd, db->dataout, (size_t)(db->datain - db->dataout)); if (written > 0) { db->dataout += written; @@ -603,8 +720,13 @@ databuf_flush( dumpsize += (dumpbytes / (off_t)1024); dumpbytes %= (off_t)1024; } - if (written < 0) { - errstr = squotef(_("data write: %s"), strerror(errno)); + if (written == 0) { + int save_errno = errno; + m = vstrallocf(_("data write: %s"), strerror(save_errno)); + amfree(errstr); + errstr = quote_string(m); + amfree(m); + errno = save_errno; return -1; } db->datain = db->dataout = db->buf; @@ -661,7 +783,7 @@ parse_info_line( size_t len; } fields[] = { { "BACKUP", file.program, SIZEOF(file.program) }, - { "DUMPER", file.dumper, SIZEOF(file.dumper) }, + { "APPLICATION", file.application, SIZEOF(file.application) }, { "RECOVER_CMD", file.recover_cmd, SIZEOF(file.recover_cmd) }, { "COMPRESS_SUFFIX", file.comp_suffix, SIZEOF(file.comp_suffix) }, { "SERVER_CUSTOM_COMPRESS", file.srvcompprog, SIZEOF(file.srvcompprog) }, @@ -734,6 +856,11 @@ process_dumpline( break; } + if (strcmp(tok, "no-op") == 0) { + amfree(buf); + return; + } + if (strcmp(tok, "end") == 0) { SET(status, GOT_ENDLINE); break; @@ -749,15 +876,18 @@ process_dumpline( dump_result = max(dump_result, 2); tok = strtok(NULL, ""); - if (tok == NULL || *tok != '[') { - errstr = newvstrallocf(errstr, _("bad remote error: %s"), str); - } else { - char *enderr; - - tok++; /* skip over '[' */ - if ((enderr = strchr(tok, ']')) != NULL) - *enderr = '\0'; - errstr = newstralloc(errstr, tok); + if (!errstr) { /* report first error line */ + if (tok == NULL || *tok != '[') { + errstr = newvstrallocf(errstr, _("bad remote error: %s"), + str); + } else { + char *enderr; + + tok++; /* skip over '[' */ + if ((enderr = strchr(tok, ']')) != NULL) + *enderr = '\0'; + errstr = newstralloc(errstr, tok); + } } break; } @@ -777,6 +907,7 @@ bad_line: break; } g_fprintf(errf, "%s\n", str); + errf_lines++; amfree(buf); } @@ -785,10 +916,6 @@ add_msg_data( const char * str, size_t len) { - static struct { - char *buf; /* buffer holding msg data */ - size_t size; /* size of alloced buffer */ - } msg = { NULL, 0 }; char *line, *ch; size_t buflen; @@ -868,24 +995,33 @@ add_msg_data( } -static void +static int log_msgout( logtype_t typ) { char *line; + int count = 0; fflush(errf); - if (fseek(errf, 0L, SEEK_SET) < 0) { + if (fseeko(errf, 0L, SEEK_SET) < 0) { dbprintf(_("log_msgout: warning - seek failed: %s\n"), strerror(errno)); } while ((line = agets(errf)) != NULL) { + if (errf_lines >= 100 && count >= 20) + break; if (line[0] != '\0') { log_add(typ, "%s", line); } amfree(line); + count++; + } + amfree(line); + + if (errf_lines >= 100) { + log_add(typ, "Look in the '%s' file for full error messages", errfname); } - afclose(errf); + return errf_lines < 100; } /* ------------- */ @@ -905,6 +1041,7 @@ finish_tapeheader( strncpy(file->name, hostname, SIZEOF(file->name) - 1); strncpy(file->disk, diskname, SIZEOF(file->disk) - 1); file->dumplevel = level; + file->blocksize = DISK_BLOCK_BYTES; /* * If we're doing the compression here, we need to override what @@ -949,23 +1086,38 @@ finish_tapeheader( if (srvencrypt != ENCRYPT_NONE) { file->encrypted= 1; if (srvencrypt == ENCRYPT_SERV_CUST) { - g_snprintf(file->decrypt_cmd, SIZEOF(file->decrypt_cmd), - " %s %s |", srv_encrypt, srv_decrypt_opt); + if (srv_decrypt_opt) { + g_snprintf(file->decrypt_cmd, SIZEOF(file->decrypt_cmd), + " %s %s |", srv_encrypt, srv_decrypt_opt); + strncpy(file->srv_decrypt_opt, srv_decrypt_opt, SIZEOF(file->srv_decrypt_opt) - 1); + file->srv_decrypt_opt[SIZEOF(file->srv_decrypt_opt) - 1] = '\0'; + } else { + g_snprintf(file->decrypt_cmd, SIZEOF(file->decrypt_cmd), + " %s |", srv_encrypt); + file->srv_decrypt_opt[0] = '\0'; + } strncpy(file->encrypt_suffix, "enc", SIZEOF(file->encrypt_suffix) - 1); file->encrypt_suffix[SIZEOF(file->encrypt_suffix) - 1] = '\0'; strncpy(file->srv_encrypt, srv_encrypt, SIZEOF(file->srv_encrypt) - 1); file->srv_encrypt[SIZEOF(file->srv_encrypt) - 1] = '\0'; - strncpy(file->srv_decrypt_opt, srv_decrypt_opt, SIZEOF(file->srv_decrypt_opt) - 1); - file->srv_decrypt_opt[SIZEOF(file->srv_decrypt_opt) - 1] = '\0'; } else if ( srvencrypt == ENCRYPT_CUST ) { + if (clnt_decrypt_opt) { + g_snprintf(file->decrypt_cmd, SIZEOF(file->decrypt_cmd), + " %s %s |", clnt_encrypt, clnt_decrypt_opt); + strncpy(file->clnt_decrypt_opt, clnt_decrypt_opt, + SIZEOF(file->clnt_decrypt_opt)); + file->clnt_decrypt_opt[SIZEOF(file->clnt_decrypt_opt) - 1] = '\0'; + } else { + g_snprintf(file->decrypt_cmd, SIZEOF(file->decrypt_cmd), + " %s |", clnt_encrypt); + file->clnt_decrypt_opt[0] = '\0'; + } g_snprintf(file->decrypt_cmd, SIZEOF(file->decrypt_cmd), " %s %s |", clnt_encrypt, clnt_decrypt_opt); strncpy(file->encrypt_suffix, "enc", SIZEOF(file->encrypt_suffix) - 1); file->encrypt_suffix[SIZEOF(file->encrypt_suffix) - 1] = '\0'; strncpy(file->clnt_encrypt, clnt_encrypt, SIZEOF(file->clnt_encrypt) - 1); file->clnt_encrypt[SIZEOF(file->clnt_encrypt) - 1] = '\0'; - strncpy(file->clnt_decrypt_opt, clnt_decrypt_opt, SIZEOF(file->clnt_decrypt_opt)); - file->clnt_decrypt_opt[SIZEOF(file->clnt_decrypt_opt) - 1] = '\0'; } } else { if (file->encrypt_suffix[0] == '\0') { @@ -977,6 +1129,10 @@ finish_tapeheader( file->encrypted= 1; } } + if (dle_str) + file->dle_str = stralloc(dle_str); + else + file->dle_str = NULL; } /* @@ -988,20 +1144,24 @@ write_tapeheader( dumpfile_t *file) { char * buffer; - ssize_t written; + size_t written; - buffer = build_header(file, DISK_BLOCK_BYTES); + if (debug_dumper > 1) + dump_dumpfile_t(file); + buffer = build_header(file, NULL, DISK_BLOCK_BYTES); + if (!buffer) /* this shouldn't happen */ + error(_("header does not fit in %zd bytes"), (size_t)DISK_BLOCK_BYTES); - written = fullwrite(outfd, buffer, DISK_BLOCK_BYTES); + written = full_write(outfd, buffer, DISK_BLOCK_BYTES); amfree(buffer); if(written == DISK_BLOCK_BYTES) return 0; - if(written < 0) - return written; return -1; } +int indexout = -1; + static int do_dump( struct databuf *db) @@ -1009,39 +1169,47 @@ do_dump( char *indexfile_tmp = NULL; char *indexfile_real = NULL; char level_str[NUM_STR_SIZE]; + char *time_str; char *fn; char *q; times_t runtime; double dumptime; /* Time dump took in secs */ - char *errfname = NULL; - int indexout; pid_t indexpid = -1; + char *m; + int to_unlink = 1; startclock(); + if (msg.buf) msg.buf[0] = '\0'; /* reset msg buffer */ status = 0; dump_result = 0; dumpbytes = dumpsize = headersize = origsize = (off_t)0; fh_init(&file); g_snprintf(level_str, SIZEOF(level_str), "%d", level); + time_str = get_timestamp_from_time(0); fn = sanitise_filename(diskname); + errf_lines = 0; errfname = newvstralloc(errfname, - AMANDA_TMPDIR, - "/", hostname, + AMANDA_DBGDIR, + "/log.error", NULL); + mkdir(errfname, 0700); + errfname = newvstralloc(errfname, + AMANDA_DBGDIR, + "/log.error/", hostname, ".", fn, ".", level_str, + ".", time_str, ".errout", NULL); amfree(fn); + amfree(time_str); if((errf = fopen(errfname, "w+")) == NULL) { errstr = newvstrallocf(errstr, "errfile open \"%s\": %s", errfname, strerror(errno)); amfree(errfname); goto failed; } - unlink(errfname); /* so it goes away on close */ - amfree(errfname); if (streams[INDEXFD].fd != NULL) { indexfile_real = getindexfname(hostname, diskname, dumper_timestamp, level); @@ -1062,7 +1230,7 @@ do_dump( indexfile_tmp, strerror(errno)); goto failed; } else { - if (runcompress(indexout, &indexpid, COMP_BEST) < 0) { + if (runcompress(indexout, &indexpid, COMP_BEST, "index compress") < 0) { aclose(indexout); goto failed; } @@ -1098,12 +1266,26 @@ do_dump( } dumpsize -= headersize; /* don't count the header */ - if (dumpsize <= (off_t)0) { + if (dumpsize <= (off_t)0 && data_path == DATA_PATH_AMANDA) { dumpsize = (off_t)0; dump_result = max(dump_result, 2); if (!errstr) errstr = stralloc(_("got no data")); } + if (data_path == DATA_PATH_DIRECTTCP) { + dumpsize = origsize; + } + + if (!ISSET(status, HEADER_DONE)) { + dump_result = max(dump_result, 2); + if (!errstr) errstr = stralloc(_("got no header information")); + } + + if (dumpsize == 0 && data_path == DATA_PATH_AMANDA) { + dump_result = max(dump_result, 2); + if (!errstr) errstr = stralloc(_("got no data")); + } + if (dump_result > 1) goto failed; @@ -1117,9 +1299,11 @@ do_dump( (long long)dumpsize, (isnormal(dumptime) ? ((double)dumpsize / (double)dumptime) : 0.0), (long long)origsize); - q = squotef("[%s]", errstr); + m = vstrallocf("[%s]", errstr); + q = quote_string(m); + amfree(m); putresult(DONE, _("%s %lld %lld %lu %s\n"), handle, - (long long)origsize, + (long long)origsize, (long long)dumpsize, (unsigned long)((double)dumptime+0.5), q); amfree(q); @@ -1133,20 +1317,29 @@ do_dump( case 1: log_start_multiline(); log_add(L_STRANGE, "%s %s %d [%s]", hostname, qdiskname, level, errstr); - log_msgout(L_STRANGE); + to_unlink = log_msgout(L_STRANGE); log_end_multiline(); break; } - if (errf) afclose(errf); + if (errf) + afclose(errf); + if (errfname) { + if (to_unlink) + unlink(errfname); + amfree(errfname); + } + + if (data_path == DATA_PATH_AMANDA) + aclose(db->fd); - aclose(db->fd); if (indexfile_tmp) { amwait_t index_status; /*@i@*/ aclose(indexout); waitpid(indexpid,&index_status,0); + log_add(L_INFO, "pid-done %ld", (long)indexpid); if (rename(indexfile_tmp, indexfile_real) != 0) { log_add(L_WARNING, _("could not rename \"%s\" to \"%s\": %s"), indexfile_tmp, indexfile_real, strerror(errno)); @@ -1157,55 +1350,72 @@ do_dump( if(db->compresspid != -1) { waitpid(db->compresspid,NULL,0); + log_add(L_INFO, "pid-done %ld", (long)db->compresspid); } if(db->encryptpid != -1) { waitpid(db->encryptpid,NULL,0); + log_add(L_INFO, "pid-done %ld", (long)db->encryptpid); } amfree(errstr); + dumpfile_free_data(&file); return 1; failed: - q = squotef("[%s]", errstr); + m = vstrallocf("[%s]", errstr); + q = quote_string(m); putresult(FAILED, "%s %s\n", handle, q); amfree(q); + amfree(m); aclose(db->fd); /* kill all child process */ if (db->compresspid != -1) { g_fprintf(stderr,_("%s: kill compress command\n"),get_pname()); if (kill(db->compresspid, SIGTERM) < 0) { - if (errno != ESRCH) + if (errno != ESRCH) { g_fprintf(stderr,_("%s: can't kill compress command: %s\n"), get_pname(), strerror(errno)); + } else { + log_add(L_INFO, "pid-done %ld", (long)db->compresspid); + } } else { waitpid(db->compresspid,NULL,0); + log_add(L_INFO, "pid-done %ld", (long)db->compresspid); } } if (db->encryptpid != -1) { g_fprintf(stderr,_("%s: kill encrypt command\n"),get_pname()); if (kill(db->encryptpid, SIGTERM) < 0) { - if (errno != ESRCH) + if (errno != ESRCH) { g_fprintf(stderr,_("%s: can't kill encrypt command: %s\n"), get_pname(), strerror(errno)); + } else { + log_add(L_INFO, "pid-done %ld", (long)db->encryptpid); + } } else { waitpid(db->encryptpid,NULL,0); + log_add(L_INFO, "pid-done %ld", (long)db->encryptpid); } } if (indexpid != -1) { g_fprintf(stderr,_("%s: kill index command\n"),get_pname()); if (kill(indexpid, SIGTERM) < 0) { - if (errno != ESRCH) + if (errno != ESRCH) { g_fprintf(stderr,_("%s: can't kill index command: %s\n"), get_pname(),strerror(errno)); + } else { + log_add(L_INFO, "pid-done %ld", (long)indexpid); + } } else { waitpid(indexpid,NULL,0); + log_add(L_INFO, "pid-done %ld", (long)indexpid); } } @@ -1213,11 +1423,17 @@ failed: log_add(L_FAIL, _("%s %s %s %d [%s]"), hostname, qdiskname, dumper_timestamp, level, errstr); if (errf) { - log_msgout(L_FAIL); + to_unlink = log_msgout(L_FAIL); } log_end_multiline(); - if (errf) afclose(errf); + if (errf) + afclose(errf); + if (errfname) { + if (to_unlink) + unlink(errfname); + amfree(errfname); + } if (indexfile_tmp) { unlink(indexfile_tmp); @@ -1272,6 +1488,14 @@ read_mesgfd( } if (ISSET(status, GOT_INFO_ENDLINE) && !ISSET(status, HEADER_DONE)) { + /* Use the first in the dataport_list */ + in_port_t data_port; + char *data_host = dataport_list; + char *s= strchr(dataport_list, ':'); + *s = '\0'; + s++; + data_port = atoi(s); + SET(status, HEADER_DONE); /* time to do the header */ finish_tapeheader(&file); @@ -1280,8 +1504,25 @@ read_mesgfd( strerror(errno)); dump_result = 2; stop_dump(); + dumpfile_free_data(&file); return; } + dumpfile_free_data(&file); + aclose(db->fd); + if (data_path == DATA_PATH_AMANDA) { + g_debug(_("Sending data to %s:%d\n"), data_host, data_port); + db->fd = stream_client(data_host, data_port, + STREAM_BUFSIZE, 0, NULL, 0); + if (db->fd == -1) { + errstr = newvstrallocf(errstr, + _("Can't open data output stream: %s"), + strerror(errno)); + dump_result = 2; + stop_dump(); + return; + } + } + dumpsize += (off_t)DISK_BLOCK_KB; headersize += (off_t)DISK_BLOCK_KB; @@ -1297,7 +1538,7 @@ read_mesgfd( * reading the datafd. */ if ((srvcompress != COMP_NONE) && (srvcompress != COMP_CUST)) { - if (runcompress(db->fd, &db->compresspid, srvcompress) < 0) { + if (runcompress(db->fd, &db->compresspid, srvcompress, "data compress") < 0) { dump_result = 2; stop_dump(); return; @@ -1333,6 +1574,7 @@ read_datafd( errstr = newvstrallocf(errstr, _("data read: %s"), security_stream_geterror(streams[DATAFD].fd)); dump_result = 2; + aclose(db->fd); stop_dump(); return; } @@ -1350,6 +1592,7 @@ read_datafd( } security_stream_close(streams[DATAFD].fd); streams[DATAFD].fd = NULL; + aclose(db->fd); /* * If the mesg fd and index fd has also shut down, then we're done. */ @@ -1364,7 +1607,8 @@ read_datafd( */ assert(buf != NULL); if (databuf_write(db, buf, (size_t)size) < 0) { - errstr = newvstrallocf(errstr, _("data write: %s"), strerror(errno)); + int save_errno = errno; + errstr = newvstrallocf(errstr, _("data write: %s"), strerror(save_errno)); dump_result = 2; stop_dump(); return; @@ -1412,6 +1656,7 @@ read_indexfd( if ((set_datafd == 0 || streams[DATAFD].fd == NULL) && streams[MESGFD].fd == NULL) stop_dump(); + aclose(indexout); return; } @@ -1420,7 +1665,7 @@ read_indexfd( /* * We ignore error while writing to the index file. */ - if (fullwrite(fd, buf, (size_t)size) < 0) { + if (full_write(fd, buf, (size_t)size) < (size_t)size) { /* Ignore error, but schedule another read. */ if(indexfderror == 0) { indexfderror = 1; @@ -1430,6 +1675,78 @@ read_indexfd( security_stream_read(streams[INDEXFD].fd, read_indexfd, cookie); } +static void +handle_filter_stderr( + void *cookie) +{ + filter_t *filter = cookie; + ssize_t nread; + char *b, *p; + gint64 len; + + event_release(filter->event); + + if (filter->buffer == NULL) { + /* allocate initial buffer */ + filter->buffer = g_malloc(2048); + filter->first = 0; + filter->size = 0; + filter->allocated_size = 2048; + } else if (filter->first > 0) { + if (filter->allocated_size - filter->size - filter->first < 1024) { + memmove(filter->buffer, filter->buffer + filter->first, + filter->size); + filter->first = 0; + } + } else if (filter->allocated_size - filter->size < 1024) { + /* double the size of the buffer */ + filter->allocated_size *= 2; + filter->buffer = g_realloc(filter->buffer, filter->allocated_size); + } + + nread = read(filter->fd, filter->buffer + filter->first + filter->size, + filter->allocated_size - filter->first - filter->size - 2); + + if (nread != 0) { + dump_result = max(dump_result, 2); + } + + if (nread <= 0) { + aclose(filter->fd); + if (filter->size > 0 && filter->buffer[filter->first + filter->size - 1] != '\n') { + /* Add a '\n' at end of buffer */ + filter->buffer[filter->first + filter->size] = '\n'; + filter->size++; + } + } else { + filter->size += nread; + } + + /* process all complete lines */ + b = filter->buffer + filter->first; + filter->buffer[filter->first + filter->size] = '\0'; + while (b < filter->buffer + filter->first + filter->size && + (p = strchr(b, '\n')) != NULL) { + *p = '\0'; + g_fprintf(errf, _("? %s: %s\n"), filter->name, b); + if (errstr == NULL) { + errstr = stralloc(b); + } + len = p - b + 1; + filter->first += len; + filter->size -= len; + b = p + 1; + } + + if (nread <= 0) { + g_free(filter->buffer); + g_free(filter); + } else { + filter->event = event_register((event_id_t)filter->fd, EV_READFD, + handle_filter_stderr, filter); + } +} + /* * Startup a timeout in the event handler. If the arg is 0, * then remove the timeout. @@ -1478,7 +1795,19 @@ timeout_callback( static void stop_dump(void) { - int i; + int i; + struct cmdargs *cmdargs = NULL; + + /* Check if I have a pending ABORT command */ + cmdargs = get_pending_cmd(); + if (cmdargs) { + if (cmdargs->cmd != ABORT) { + error(_("beurk %d"), cmdargs->cmd); + } + amfree(errstr); + errstr = stralloc(cmdargs->argv[1]); + free_cmdargs(cmdargs); + } for (i = 0; i < NSTREAMS; i++) { if (streams[i].fd != NULL) { @@ -1486,6 +1815,7 @@ stop_dump(void) streams[i].fd = NULL; } } + aclose(indexout); timeout(0); } @@ -1500,9 +1830,12 @@ static int runcompress( int outfd, pid_t * pid, - comp_t comptype) + comp_t comptype, + char *name) { int outpipe[2], rval; + int errpipe[2]; + filter_t *filter; assert(outfd >= 0); assert(pid != NULL); @@ -1513,11 +1846,19 @@ runcompress( return (-1); } + /* errpipe[0] is pipe's output, outpipe[1] is input. */ + if (pipe(errpipe) < 0) { + errstr = newvstrallocf(errstr, _("pipe: %s"), strerror(errno)); + return (-1); + } + switch (*pid = fork()) { case -1: errstr = newvstrallocf(errstr, _("couldn't fork: %s"), strerror(errno)); aclose(outpipe[0]); aclose(outpipe[1]); + aclose(errpipe[0]); + aclose(errpipe[1]); return (-1); default: rval = dup2(outpipe[1], outfd); @@ -1525,8 +1866,20 @@ runcompress( errstr = newvstrallocf(errstr, _("couldn't dup2: %s"), strerror(errno)); aclose(outpipe[1]); aclose(outpipe[0]); + aclose(errpipe[1]); + filter = g_new0(filter_t, 1); + filter->fd = errpipe[0]; + filter->name = name; + filter->buffer = NULL; + filter->size = 0; + filter->allocated_size = 0; + filter->event = event_register((event_id_t)filter->fd, EV_READFD, + handle_filter_stderr, filter); +g_debug("event register %s %d", name, filter->fd); return (rval); case 0: + close(outpipe[1]); + close(errpipe[0]); if (dup2(outpipe[0], 0) < 0) { error(_("err dup2 in: %s"), strerror(errno)); /*NOTREACHED*/ @@ -1535,15 +1888,28 @@ runcompress( error(_("err dup2 out: %s"), strerror(errno)); /*NOTREACHED*/ } - safe_fd(-1, 0); + if (dup2(errpipe[1], 2) == -1) { + error(_("err dup2 err: %s"), strerror(errno)); + /*NOTREACHED*/ + } if (comptype != COMP_SERVER_CUST) { + char *base = stralloc(COMPRESS_PATH); + log_add(L_INFO, "%s pid %ld", basename(base), (long)getpid()); + amfree(base); + safe_fd(-1, 0); + set_root_privs(-1); execlp(COMPRESS_PATH, COMPRESS_PATH, ( comptype == COMP_BEST ? COMPRESS_BEST_OPT : COMPRESS_FAST_OPT), (char *)NULL); error(_("error: couldn't exec %s: %s"), COMPRESS_PATH, strerror(errno)); /*NOTREACHED*/ } else if (*srvcompprog) { + char *base = stralloc(srvcompprog); + log_add(L_INFO, "%s pid %ld", basename(base), (long)getpid()); + amfree(base); + safe_fd(-1, 0); + set_root_privs(-1); execlp(srvcompprog, srvcompprog, (char *)0); - error(_("error: couldn't exec server custom filter%s.\n"), srvcompprog); + error(_("error: couldn't exec server custom compression '%s'.\n"), srvcompprog); /*NOTREACHED*/ } } @@ -1564,6 +1930,8 @@ runencrypt( encrypt_t encrypttype) { int outpipe[2], rval; + int errpipe[2]; + filter_t *filter; assert(outfd >= 0); assert(pid != NULL); @@ -1574,11 +1942,19 @@ runencrypt( return (-1); } + /* errpipe[0] is pipe's output, outpipe[1] is input. */ + if (pipe(errpipe) < 0) { + errstr = newvstrallocf(errstr, _("pipe: %s"), strerror(errno)); + return (-1); + } + switch (*pid = fork()) { case -1: errstr = newvstrallocf(errstr, _("couldn't fork: %s"), strerror(errno)); aclose(outpipe[0]); aclose(outpipe[1]); + aclose(errpipe[0]); + aclose(errpipe[1]); return (-1); default: rval = dup2(outpipe[1], outfd); @@ -1586,8 +1962,19 @@ runencrypt( errstr = newvstrallocf(errstr, _("couldn't dup2: %s"), strerror(errno)); aclose(outpipe[1]); aclose(outpipe[0]); + aclose(errpipe[1]); + filter = g_new0(filter_t, 1); + filter->fd = errpipe[0]; + filter->name = "encrypt"; + filter->buffer = NULL; + filter->size = 0; + filter->allocated_size = 0; + filter->event = event_register((event_id_t)filter->fd, EV_READFD, + handle_filter_stderr, filter); +g_debug("event register %s %d", "encrypt data", filter->fd); return (rval); - case 0: + case 0: { + char *base; if (dup2(outpipe[0], 0) < 0) { error(_("err dup2 in: %s"), strerror(errno)); /*NOTREACHED*/ @@ -1596,12 +1983,22 @@ runencrypt( error(_("err dup2 out: %s"), strerror(errno)); /*NOTREACHED*/ } + if (dup2(errpipe[1], 2) == -1) { + error(_("err dup2 err: %s"), strerror(errno)); + /*NOTREACHED*/ + } + close(errpipe[0]); + base = stralloc(srv_encrypt); + log_add(L_INFO, "%s pid %ld", basename(base), (long)getpid()); + amfree(base); safe_fd(-1, 0); if ((encrypttype == ENCRYPT_SERV_CUST) && *srv_encrypt) { + set_root_privs(-1); execlp(srv_encrypt, srv_encrypt, (char *)0); - error(_("error: couldn't exec server encryption%s.\n"), srv_encrypt); + error(_("error: couldn't exec server custom encryption '%s'.\n"), srv_encrypt); /*NOTREACHED*/ } + } } /*NOTREACHED*/ return (-1); @@ -1624,6 +2021,8 @@ sendbackup_response( assert(response_error != NULL); assert(sech != NULL); + security_close_connection(sech, hostname); + if (pkt == NULL) { errstr = newvstrallocf(errstr, _("[request failed: %s]"), security_geterror(sech)); @@ -1631,8 +2030,6 @@ sendbackup_response( return; } - security_close_connection(sech, hostname); - extra = NULL; memset(ports, 0, SIZEOF(ports)); if (pkt->type == P_NAK) { @@ -1729,6 +2126,9 @@ bad_nak: char ch; *p++ = '\0'; if(strncmp_const_skip(tok, "features=", tok, ch) == 0) { + char *u = strchr(tok, ';'); + if (u) + *u = '\0'; am_release_feature_set(their_features); if((their_features = am_string_to_feature(tok)) == NULL) { errstr = newvstrallocf(errstr, @@ -1736,6 +2136,8 @@ bad_nak: tok); goto parse_error; } + if (u) + *u = ';'; } tok = p; } @@ -1772,16 +2174,6 @@ bad_nak: for (i = 0; i < NSTREAMS; i++) { if (streams[i].fd == NULL) continue; -#ifdef KRB4_SECURITY - /* - * XXX krb4 historically never authenticated the index stream! - * We need to reproduce this lossage here to preserve compatibility - * with old clients. - * It is wrong to delve into sech, but we have no choice here. - */ - if (strcasecmp(sech->driver->name, "krb4") == 0 && i == INDEXFD) - continue; -#endif if (security_stream_auth(streams[i].fd) < 0) { errstr = newvstrallocf(errstr, _("[could not authenticate %s stream: %s]"), @@ -1835,6 +2227,8 @@ dumper_get_security_conf( return (amandad_path); } else if(strcmp(string, "client_username")==0) { return (client_username); + } else if(strcmp(string, "client_port")==0) { + return (client_port); } else if(strcmp(string, "ssh_keys")==0) { return (ssh_keys); } else if(strcmp(string, "kencrypt")==0) { @@ -1856,15 +2250,17 @@ startup_dump( const char *progname, const char *amandad_path, const char *client_username, + const char *client_port, const char *ssh_keys, + const char *auth, const char *options) { char level_string[NUM_STR_SIZE]; char *req = NULL; - char *authopt, *endauthopt, authoptbuf[80]; + char *authopt; int response_error; const security_driver_t *secdrv; - char *backup_api; + char *application_api; int has_features; int has_hostname; int has_device; @@ -1873,7 +2269,9 @@ startup_dump( (void)disk; /* Quiet unused parameter warning */ (void)amandad_path; /* Quiet unused parameter warning */ (void)client_username; /* Quiet unused parameter warning */ + (void)client_port; /* Quiet unused parameter warning */ (void)ssh_keys; /* Quiet unused parameter warning */ + (void)auth; /* Quiet unused parameter warning */ has_features = am_has_feature(their_features, fe_req_options_features); has_hostname = am_has_feature(their_features, fe_req_options_hostname); @@ -1886,28 +2284,13 @@ startup_dump( * Options really need to be pre-parsed into some sort of structure * much earlier, and then flattened out again before transmission. */ - authopt = strstr(options, "auth="); - if (authopt == NULL) { - authopt = "BSD"; - } else { - endauthopt = strchr(authopt, ';'); - if ((endauthopt == NULL) || - ((sizeof(authoptbuf) - 1) < (size_t)(endauthopt - authopt))) { - authopt = "BSD"; - } else { - authopt += strlen("auth="); - strncpy(authoptbuf, authopt, (size_t)(endauthopt - authopt)); - authoptbuf[endauthopt - authopt] = '\0'; - authopt = authoptbuf; - } - } g_snprintf(level_string, SIZEOF(level_string), "%d", level); if(strcmp(progname, "DUMP") == 0 || strcmp(progname, "GNUTAR") == 0) { - backup_api = ""; + application_api = ""; } else { - backup_api = "BACKUP "; + application_api = "BACKUP "; } req = vstralloc("SERVICE sendbackup\n", "OPTIONS ", @@ -1918,25 +2301,58 @@ startup_dump( has_hostname ? hostname : "", has_hostname ? ";" : "", has_config ? "config=" : "", - has_config ? config_name : "", + has_config ? get_config_name() : "", has_config ? ";" : "", "\n", - backup_api, progname, - " ", qdiskname, - " ", device && has_device ? device : "", - " ", level_string, - " ", dumpdate, - " OPTIONS ", options, - /* compat: if auth=krb4, send krb4-auth */ - (strcasecmp(authopt, "krb4") ? "" : "krb4-auth"), - "\n", NULL); + amfree(dle_str); + if (am_has_feature(their_features, fe_req_xml)) { + char *p = NULL; + char *pclean; + vstrextend(&p, "\n", NULL); + if (*application_api != '\0') { + vstrextend(&p, " APPLICATION\n", NULL); + } else { + vstrextend(&p, " ", progname, "\n", NULL); + } + vstrextend(&p, " ", b64disk, "\n", NULL); + if (device && has_device) { + vstrextend(&p, " ", b64device, "\n", + NULL); + } + vstrextend(&p, " ", level_string, "\n", NULL); + vstrextend(&p, options+1, "\n", NULL); + pclean = clean_dle_str_for_client(p); + vstrextend(&req, pclean, NULL); + amfree(pclean); + dle_str = p; + } else if (*application_api != '\0') { + errstr = newvstrallocf(errstr, + _("[does not support application-api]")); + amfree(req); + return 2; + } else { + authopt = strstr(options, "auth="); + if (auth == NULL) { + auth = "BSD"; + } + vstrextend(&req, + progname, + " ", qdiskname, + " ", device && has_device ? device : "", + " ", level_string, + " ", dumpdate, + " OPTIONS ", options, + "\n", + NULL); + } + dbprintf(_("send request:\n----\n%s\n----\n\n"), req); - secdrv = security_getdriver(authopt); + secdrv = security_getdriver(auth); if (secdrv == NULL) { errstr = newvstrallocf(errstr, - _("[could not find security driver '%s']"), authopt); + _("[could not find security driver '%s']"), auth); amfree(req); return 2; }