Imported Upstream version 3.2.0
[debian/amanda] / client-src / sendbackup.c
index fd71862517aa81e17977217e612c718874066f47..dacb961e143ab296e4c4e19e3de67a14994795cd 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
- * Copyright (c) 1991-1998 University of Maryland at College Park
+ * Copyright (c) 1991-1999 University of Maryland at College Park
  * All Rights Reserved.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * file named AUTHORS, in the root directory of this distribution.
  */
 /* 
- * $Id: sendbackup.c,v 1.44.2.9.4.4.2.16 2004/01/14 12:59:12 martinea Exp $
+ * $Id: sendbackup.c,v 1.88 2006/07/25 18:27:56 martinea Exp $
  *
  * common code for the sendbackup-* programs.
  */
 
 #include "amanda.h"
+#include "match.h"
 #include "sendbackup.h"
 #include "clock.h"
 #include "pipespawn.h"
 #include "amfeatures.h"
-#include "stream.h"
 #include "arglist.h"
 #include "getfsent.h"
-#include "version.h"
+#include "conffile.h"
+#include "amandates.h"
+
+#define sendbackup_debug(i, ...) do {  \
+       if ((i) <= debug_sendbackup) {  \
+           dbprintf(__VA_LIST__);      \
+       }                               \
+} while (0)
 
 #define TIMEOUT 30
 
-int comppid = -1;
-int dumppid = -1;
-int tarpid = -1;
-int encpid = -1;
-int indexpid = -1;
+pid_t comppid = (pid_t)-1;
+pid_t dumppid = (pid_t)-1;
+pid_t tarpid = (pid_t)-1;
+pid_t encpid = (pid_t)-1;
+pid_t indexpid = (pid_t)-1;
+pid_t application_api_pid = (pid_t)-1;
 char *errorstr = NULL;
 
-int data_socket, data_port, dataf;
-int mesg_socket, mesg_port, mesgf;
-int index_socket, index_port, indexf;
-
-option_t *options;
+int datafd;
+int mesgfd;
+int indexfd;
 
-#ifdef KRB4_SECURITY
-#include "sendbackup-krb4.h"
-#else                                  /* I'd tell you what this does */
-#define NAUGHTY_BITS                   /* but then I'd have to kill you */
-#endif
+g_option_t *g_options = NULL;
 
 long dump_size = -1;
 
 backup_program_t *program = NULL;
+dle_t *gdle = NULL;
 
 static am_feature_t *our_features = NULL;
 static char *our_feature_string = NULL;
-static g_option_t *g_options = NULL;
+static char *amandad_auth = NULL;
 
 /* local functions */
-int main P((int argc, char **argv));
-char *optionstr P((option_t *options));
-char *childstr P((int pid));
-int check_status P((int pid, amwait_t w));
-
-int pipefork P((void (*func) P((void)), char *fname, int *stdinfd,
-               int stdoutfd, int stderrfd));
-void parse_backup_messages P((int mesgin));
-static void process_dumpline P((char *str));
+int main(int argc, char **argv);
+char *childstr(pid_t pid);
+int check_status(pid_t pid, amwait_t w, int mesgfd);
+
+pid_t pipefork(void (*func)(void), char *fname, int *stdinfd,
+               int stdoutfd, int stderrfd);
+int check_result(int mesgfd);
+void parse_backup_messages(dle_t *dle, int mesgin);
+static void process_dumpline(char *str);
+static void save_fd(int *, int);
+void application_api_info_tapeheader(int mesgfd, char *prog, dle_t *dle);
+
+int fdprintf(int fd, char *format, ...) G_GNUC_PRINTF(2, 3);
+
+int
+fdprintf(
+    int   fd,
+    char *format,
+    ...)
+{
+    va_list  argp;
+    char    *s;
+    int      r;
 
+    arglist_start(argp, format);
+    s = g_strdup_vprintf(format, argp);
+    arglist_end(argp);
 
-char *optionstr(option_t *options)
-{
-    static char *optstr = NULL;
-    char *compress_opt = "";
-    char *record_opt = "";
-    char *bsd_opt = "";
-    char *krb4_opt = "";
-    char *kencrypt_opt = "";
-    char *index_opt = "";
-    char *exclude_file_opt;
-    char *exclude_list_opt;
-    char *exc = NULL;
-    sle_t *excl;
-
-    if(options->compress == COMPR_BEST)
-       compress_opt = "compress-best;";
-    else if(options->compress == COMPR_FAST)
-       compress_opt = "compress-fast;";
-    else if(options->compress == COMPR_SERVER_BEST)
-       compress_opt = "srvcomp-best;";
-    else if(options->compress == COMPR_SERVER_FAST)
-       compress_opt = "srvcomp-fast;";
-    if(options->no_record) record_opt = "no-record;";
-    if(options->bsd_auth) bsd_opt = "bsd-auth;";
-#ifdef KRB4_SECURITY
-    if(options->krb4_auth) krb4_opt = "krb4-auth;";
-    if(options->kencrypt) kencrypt_opt = "kencrypt;";
-#endif
-    if(options->createindex) index_opt = "index;";
-
-    exclude_file_opt = stralloc("");
-    if(options->exclude_file) {
-       for(excl = options->exclude_file->first; excl != NULL; excl=excl->next){
-           exc = newvstralloc(exc, "exclude-file=", excl->name, ";", NULL);
-           strappend(exclude_file_opt, exc);
-       }
-    }
-    exclude_list_opt = stralloc("");
-    if(options->exclude_list) {
-       for(excl = options->exclude_list->first; excl != NULL; excl=excl->next){
-           exc = newvstralloc(exc, "exclude-list=", excl->name, ";", NULL);
-           strappend(exclude_list_opt, exc);
-       }
-    }
-    optstr = newvstralloc(optstr,
-                         compress_opt,
-                         record_opt,
-                         bsd_opt,
-                         krb4_opt,
-                         kencrypt_opt,
-                         index_opt,
-                         exclude_file_opt,
-                         exclude_list_opt,
-                         NULL);
-    return optstr;
+    r = full_write(fd, s, strlen(s));
+    amfree(s);
+    return r;
 }
 
-
-int main(argc, argv)
-int argc;
-char **argv;
+int
+main(
+    int                argc,
+    char **    argv)
 {
     int interactive = 0;
-    int level, mesgpipe[2];
-    char *prog, *disk, *amdevice, *dumpdate, *stroptions;
+    int level = 0;
+    int mesgpipe[2];
+    dle_t *dle = NULL;
+    char *dumpdate, *stroptions;
+    char *qdisk = NULL;
+    char *qamdevice = NULL;
     char *line = NULL;
     char *err_extra = NULL;
     char *s;
     int i;
     int ch;
-    unsigned long malloc_hist_1, malloc_size_1;
-    unsigned long malloc_hist_2, malloc_size_2;
-    int fd;
+    GSList *errlist;
+    FILE   *mesgstream;
+    level_t *alevel;
 
     /* initialize */
-
-    for(fd = 3; fd < FD_SETSIZE; fd++) {
-       /*
-        * Make sure nobody spoofs us with a lot of extra open files
-        * that would cause an open we do to get a very high file
-        * descriptor, which in turn might be used as an index into
-        * an array (e.g. an fd_set).
-        */
-#ifdef KRB4_SECURITY
-       if (fd != KEY_PIPE)     /* XXX interface needs to be fixed */
-#endif
-               close(fd);
-    }
+    /*
+     * 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(DATA_FD_OFFSET, DATA_FD_COUNT*2);
+    openbsd_fd_inform();
 
     safe_cd();
 
     set_pname("sendbackup");
 
-    malloc_size_1 = malloc_inuse(&malloc_hist_1);
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
+
+    /* Don't die when interrupt received */
+    signal(SIGINT, SIG_IGN);
 
-    interactive = (argc > 1 && strcmp(argv[1],"-t") == 0);
-    erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
-    dbopen();
+    if(argc > 1 && strcmp(argv[1],"-t") == 0) {
+       interactive = 1;
+       argc--;
+       argv++;
+    } else {
+       interactive = 0;
+    }
+
+    add_amanda_log_handler(amanda_log_stderr);
+    add_amanda_log_handler(amanda_log_syslog);
+    dbopen(DBG_SUBDIR_CLIENT);
     startclock();
-    dbprintf(("%s: version %s\n", argv[0], version()));
+    dbprintf(_("Version %s\n"), VERSION);
+
+    if(argc > 2 && strcmp(argv[1], "amandad") == 0) {
+       amandad_auth = stralloc(argv[2]);
+    }
 
     our_features = am_init_feature_set();
     our_feature_string = am_feature_to_string(our_features);
 
+    config_init(CONFIG_INIT_CLIENT, NULL);
+    /* (check for config errors comes later) */
+
+    check_running_as(RUNNING_AS_CLIENT_LOGIN);
+
     if(interactive) {
        /*
         * In interactive (debug) mode, the backup data is sent to
@@ -191,92 +182,130 @@ char **argv;
         * programs on the tape host are set up.  The index service is
         * run and goes to stdout.
         */
-       fprintf(stderr, "%s: running in interactive test mode\n", get_pname());
+       g_fprintf(stderr, _("%s: running in interactive test mode\n"), get_pname());
        fflush(stderr);
     }
 
-    prog = NULL;
-    disk = NULL;
-    amdevice = NULL;
+    qdisk = NULL;
     dumpdate = NULL;
     stroptions = NULL;
 
-    /* parse dump request */
-
     for(; (line = agets(stdin)) != NULL; free(line)) {
+       if (line[0] == '\0')
+           continue;
        if(interactive) {
-           fprintf(stderr, "%s> ", get_pname());
+           g_fprintf(stderr, "%s> ", get_pname());
            fflush(stderr);
        }
-#define sc "OPTIONS "
-       if(strncmp(line, sc, sizeof(sc)-1) == 0) {
-#undef sc
+       if(strncmp_const(line, "OPTIONS ") == 0) {
            g_options = parse_g_options(line+8, 1);
            if(!g_options->hostname) {
                g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
                gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
                g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
            }
+
+           if (g_options->config) {
+               /* overlay this configuration on the existing (nameless) configuration */
+               config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
+                           g_options->config);
+
+               dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
+           }
+
+           /* check for any config errors now */
+           if (config_errors(&errlist) >= CFGERR_ERRORS) {
+               char *errstr = config_errors_to_error_string(errlist);
+               g_printf("%s\n", errstr);
+               dbclose();
+               return 1;
+           }
+
+           if (am_has_feature(g_options->features, fe_req_xml)) {
+               break;
+           }
            continue;
        }
 
-       if (prog != NULL) {
-           err_extra = "multiple requests";
+       if (dle && dle->program != NULL) {
+           err_extra = _("multiple requests");
            goto err;
        }
 
+       dbprintf(_("  sendbackup req: <%s>\n"), line);
+       dle = alloc_dle();
+
        s = line;
        ch = *s++;
 
        skip_whitespace(s, ch);                 /* find the program name */
        if(ch == '\0') {
-           err_extra = "no program name";
+           err_extra = _("no program name");
            goto err;                           /* no program name */
        }
-       prog = s - 1;
+       dle->program = s - 1;
        skip_non_whitespace(s, ch);
        s[-1] = '\0';
-       prog = stralloc(prog);
+
+        if (strcmp(dle->program, "APPLICATION")==0) {
+            dle->program_is_application_api=1;
+            skip_whitespace(s, ch);             /* find dumper name */
+            if (ch == '\0') {
+                goto err;                       /* no program */
+            }
+            dle->program = s - 1;
+            skip_non_whitespace(s, ch);
+            s[-1] = '\0';
+        }
+       dle->program = stralloc(dle->program);
 
        skip_whitespace(s, ch);                 /* find the disk name */
        if(ch == '\0') {
-           err_extra = "no disk name";
+           err_extra = _("no disk name");
            goto err;                           /* no disk name */
        }
-       amfree(disk);
-       disk = s - 1;
-       skip_non_whitespace(s, ch);
+
+       amfree(qdisk);
+       qdisk = s - 1;
+       ch = *qdisk;
+       skip_quoted_string(s, ch);
        s[-1] = '\0';
-       disk = stralloc(disk);
+       qdisk = stralloc(qdisk);
+       dle->disk = unquote_string(qdisk);
 
        skip_whitespace(s, ch);                 /* find the device or level */
        if (ch == '\0') {
-           err_extra = "bad level";
+           err_extra = _("bad level");
            goto err;
        }
 
        if(!isdigit((int)s[-1])) {
-           amfree(amdevice);
-           amdevice = s - 1;
-           skip_non_whitespace(s, ch);
+           amfree(qamdevice);
+           qamdevice = s - 1;
+           ch = *qamdevice;
+           skip_quoted_string(s, ch);
            s[-1] = '\0';
-           amdevice = stralloc(amdevice);
+           qamdevice = stralloc(qamdevice);
+           dle->device = unquote_string(qamdevice);
            skip_whitespace(s, ch);             /* find level number */
        }
        else {
-           amdevice = stralloc(disk);
+           dle->device = stralloc(dle->disk);
+           qamdevice = stralloc(qdisk);
        }
-
                                                /* find the level number */
        if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
-           err_extra = "bad level";
+           err_extra = _("bad level");
            goto err;                           /* bad level */
        }
        skip_integer(s, ch);
+       alevel = g_new0(level_t, 1);
+       alevel->level = level;
+       dle->levellist = g_slist_append(dle->levellist, alevel);
 
        skip_whitespace(s, ch);                 /* find the dump date */
        if(ch == '\0') {
-           err_extra = "no dumpdate";
+           err_extra = _("no dumpdate");
            goto err;                           /* no dumpdate */
        }
        amfree(dumpdate);
@@ -287,254 +316,489 @@ char **argv;
 
        skip_whitespace(s, ch);                 /* find the options keyword */
        if(ch == '\0') {
-           err_extra = "no options";
+           err_extra = _("no options");
            goto err;                           /* no options */
        }
-#define sc "OPTIONS "
-       if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           err_extra = "no OPTIONS keyword";
+       if(strncmp_const_skip(s - 1, "OPTIONS ", s, ch) != 0) {
+           err_extra = _("no OPTIONS keyword");
            goto err;                           /* no options */
        }
-       s += sizeof(sc)-1;
-       ch = s[-1];
-#undef sc
        skip_whitespace(s, ch);                 /* find the options string */
        if(ch == '\0') {
-           err_extra = "bad options string";
+           err_extra = _("bad options string");
            goto err;                           /* no options */
        }
        amfree(stroptions);
        stroptions = stralloc(s - 1);
     }
     amfree(line);
+    if (g_options == NULL) {
+       g_printf(_("ERROR [Missing OPTIONS line in sendbackup input]\n"));
+       error(_("Missing OPTIONS line in sendbackup input\n"));
+       /*NOTREACHED*/
+    }
 
-    dbprintf(("  parsed request as: program `%s'\n", prog));
-    dbprintf(("                     disk `%s'\n", disk));
-    dbprintf(("                     device `%s'\n", amdevice));
-    dbprintf(("                     level %d\n", level));
-    dbprintf(("                     since %s\n", dumpdate));
-    dbprintf(("                     options `%s'\n", stroptions));
+    if (am_has_feature(g_options->features, fe_req_xml)) {
+       char *errmsg = NULL;
 
-    for(i = 0; programs[i] != NULL; i++) {
-       if (strcmp(programs[i]->name, prog) == 0) {
-           break;
+       dle = amxml_parse_node_FILE(stdin, &errmsg);
+       if (errmsg) {
+           err_extra = errmsg;
+           goto err;
+       }
+       if (!dle) {
+           err_extra = _("One DLE required");
+           goto err;
+       } else if (dle->next) {
+           err_extra = _("Only one DLE allowed");
+           goto err;
        }
+
+       qdisk = quote_string(dle->disk);
+       if (dle->device == NULL)
+           dle->device = stralloc(dle->disk);
+       qamdevice = quote_string(dle->device);
+       dumpdate = stralloc("NODATE");
+       stroptions = stralloc("");
+    } else {
+       parse_options(stroptions, dle, g_options->features, 0);
     }
-    if (programs[i] == NULL) {
-       error("ERROR [%s: unknown program %s]", get_pname(), prog);
+    gdle = dle;
+
+    if (dle->program   == NULL ||
+       dle->disk      == NULL ||
+       dle->device    == NULL ||
+       dle->levellist == NULL ||
+       dumpdate       == NULL) {
+       err_extra = _("no valid sendbackup request");
+       goto err;
     }
-    program = programs[i];
 
-    options = parse_options(stroptions, disk, amdevice, g_options->features, 0);
+    if (g_slist_length(dle->levellist) != 1) {
+       err_extra = _("Too many level");
+       goto err;
+    }
 
-#ifdef KRB4_SECURITY
-    /* modification by BIS@BBN 4/25/2003:
-     * with the option processing changes in amanda 2.4.4, must change
-     * the conditional from krb4_auth to options->krb4_auth */
-    if(options->krb4_auth) {
-       if(read(KEY_PIPE, session_key, sizeof session_key) 
-          != sizeof session_key) {
-         error("ERROR [%s: could not read session key]", get_pname());
+    alevel = (level_t *)dle->levellist->data;
+    level = alevel->level;
+    dbprintf(_("  Parsed request as: program `%s'\n"), dle->program);
+    dbprintf(_("                     disk `%s'\n"), qdisk);
+    dbprintf(_("                     device `%s'\n"), qamdevice);
+    dbprintf(_("                     level %d\n"), level);
+    dbprintf(_("                     since %s\n"), dumpdate);
+    dbprintf(_("                     options `%s'\n"), stroptions);
+    dbprintf(_("                     datapath `%s'\n"),
+                           data_path_to_string(dle->data_path));
+
+    if (dle->program_is_application_api==1) {
+       /* check that the application_api exist */
+    } else {
+       for(i = 0; programs[i]; i++) {
+           if (strcmp(programs[i]->name, dle->program) == 0) {
+               break;
+           }
+       }
+       if (programs[i] == NULL) {
+           dbprintf(_("ERROR [%s: unknown program %s]\n"), get_pname(),
+                    dle->program);
+           error(_("ERROR [%s: unknown program %s]"), get_pname(),
+                 dle->program);
+           /*NOTREACHED*/
        }
+       program = programs[i];
     }
-#endif
 
     if(!interactive) {
-      data_socket = stream_server(&data_port, STREAM_BUFSIZE, -1);
-      if(data_socket < 0) {
-       error("ERROR [%s: could not create data socket: %s]",
-             get_pname(), strerror(errno));
-      }
-      mesg_socket = stream_server(&mesg_port, -1, -1);
-      if(mesg_socket < 0) {
-       error("ERROR [%s: could not create mesg socket: %s]",
-             get_pname(), strerror(errno));
-      }
+       datafd = DATA_FD_OFFSET + 0;
+       mesgfd = DATA_FD_OFFSET + 2;
+       indexfd = DATA_FD_OFFSET + 4;
     }
-    if (!interactive && options->createindex) {
-      index_socket = stream_server(&index_port, -1, -1);
-      if(index_socket < 0) {
-       error("ERROR [%s: could not create index socket: %s]",
-             get_pname(), strerror(errno));
-      }
-    } else {
-      index_port = -1;
+    if (!dle->create_index)
+       indexfd = -1;
+
+    if (dle->auth && amandad_auth) {
+       if(strcasecmp(dle->auth, amandad_auth) != 0) {
+           g_printf(_("ERROR [client configured for auth=%s while server requested '%s']\n"),
+                  amandad_auth, dle->auth);
+           exit(-1);
+       }
     }
 
-    printf("CONNECT DATA %d MESG %d INDEX %d\n",
-          data_port, mesg_port, index_port);
-    printf("OPTIONS ");
+    if (dle->kencrypt) {
+       g_printf("KENCRYPT\n");
+    }
+
+    g_printf(_("CONNECT DATA %d MESG %d INDEX %d\n"),
+          DATA_FD_OFFSET, DATA_FD_OFFSET+1,
+          indexfd == -1 ? -1 : DATA_FD_OFFSET+2);
+    g_printf(_("OPTIONS "));
     if(am_has_feature(g_options->features, fe_rep_options_features)) {
-       printf("features=%s;", our_feature_string);
+       g_printf("features=%s;", our_feature_string);
     }
     if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
-       printf("hostname=%s;", g_options->hostname);
+       g_printf("hostname=%s;", g_options->hostname);
     }
-    if(am_has_feature(g_options->features, fe_rep_options_sendbackup_options)) {
-       printf("%s", optionstr(options));
+    if (!am_has_feature(g_options->features, fe_rep_options_features) &&
+       !am_has_feature(g_options->features, fe_rep_options_hostname)) {
+       g_printf(";");
     }
-    printf("\n");
+    g_printf("\n");
     fflush(stdout);
-    freopen("/dev/null", "w", stdout);
-
-    if (options->createindex)
-      dbprintf(("%s: waiting for connect on %d, then %d, then %d\n",
-               debug_prefix_time(NULL), data_port, mesg_port, index_port));
-    else
-      dbprintf(("%s: waiting for connect on %d, then %d\n",
-               debug_prefix_time(NULL), data_port, mesg_port));
-
-    if(interactive) {
-      if((dataf = open("/dev/null", O_RDWR)) < 0) {
-       error("ERROR [%s: open of /dev/null for debug data stream: %s]",
-             get_pname(), strerror(errno));
-      }
-      mesgf = 2;
-    } else {
-      dataf = stream_accept(data_socket, TIMEOUT, -1, -1);
-      if(dataf == -1) {
-       dbprintf(("%s: timeout on data port %d\n",
-                 debug_prefix_time(NULL), data_port));
-      }
-      mesgf = stream_accept(mesg_socket, TIMEOUT, -1, -1);
-      if(mesgf == -1) {
-        dbprintf(("%s: timeout on mesg port %d\n",
-                 debug_prefix_time(NULL), mesg_port));
-      }
+    if (freopen("/dev/null", "w", stdout) == NULL) {
+       dbprintf(_("Error redirecting stdout to /dev/null: %s\n"),
+                strerror(errno));
+        exit(1);
     }
+
     if(interactive) {
-      indexf = 1;
-    } else if (options->createindex) {
-      indexf = stream_accept(index_socket, TIMEOUT, -1, -1);
-      if (indexf == -1) {
-       dbprintf(("%s: timeout on index port %d\n",
-                 debug_prefix_time(NULL), index_port));
+      if((datafd = open("/dev/null", O_RDWR)) < 0) {
+       error(_("ERROR [open of /dev/null for debug data stream: %s]\n"),
+               strerror(errno));
+       /*NOTREACHED*/
       }
+      mesgfd = 2;
+      indexfd = 1;
     }
 
     if(!interactive) {
-      if(dataf == -1 || mesgf == -1 || (options->createindex && indexf == -1)) {
+      if(datafd == -1 || mesgfd == -1 || (dle->create_index && indexfd == -1)) {
         dbclose();
         exit(1);
       }
     }
 
-    dbprintf(("%s: got all connections\n", debug_prefix_time(NULL)));
+    mesgstream = fdopen(mesgfd,"w");
+    run_client_scripts(EXECUTE_ON_PRE_DLE_BACKUP, g_options, dle, mesgstream);
+    fflush(mesgstream);
+
+    if (dle->program_is_application_api==1) {
+       guint j;
+       char *cmd=NULL;
+       GPtrArray *argv_ptr;
+       char levelstr[20];
+       backup_support_option_t *bsu;
+       char *compopt = NULL;
+       char *encryptopt = skip_argument;
+       int compout, dumpout;
+       GSList    *scriptlist;
+       script_t  *script;
+       time_t     cur_dumptime;
+       int        result;
+       GPtrArray *errarray;
+       int        errfd[2];
+       FILE      *dumperr;
+
+       /*  apply client-side encryption here */
+       if ( dle->encrypt == ENCRYPT_CUST ) {
+           encpid = pipespawn(dle->clnt_encrypt, STDIN_PIPE, 0,
+                              &compout, &datafd, &mesgfd,
+                              dle->clnt_encrypt, encryptopt, NULL);
+           dbprintf(_("encrypt: pid %ld: %s\n"), (long)encpid, dle->clnt_encrypt);
+       } else {
+           compout = datafd;
+           encpid = -1;
+       }
 
-#ifdef KRB4_SECURITY
-    if(!interactive) {
-      /* modification by BIS@BBN 4/25/2003:
-       * with the option processing changes in amanda 2.4.4, must change
-       * the conditional from krb4_auth to options->krb4_auth */
-      if (options->krb4_auth) {
-        if(kerberos_handshake(dataf, session_key) == 0) {
-           dbprintf(("%s: kerberos_handshake on data socket failed\n",
-                     debug_prefix_time(NULL)));
-           dbclose();
-           exit(1);
-        } else {
-           dbprintf(("%s: kerberos_handshake on data socket succeeded\n",
-                     debug_prefix_time(NULL)));
+       /*  now do the client-side compression */
+       if(dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
+           compopt = skip_argument;
+#if defined(COMPRESS_BEST_OPT) && defined(COMPRESS_FAST_OPT)
+           if(dle->compress == COMP_BEST) {
+               compopt = COMPRESS_BEST_OPT;
+           } else {
+               compopt = COMPRESS_FAST_OPT;
+           }
+#endif
+           comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE, 0,
+                               &dumpout, &compout, &mesgfd,
+                               COMPRESS_PATH, compopt, NULL);
+           if(compopt != skip_argument) {
+               dbprintf(_("compress pid %ld: %s %s\n"),
+                        (long)comppid, COMPRESS_PATH, compopt);
+           } else {
+               dbprintf(_("compress pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
+           }
+       } else if (dle->compress == COMP_CUST) {
+           compopt = skip_argument;
+           comppid = pipespawn(dle->compprog, STDIN_PIPE, 0,
+                               &dumpout, &compout, &mesgfd,
+                               dle->compprog, compopt, NULL);
+           if(compopt != skip_argument) {
+               dbprintf(_("pid %ld: %s %s\n"),
+                        (long)comppid, dle->compprog, compopt);
+           } else {
+               dbprintf(_("pid %ld: %s\n"), (long)comppid, dle->compprog);
+           }
+       } else {
+           dumpout = compout;
+           comppid = -1;
+       }
 
+       cur_dumptime = time(0);
+       bsu = backup_support_option(dle->program, g_options, dle->disk,
+                                   dle->device, &errarray);
+       if (!bsu) {
+           char  *errmsg;
+           char  *qerrmsg;
+           guint  i;
+           for (i=0; i < errarray->len; i++) {
+               errmsg = g_ptr_array_index(errarray, i);
+               qerrmsg = quote_string(errmsg);
+               fdprintf(mesgfd,
+                         _("sendbackup: error [Application '%s': %s]\n"),
+                         dle->program, errmsg);
+               dbprintf("aa: %s\n",qerrmsg);
+               amfree(qerrmsg);
+           }
+           if (i == 0) { /* no errarray */
+               errmsg = vstrallocf(_("Can't execute application '%s'"),
+                                   dle->program);
+               qerrmsg = quote_string(errmsg);
+               fdprintf(mesgfd, _("sendbackup: error [%s]\n"), errmsg);
+               dbprintf(_("ERROR %s\n"), qerrmsg);
+               amfree(qerrmsg);
+               amfree(errmsg);
+           }
+           return 0;
        }
 
-        if(kerberos_handshake(mesgf, session_key) == 0) {
-           dbprintf(("%s: kerberos_handshake on mesg socket failed\n",
-                     debug_prefix_time(NULL)));
-           dbclose();
+       if (pipe(errfd) < 0) {
+           char  *errmsg;
+           char  *qerrmsg;
+           errmsg = vstrallocf(_("Application '%s': can't create pipe"),
+                                   dle->program);
+           qerrmsg = quote_string(errmsg);
+           fdprintf(mesgfd, _("sendbackup: error [%s]\n"), errmsg);
+           dbprintf(_("ERROR %s\n"), qerrmsg);
+           amfree(qerrmsg);
+           amfree(errmsg);
+           return 0;
+       }
+
+       switch(application_api_pid=fork()) {
+       case 0:
+           argv_ptr = g_ptr_array_new();
+           cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
+           g_ptr_array_add(argv_ptr, stralloc(dle->program));
+           g_ptr_array_add(argv_ptr, stralloc("backup"));
+           if (bsu->message_line == 1) {
+               g_ptr_array_add(argv_ptr, stralloc("--message"));
+               g_ptr_array_add(argv_ptr, stralloc("line"));
+           }
+           if (g_options->config && bsu->config == 1) {
+               g_ptr_array_add(argv_ptr, stralloc("--config"));
+               g_ptr_array_add(argv_ptr, stralloc(g_options->config));
+           }
+           if (g_options->hostname && bsu->host == 1) {
+               g_ptr_array_add(argv_ptr, stralloc("--host"));
+               g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
+           }
+           if (dle->disk && bsu->disk == 1) {
+               g_ptr_array_add(argv_ptr, stralloc("--disk"));
+               g_ptr_array_add(argv_ptr, stralloc(dle->disk));
+           }
+           g_ptr_array_add(argv_ptr, stralloc("--device"));
+           g_ptr_array_add(argv_ptr, stralloc(dle->device));
+           if (level <= bsu->max_level) {
+               g_ptr_array_add(argv_ptr, stralloc("--level"));
+               g_snprintf(levelstr,19,"%d",level);
+               g_ptr_array_add(argv_ptr, stralloc(levelstr));
+           }
+           if (indexfd != -1 && bsu->index_line == 1) {
+               g_ptr_array_add(argv_ptr, stralloc("--index"));
+               g_ptr_array_add(argv_ptr, stralloc("line"));
+           }
+           if (dle->record && bsu->record == 1) {
+               g_ptr_array_add(argv_ptr, stralloc("--record"));
+           }
+           application_property_add_to_argv(argv_ptr, dle, bsu,
+                                            g_options->features);
+
+           for (scriptlist = dle->scriptlist; scriptlist != NULL;
+                scriptlist = scriptlist->next) {
+               script = (script_t *)scriptlist->data;
+               if (script->result && script->result->proplist) {
+                   property_add_to_argv(argv_ptr, script->result->proplist);
+               }
+           }
+
+           g_ptr_array_add(argv_ptr, NULL);
+           dbprintf(_("%s: running \"%s\n"), get_pname(), cmd);
+           for (j = 1; j < argv_ptr->len - 1; j++)
+               dbprintf(" %s\n", (char *)g_ptr_array_index(argv_ptr,j));
+           dbprintf(_("\"\n"));
+           if(dup2(dumpout, 1) == -1) {
+               error(_("Can't dup2: %s"),strerror(errno));
+               /*NOTREACHED*/
+           }
+           if (dup2(errfd[1], 2) == -1) {
+               error(_("Can't dup2: %s"),strerror(errno));
+               /*NOTREACHED*/
+           }
+           if(dup2(mesgfd, 3) == -1) {
+               error(_("Can't dup2: %s"),strerror(errno));
+               /*NOTREACHED*/
+           }
+           if(indexfd > 0) {
+               if(dup2(indexfd, 4) == -1) {
+                   error(_("Can't dup2: %s"),strerror(errno));
+                   /*NOTREACHED*/
+               }
+               fcntl(indexfd, F_SETFD, 0);
+           }
+           application_api_info_tapeheader(mesgfd, dle->program, dle);
+           if (indexfd != 0) {
+               safe_fd(3, 2);
+           } else {
+               safe_fd(3, 1);
+           }
+           execve(cmd, (char **)argv_ptr->pdata, safe_env());
            exit(1);
-        } else {
-           dbprintf(("%s: kerberos_handshake on mesg socket succeeded\n",
-                     debug_prefix_time(NULL)));
+           break;
+       default:
+           break;
+       case -1:
+           error(_("%s: fork returned: %s"), get_pname(), strerror(errno));
+       }
 
+       close(errfd[1]);
+       dumperr = fdopen(errfd[0],"r");
+       if (!dumperr) {
+           error(_("Can't fdopen: %s"), strerror(errno));
+           /*NOTREACHED*/
        }
 
-        dbprintf(("%s: kerberos handshakes succeeded!\n",
-                 debug_prefix_time(NULL)));
-      }
-    }
-#endif
+       result = 0;
+       while ((line = agets(dumperr)) != NULL) {
+           if (strlen(line) > 0) {
+               fdprintf(mesgfd, "sendbackup: error [%s]\n", line);
+               dbprintf("error: %s\n", line);
+               result = 1;
+           }
+           amfree(line);
+       }
 
-    if(!interactive) {
-      /* redirect stderr */
-      if(dup2(mesgf, 2) == -1) {
-         dbprintf(("%s: error redirecting stderr: %s\n",
-                   debug_prefix(NULL), strerror(errno)));
-         dbclose();
-         exit(1);
-      }
-    }
+       result |= check_result(mesgfd);
+       if (result == 0) {
+           char *amandates_file;
+
+           amandates_file = getconf_str(CNF_AMANDATES);
+           if(start_amandates(amandates_file, 1)) {
+               amandates_updateone(dle->disk, level, cur_dumptime);
+               finish_amandates();
+               free_amandates();
+           } else {
+               if (GPOINTER_TO_INT(dle->estimatelist->data) == ES_CALCSIZE &&
+                   bsu->calcsize) {
+                   error(_("error [opening %s for writing: %s]"),
+                         amandates_file, strerror(errno));
+               } else {
+                   g_debug(_("non-fatal error opening '%s' for writing: %s]"),
+                           amandates_file, strerror(errno));
+               }
+           }
+       }
+       amfree(bsu);
+     } else {
+       if(!interactive) {
+           /* redirect stderr */
+           if(dup2(mesgfd, 2) == -1) {
+               dbprintf(_("Error redirecting stderr to fd %d: %s\n"),
+                        mesgfd, strerror(errno));
+               dbclose();
+               exit(1);
+           }
+       }
+       if(pipe(mesgpipe) == -1) {
+           s = strerror(errno);
+           dbprintf(_("error [opening mesg pipe: %s]\n"), s);
+           error(_("error [opening mesg pipe: %s]"), s);
+       }
 
-    if(pipe(mesgpipe) == -1) {
-      error("error [opening mesg pipe: %s]", strerror(errno));
+       program->start_backup(dle, g_options->hostname,
+                             datafd, mesgpipe[1], indexfd);
+       dbprintf(_("Started backup\n"));
+       parse_backup_messages(dle, mesgpipe[0]);
+       dbprintf(_("Parsed backup messages\n"));
     }
 
-    program->start_backup(g_options->hostname, disk, amdevice, level, dumpdate,
-                         dataf, mesgpipe[1], indexf);
-    parse_backup_messages(mesgpipe[0]);
+    run_client_scripts(EXECUTE_ON_POST_DLE_BACKUP, g_options, dle, mesgstream);
+    fflush(mesgstream);
 
-    amfree(prog);
-    amfree(disk);
-    amfree(amdevice);
+    amfree(qdisk);
+    amfree(qamdevice);
     amfree(dumpdate);
     amfree(stroptions);
     amfree(our_feature_string);
     am_release_feature_set(our_features);
     our_features = NULL;
-    am_release_feature_set(g_options->features);
-    g_options->features = NULL;
-    amfree(g_options->hostname);
-    amfree(g_options->str);
-    amfree(g_options);
+    free_g_options(g_options);
 
     dbclose();
 
-    malloc_size_2 = malloc_inuse(&malloc_hist_2);
+    return 0;
 
-    if(malloc_size_1 != malloc_size_2) {
-       malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
+ err:
+    if (err_extra) {
+       g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
+       dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
+    } else {
+       g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
+       dbprintf(_("REQ packet is bogus\n"));
     }
 
-    return 0;
+    amfree(qdisk);
+    amfree(qamdevice);
+    amfree(dumpdate);
+    amfree(stroptions);
+    amfree(our_feature_string);
 
- err:
-    printf("FORMAT ERROR IN REQUEST PACKET\n");
-    dbprintf(("%s: REQ packet is bogus%s%s\n",
-             debug_prefix_time(NULL),
-             err_extra ? ": " : "",
-             err_extra ? err_extra : ""));
     dbclose();
     return 1;
 }
 
-char *childstr(pid)
-int pid;
+
 /*
  * Returns a string for a child process.  Checks the saved dump and
  * compress pids to see which it is.
  */
+
+char *
+childstr(
+    pid_t pid)
 {
     if(pid == dumppid) return program->backup_name;
     if(pid == comppid) return "compress";
-    if(pid == encpid)  return "kencrypt";
+    if(pid == encpid) return "encrypt";
     if(pid == indexpid) return "index";
+    if(pid == application_api_pid) {
+       if (!gdle) {
+           dbprintf("gdle == NULL\n");
+           return "gdle == NULL";
+       }
+       return gdle->program;
+    }
     return "unknown";
 }
 
 
-int check_status(pid, w)
-int pid;
-amwait_t w;
 /*
  * Determine if the child return status really indicates an error.
  * If so, add the error message to the error string; more than one
  * child can have an error.
  */
+
+int
+check_status(
+    pid_t      pid,
+    amwait_t   w,
+    int                mesgfd)
 {
     char *thiserr = NULL;
-    char *str;
+    char *str, *strX;
     int ret, sig, rc;
-    char number[NUM_STR_SIZE];
 
     str = childstr(pid);
 
@@ -553,59 +817,68 @@ amwait_t w;
         * but the failure is noted.
         */
        if(ret != 0) {
-           fprintf(stderr, "? %s returned %d\n", str, ret);
+           fdprintf(mesgfd, _("? index %s returned %d\n"), str, ret);
            rc = 0;
        }
-    }
-
-#ifndef HAVE_GZIP
-    if(pid == comppid) {
+       indexpid = -1;
+       strX = "index";
+    } else if(pid == comppid) {
        /*
         * compress returns 2 sometimes, but it is ok.
         */
+#ifndef HAVE_GZIP
        if(ret == 2) {
            rc = 0;
        }
-    }
 #endif
-
-#ifdef DUMP_RETURNS_1
-    if(pid == dumppid && tarpid == -1) {
+       comppid = -1;
+       strX = "compress";
+    } else if(pid == dumppid && tarpid == -1) {
         /*
         * Ultrix dump returns 1 sometimes, but it is ok.
         */
+#ifdef DUMP_RETURNS_1
         if(ret == 1) {
            rc = 0;
        }
-    }
 #endif
-
-#ifdef IGNORE_TAR_ERRORS
-    if(pid == tarpid) {
+       dumppid = -1;
+       strX = "dump";
+    } else if(pid == tarpid) {
+       if (ret == 1) {
+           rc = 0;
+       }
        /*
         * tar bitches about active filesystems, but we do not care.
         */
+#ifdef IGNORE_TAR_ERRORS
         if(ret == 2) {
            rc = 0;
        }
-    }
 #endif
+       dumppid = tarpid = -1;
+       strX = "dump";
+    } else if(pid == application_api_pid) {
+       strX = "Application";
+    } else {
+       strX = "unknown";
+    }
 
     if(rc == 0) {
        return 0;                               /* normal exit */
     }
 
     if(ret == 0) {
-       ap_snprintf(number, sizeof(number), "%d", sig);
-       thiserr = vstralloc(str, " got signal ", number, NULL);
+       thiserr = vstrallocf(_("%s (%d) %s got signal %d"), strX, (int)pid, str,
+                            sig);
     } else {
-       ap_snprintf(number, sizeof(number), "%d", ret);
-       thiserr = vstralloc(str, " returned ", number, NULL);
+       thiserr = vstrallocf(_("%s (%d) %s returned %d"), strX, (int)pid, str, ret);
     }
 
+    fdprintf(mesgfd, "? %s\n", thiserr);
+
     if(errorstr) {
-       strappend(errorstr, ", ");
-       strappend(errorstr, thiserr);
+       errorstr =  newvstrallocf(errorstr, "%s, %s", errorstr, thiserr);
        amfree(thiserr);
     } else {
        errorstr = thiserr;
@@ -615,15 +888,18 @@ amwait_t w;
 }
 
 
-/* Send header info to the message file.
-*/
-void write_tapeheader()
+/*
+ *Send header info to the message file.
+ */
+void
+info_tapeheader(
+    dle_t *dle)
 {
-    fprintf(stderr, "%s: info BACKUP=%s\n", get_pname(), program->backup_name);
+    g_fprintf(stderr, "%s: info BACKUP=%s\n", get_pname(), program->backup_name);
 
-    fprintf(stderr, "%s: info RECOVER_CMD=", get_pname());
-    if (options->compress == COMPR_FAST || options->compress == COMPR_BEST)
-       fprintf(stderr, "%s %s |", UNCOMPRESS_PATH,
+    g_fprintf(stderr, "%s: info RECOVER_CMD=", get_pname());
+    if (dle->compress == COMP_FAST || dle->compress == COMP_BEST)
+       g_fprintf(stderr, "%s %s |", UNCOMPRESS_PATH,
 #ifdef UNCOMPRESS_OPT
                UNCOMPRESS_OPT
 #else
@@ -631,33 +907,99 @@ void write_tapeheader()
 #endif
                );
 
-    fprintf(stderr, "%s -f... -\n", program->restore_name);
+    g_fprintf(stderr, "%s -xpGf - ...\n", program->restore_name);
 
-    if (options->compress == COMPR_FAST || options->compress == COMPR_BEST)
-       fprintf(stderr, "%s: info COMPRESS_SUFFIX=%s\n",
+    if (dle->compress == COMP_FAST || dle->compress == COMP_BEST)
+       g_fprintf(stderr, "%s: info COMPRESS_SUFFIX=%s\n",
                        get_pname(), COMPRESS_SUFFIX);
 
-    fprintf(stderr, "%s: info end\n", get_pname());
+    g_fprintf(stderr, "%s: info end\n", get_pname());
 }
 
-int pipefork(func, fname, stdinfd, stdoutfd, stderrfd)
-void (*func) P((void));
-char *fname;
-int *stdinfd;
-int stdoutfd, stderrfd;
+void
+application_api_info_tapeheader(
+    int       mesgfd,
+    char     *prog,
+    dle_t *dle)
 {
-    int pid, inpipe[2];
+    char line[1024];
+
+    g_snprintf(line, 1024, "%s: info BACKUP=APPLICATION\n", get_pname());
+    if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
+       dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
+       return;
+    }
 
-    dbprintf(("%s: forking function %s in pipeline\n",
-             debug_prefix_time(NULL), fname));
+    g_snprintf(line, 1024, "%s: info APPLICATION=%s\n", get_pname(), prog);
+    if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
+       dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
+       return;
+    }
+
+    g_snprintf(line, 1024, "%s: info RECOVER_CMD=", get_pname());
+    if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
+       dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
+       return;
+    }
+
+    if (dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
+       g_snprintf(line, 1024, "%s %s |", UNCOMPRESS_PATH,
+#ifdef UNCOMPRESS_OPT
+                UNCOMPRESS_OPT
+#else
+                ""
+#endif
+                );
+       if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
+           dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
+           return;
+       }
+    }
+    g_snprintf(line, 1024, "%s/%s restore [./file-to-restore]+\n",
+              APPLICATION_DIR, prog);
+    if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
+       dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
+       return;
+    }
+
+    if (dle->compress) {
+       g_snprintf(line, 1024, "%s: info COMPRESS_SUFFIX=%s\n",
+                get_pname(), COMPRESS_SUFFIX);
+       if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
+           dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
+           return;
+       }
+    }
+
+    g_snprintf(line, 1024, "%s: info end\n", get_pname());
+    if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
+       dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
+       return;
+    }
+}
+
+pid_t
+pipefork(
+    void       (*func)(void),
+    char *     fname,
+    int *      stdinfd,
+    int                stdoutfd,
+    int                stderrfd)
+{
+    int inpipe[2];
+    pid_t pid;
+
+    dbprintf(_("Forking function %s in pipeline\n"), fname);
 
     if(pipe(inpipe) == -1) {
-       error("error [open pipe to %s: %s]", fname, strerror(errno));
+       error(_("error [open pipe to %s: %s]"), fname, strerror(errno));
+       /*NOTREACHED*/
     }
 
     switch(pid = fork()) {
     case -1:
-       error("error [fork %s: %s]", fname, strerror(errno));
+       error(_("error [fork %s: %s]"), fname, strerror(errno));
+       /*NOTREACHED*/
     default:   /* parent process */
        aclose(inpipe[0]);      /* close input side of pipe */
        *stdinfd = inpipe[1];
@@ -666,30 +1008,91 @@ int stdoutfd, stderrfd;
        aclose(inpipe[1]);      /* close output side of pipe */
 
        if(dup2(inpipe[0], 0) == -1) {
-           error("error [dup2 0 %s: dup2 in: %s]", fname, strerror(errno));
+           error(_("error [fork %s: dup2(%d, in): %s]"),
+                 fname, inpipe[0], strerror(errno));
+           /*NOTRACHED*/
        }
        if(dup2(stdoutfd, 1) == -1) {
-           error("error [dup2 1 %s: dup2 out: %s]", fname, strerror(errno));
+           error(_("error [fork %s: dup2(%d, out): %s]"),
+                 fname, stdoutfd, strerror(errno));
+           /*NOTRACHED*/
        }
        if(dup2(stderrfd, 2) == -1) {
-           error("error [dup2 2 %s: dup2 err: %s]", fname, strerror(errno));
+           error(_("error [fork %s: dup2(%d, err): %s]"),
+                 fname, stderrfd, strerror(errno));
+           /*NOTRACHED*/
        }
 
        func();
        exit(0);
-       /* NOTREACHED */
+       /*NOTREACHED*/
     }
     return pid;
 }
 
-void parse_backup_messages(mesgin)
-int mesgin;
+int
+check_result(
+    int mesgfd)
 {
-    int goterror, wpid;
+    int goterror;
+    pid_t wpid;
     amwait_t retstat;
-    char *line;
 
     goterror = 0;
+
+
+    while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
+       if(check_status(wpid, retstat, mesgfd)) goterror = 1;
+    }
+
+    if (dumppid != -1) {
+       sleep(5);
+       while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
+           if(check_status(wpid, retstat, mesgfd)) goterror = 1;
+       }
+    }
+    if (dumppid != -1) {
+       dbprintf(_("Sending SIGHUP to dump process %d\n"),
+                 (int)dumppid);
+       if(dumppid != -1) {
+           if(kill(dumppid, SIGHUP) == -1) {
+               dbprintf(_("Can't send SIGHUP to %d: %s\n"),
+                         (int)dumppid,
+                         strerror(errno));
+           }
+       }
+       sleep(5);
+       while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
+           if(check_status(wpid, retstat, mesgfd)) goterror = 1;
+       }
+    }
+    if (dumppid != -1) {
+       dbprintf(_("Sending SIGKILL to dump process %d\n"),
+                 (int)dumppid);
+       if(dumppid != -1) {
+           if(kill(dumppid, SIGKILL) == -1) {
+               dbprintf(_("Can't send SIGKILL to %d: %s\n"),
+                         (int)dumppid,
+                         strerror(errno));
+           }
+       }
+       sleep(5);
+       while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
+           if(check_status(wpid, retstat, mesgfd)) goterror = 1;
+       }
+    }
+
+    return goterror;
+}
+
+void
+parse_backup_messages(
+    dle_t      *dle,
+    int                mesgin)
+{
+    int goterror;
+    char *line;
+
     amfree(errorstr);
 
     for(; (line = areads(mesgin)) != NULL; free(line)) {
@@ -697,52 +1100,32 @@ int mesgin;
     }
 
     if(errno) {
-       error("error [read mesg pipe: %s]", strerror(errno));
+       error(_("error [read mesg pipe: %s]"), strerror(errno));
+       /*NOTREACHED*/
     }
 
-    while((wpid = wait(&retstat)) != -1) {
-       if(check_status(wpid, retstat)) goterror = 1;
-    }
+    goterror = check_result(mesgfd);
 
     if(errorstr) {
-       error("error [%s]", errorstr);
+       error(_("error [%s]"), errorstr);
+       /*NOTREACHED*/
     } else if(dump_size == -1) {
-       error("error [no backup size line]");
+       error(_("error [no backup size line]"));
+       /*NOTREACHED*/
     }
 
-    program->end_backup(goterror);
+    program->end_backup(dle, goterror);
 
-    fprintf(stderr, "%s: size %ld\n", get_pname(), dump_size);
-    fprintf(stderr, "%s: end\n", get_pname());
+    fdprintf(mesgfd, _("%s: size %ld\n"), get_pname(), dump_size);
+    fdprintf(mesgfd, _("%s: end\n"), get_pname());
 }
 
 
-double first_num P((char *str));
-
-double first_num(str)
-char *str;
-/*
- * Returns the value of the first integer in a string.
- */
+static void
+process_dumpline(
+    char *     str)
 {
-    char *num;
-    int ch;
-    double d;
-
-    ch = *str++;
-    while(ch && !isdigit(ch)) ch = *str++;
-    num = str - 1;
-    while(isdigit(ch) || ch == '.') ch = *str++;
-    str[-1] = '\0';
-    d = atof(num);
-    str[-1] = ch;
-    return d;
-}
-
-static void process_dumpline(str)
-char *str;
-{
-    regex_t *rp;
+    amregex_t *rp;
     char *type;
     char startchr;
 
@@ -752,7 +1135,7 @@ char *str;
        }
     }
     if(rp->typ == DMP_SIZE) {
-       dump_size = (long)((first_num(str) * rp->scale + 1023.0)/1024.0);
+       dump_size = (long)((the_num(str, rp->field)* rp->scale+1023.0)/1024.0);
     }
     switch(rp->typ) {
     case DMP_NORMAL:
@@ -779,66 +1162,60 @@ char *str;
        startchr = '!';
        break;
     }
-    dbprintf(("%s: %3d: %7s(%c): %s\n",
-             debug_prefix_time(NULL),
+    dbprintf("%3d: %7s(%c): %s\n",
              rp->srcline,
              type,
              startchr,
-             str));
-    fprintf(stderr, "%c %s\n", startchr, str);
+             str);
+    fdprintf(mesgfd, "%c %s\n", startchr, str);
 }
 
 
-/* start_index.  Creates an index file from the output of dump/tar.
-   It arranges that input is the fd to be written by the dump process.
-   If createindex is not enabled, it does nothing.  If it is not, a
-   new process will be created that tees input both to a pipe whose
-   read fd is dup2'ed input and to a program that outputs an index
-   file to `index'.
-
-   make sure that the chat from restore doesn't go to stderr cause
-   this goes back to amanda which doesn't expect to see it
-   (2>/dev/null should do it)
-
-   Originally by Alan M. McIvor, 13 April 1996
-
-   Adapted by Alexandre Oliva, 1 May 1997
-
-   This program owes a lot to tee.c from GNU sh-utils and dumptee.c
-   from the DeeJay backup package.
-
-*/
-
-static volatile int index_finished = 0;
-
-static void index_closed(sig)
-int sig;
-{
-  index_finished = 1;
-}
+/*
+ * start_index.  Creates an index file from the output of dump/tar.
+ * It arranges that input is the fd to be written by the dump process.
+ * If createindex is not enabled, it does nothing.  If it is not, a
+ * new process will be created that tees input both to a pipe whose
+ * read fd is dup2'ed input and to a program that outputs an index
+ * file to `index'.
+ *
+ * make sure that the chat from restore doesn't go to stderr cause
+ * this goes back to amanda which doesn't expect to see it
+ * (2>/dev/null should do it)
+ *
+ * Originally by Alan M. McIvor, 13 April 1996
+ *
+ * Adapted by Alexandre Oliva, 1 May 1997
+ *
+ * This program owes a lot to tee.c from GNU sh-utils and dumptee.c
+ * from the DeeJay backup package.
+ */
 
-void save_fd(fd, min)
-int *fd, min;
+static void
+save_fd(
+    int *      fd,
+    int                min)
 {
   int origfd = *fd;
 
   while (*fd >= 0 && *fd < min) {
     int newfd = dup(*fd);
     if (newfd == -1)
-      dbprintf(("%s: unable to save file descriptor [%s]\n",
-               debug_prefix(NULL), strerror(errno)));
+      dbprintf(_("Unable to save file descriptor [%s]\n"), strerror(errno));
     *fd = newfd;
   }
   if (origfd != *fd)
-    dbprintf(("%s: dupped file descriptor %i to %i\n",
-             debug_prefix(NULL), origfd, *fd));
+    dbprintf(_("Dupped file descriptor %i to %i\n"), origfd, *fd);
 }
 
-void start_index(createindex, input, mesg, index, cmd)
-int createindex, input, mesg, index;
-char *cmd;
+void
+start_index(
+    int                createindex,
+    int                input,
+    int                mesg,
+    int                index,
+    char *     cmd)
 {
-  struct sigaction act, oact;
   int pipefd[2];
   FILE *pipe_fp;
   int exitcode;
@@ -847,17 +1224,20 @@ char *cmd;
     return;
 
   if (pipe(pipefd) != 0) {
-    error("creating index pipe: %s", strerror(errno));
+    error(_("creating index pipe: %s"), strerror(errno));
+    /*NOTREACHED*/
   }
 
   switch(indexpid = fork()) {
   case -1:
-    error("forking index tee process: %s", strerror(errno));
+    error(_("forking index tee process: %s"), strerror(errno));
+    /*NOTREACHED*/
 
   default:
     aclose(pipefd[0]);
     if (dup2(pipefd[1], input) == -1) {
-      error("dup'ping index tee output: %s", strerror(errno));
+      error(_("dup'ping index tee output: %s"), strerror(errno));
+      /*NOTREACHED*/
     }
     aclose(pipefd[1]);
     return;
@@ -875,40 +1255,31 @@ char *cmd;
   dup2(index, 1);
   dup2(mesg, 2);
   dup2(input, 3);
-  for(index = 4; index < FD_SETSIZE; index++) {
+  for(index = 4; index < (int)FD_SETSIZE; index++) {
     if (index != dbfd()) {
       close(index);
     }
   }
 
-  /* set up a signal handler for SIGPIPE for when the pipe is finished
-     creating the index file */
-  /* at that point we obviously want to stop writing to it */
-  act.sa_handler = index_closed;
-  sigemptyset(&act.sa_mask);
-  act.sa_flags = 0;
-  if (sigaction(SIGPIPE, &act, &oact) != 0) {
-    error("couldn't set index SIGPIPE handler [%s]", strerror(errno));
-  }
-
   if ((pipe_fp = popen(cmd, "w")) == NULL) {
-    error("couldn't start index creator [%s]", strerror(errno));
+    error(_("couldn't start index creator [%s]"), strerror(errno));
+    /*NOTREACHED*/
   }
 
-  dbprintf(("%s: started index creator: \"%s\"\n",
-           debug_prefix_time(NULL), cmd));
+  dbprintf(_("Started index creator: \"%s\"\n"), cmd);
   while(1) {
     char buffer[BUFSIZ], *ptr;
-    int bytes_read;
-    int bytes_written;
-    int just_written;
+    ssize_t bytes_read;
+    size_t bytes_written;
+    size_t just_written;
 
-    bytes_read = read(0, buffer, sizeof(buffer));
-    if ((bytes_read < 0) && (errno == EINTR))
-      continue;
+    do {
+       bytes_read = read(0, buffer, SIZEOF(buffer));
+    } while ((bytes_read < 0) && ((errno == EINTR) || (errno == EAGAIN)));
 
     if (bytes_read < 0) {
-      error("index tee cannot read [%s]", strerror(errno));
+      error(_("index tee cannot read [%s]"), strerror(errno));
+      /*NOTREACHED*/
     }
 
     if (bytes_read == 0)
@@ -917,36 +1288,31 @@ char *cmd;
     /* write the stuff to the subprocess */
     ptr = buffer;
     bytes_written = 0;
-    while (bytes_read > bytes_written && !index_finished) {
-      just_written = write(fileno(pipe_fp), ptr, bytes_read - bytes_written);
-      if (just_written < 0) {
-         /* the signal handler may have assigned to index_finished
-          * just as we waited for write() to complete. */
-         if (!index_finished) {
-             dbprintf(("%s: index tee cannot write to index creator [%s]\n",
-                       debug_prefix_time(NULL), strerror(errno)));
-             index_finished = 1;
-       }
-      } else {
+    just_written = full_write(fileno(pipe_fp), ptr, (size_t)bytes_read);
+    if (just_written < (size_t)bytes_read) {
+       /* 
+        * just as we waited for write() to complete.
+        */
+       if (errno != EPIPE) {
+           dbprintf(_("Index tee cannot write to index creator [%s]\n"),
+                           strerror(errno));
+       }
+    } else {
        bytes_written += just_written;
        ptr += just_written;
-      }
     }
 
     /* write the stuff to stdout, ensuring none lost when interrupt
        occurs */
     ptr = buffer;
     bytes_written = 0;
-    while (bytes_read > bytes_written) {
-      just_written = write(3, ptr, bytes_read - bytes_written);
-      if ((just_written < 0) && (errno == EINTR))
-       continue;
-      if (just_written < 0) {
-       error("index tee cannot write [%s]", strerror(errno));
-      } else {
+    just_written = full_write(3, ptr, bytes_read);
+    if (just_written < (size_t)bytes_read) {
+       error(_("index tee cannot write [%s]"), strerror(errno));
+       /*NOTREACHED*/
+    } else {
        bytes_written += just_written;
        ptr += just_written;
-      }
     }
   }
 
@@ -955,10 +1321,11 @@ char *cmd;
   /* finished */
   /* check the exit code of the pipe and moan if not 0 */
   if ((exitcode = pclose(pipe_fp)) != 0) {
-    dbprintf(("%s: index pipe returned %d\n",
-             debug_prefix_time(NULL), exitcode));
+    char *exitstr = str_exit_status("Index pipe", exitcode);
+    dbprintf("%s\n", exitstr);
+    amfree(exitstr);
   } else {
-    dbprintf(("%s: index created successfully\n", debug_prefix_time(NULL)));
+    dbprintf(_("Index created successfully\n"));
   }
   pipe_fp = NULL;
 
@@ -970,7 +1337,3 @@ extern backup_program_t dump_program, gnutar_program;
 backup_program_t *programs[] = {
   &dump_program, &gnutar_program, NULL
 };
-
-#ifdef KRB4_SECURITY
-#include "sendbackup-krb4.c"
-#endif