Imported Upstream version 2.6.1
[debian/amanda] / client-src / sendbackup.c
index 9f26dafe2e2d3ccf02db171a2da62cc57d227b12..6da1326a86a7a28ff4941f98af9c25cc3662a17a 100644 (file)
@@ -39,6 +39,7 @@
 #include "getfsent.h"
 #include "version.h"
 #include "conffile.h"
+#include "amandates.h"
 
 #define sendbackup_debug(i, ...) do {  \
        if ((i) <= debug_sendbackup) {  \
@@ -53,18 +54,19 @@ 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 datafd;
 int mesgfd;
 int indexfd;
 
-option_t *options;
 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;
@@ -72,110 +74,38 @@ static char *amandad_auth = NULL;
 
 /* local functions */
 int main(int argc, char **argv);
-char *optionstr(option_t *options);
 char *childstr(pid_t pid);
-int check_status(pid_t pid, amwait_t w);
+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);
-void parse_backup_messages(int mesgin);
+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 backup_api_info_tapeheader(int mesgfd, char *prog, option_t *options);
+void application_api_info_tapeheader(int mesgfd, char *prog, dle_t *dle);
 
-double the_num(char *str, int pos);
+int fdprintf(int fd, char *format, ...) G_GNUC_PRINTF(2, 3);
 
-
-char *
-optionstr(
-    option_t * options)
+int
+fdprintf(
+    int   fd,
+    char *format,
+    ...)
 {
-    static char *optstr = NULL;
-    char *compress_opt;
-    char *encrypt_opt;
-    char *decrypt_opt;
-    char *record_opt = "";
-    char *index_opt = "";
-    char *auth_opt;
-    char *exclude_file_opt;
-    char *exclude_list_opt;
-    char *exc = NULL;
-    sle_t *excl;
-
-    if(options->compress == COMP_BEST)
-       compress_opt = stralloc("compress-best;");
-    else if(options->compress == COMP_FAST)
-       compress_opt = stralloc("compress-fast;");
-    else if(options->compress == COMP_SERVER_BEST)
-       compress_opt = stralloc("srvcomp-best;");
-    else if(options->compress == COMP_SERVER_FAST)
-       compress_opt = stralloc("srvcomp-fast;");
-    else if(options->compress == COMP_SERVER_CUST)
-       compress_opt = vstralloc("srvcomp-cust=", options->srvcompprog, ";", NULL);
-    else if(options->compress == COMP_CUST)
-       compress_opt = vstralloc("comp-cust=", options->clntcompprog, ";", NULL);
-    else
-       compress_opt = stralloc("");
-    
-    if(options->encrypt == ENCRYPT_CUST) {
-      encrypt_opt = vstralloc("encrypt-cust=", options->clnt_encrypt, ";", NULL);
-      if (options->clnt_decrypt_opt)
-       decrypt_opt = vstralloc("client-decrypt-option=", options->clnt_decrypt_opt, ";", NULL);
-      else
-       decrypt_opt = stralloc("");
-    }
-    else if(options->encrypt == ENCRYPT_SERV_CUST) {
-      encrypt_opt = vstralloc("encrypt-serv-cust=", options->srv_encrypt, ";", NULL);
-      if(options->srv_decrypt_opt)
-       decrypt_opt = vstralloc("server-decrypt-option=", options->srv_decrypt_opt, ";", NULL);
-      else
-       decrypt_opt = stralloc("");
-    }
-    else {
-       encrypt_opt = stralloc("");
-       decrypt_opt = stralloc("");
-    }
+    va_list  argp;
+    char    *s;
+    int      r;
 
-    if(options->no_record) record_opt = "no-record;";
-    if(options->auth) auth_opt = vstralloc("auth=", options->auth, ";", NULL);
-       else auth_opt = stralloc("");
-    if(options->createindex) index_opt = "index;";
+    arglist_start(argp, format);
+    s = g_strdup_vprintf(format, argp);
+    arglist_end(argp);
 
-    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);
-       }
-    }
-    amfree(exc);
-    optstr = newvstralloc(optstr,
-                         compress_opt,
-                         encrypt_opt,
-                         decrypt_opt,
-                         record_opt,
-                         index_opt,
-                         auth_opt,
-                         exclude_file_opt,
-                         exclude_list_opt,
-                         NULL);
-    amfree(compress_opt);
-    amfree(encrypt_opt);
-    amfree(decrypt_opt);
-    amfree(auth_opt);
-    amfree(exclude_file_opt);
-    amfree(exclude_list_opt);
-    return optstr;
+    r = full_write(fd, s, strlen(s));
+    amfree(s);
+    return r;
 }
 
-
 int
 main(
     int                argc,
@@ -184,22 +114,19 @@ main(
     int interactive = 0;
     int level = 0;
     int mesgpipe[2];
-    char *prog, *dumpdate, *stroptions;
-    int program_is_backup_api;
-    char *disk = NULL;
+    dle_t *dle = NULL;
+    char *dumpdate, *stroptions;
     char *qdisk = NULL;
-    char *amdevice = NULL;
     char *qamdevice = NULL;
     char *line = NULL;
     char *err_extra = NULL;
     char *s;
     int i;
     int ch;
-    FILE *toolin;
-    int status;
+    GSList *errlist;
+    FILE   *mesgstream;
 
     /* initialize */
-
     /*
      * Configure program for internationalization:
      *   1) Only set the message locale for now.
@@ -242,6 +169,7 @@ main(
     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);
 
@@ -256,13 +184,9 @@ main(
        fflush(stderr);
     }
 
-    prog = NULL;
-    disk = NULL;
     qdisk = NULL;
-    amdevice = NULL;
     dumpdate = NULL;
     stroptions = NULL;
-    program_is_backup_api=0;
 
     for(; (line = agets(stdin)) != NULL; free(line)) {
        if (line[0] == '\0')
@@ -284,17 +208,31 @@ main(
                config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
                            g_options->config);
 
-               dbrename(config_name, DBG_SUBDIR_CLIENT);
+               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) {
+       if (dle && dle->program != NULL) {
            err_extra = _("multiple requests");
            goto err;
        }
 
        dbprintf(_("  sendbackup req: <%s>\n"), line);
+       dle = alloc_dle();
+
        s = line;
        ch = *s++;
 
@@ -303,21 +241,21 @@ main(
            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';
 
-       if(strcmp(prog,"BACKUP")==0) {
-           program_is_backup_api=1;
-           skip_whitespace(s, ch);             /* find dumper name */
-           if (ch == '\0') {
-               goto err;                       /* no program */
-           }
-           prog = 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') {
@@ -325,14 +263,13 @@ main(
            goto err;                           /* no disk name */
        }
 
-       amfree(disk);
        amfree(qdisk);
        qdisk = s - 1;
        ch = *qdisk;
        skip_quoted_string(s, ch);
        s[-1] = '\0';
        qdisk = stralloc(qdisk);
-       disk = unquote_string(qdisk);
+       dle->disk = unquote_string(qdisk);
 
        skip_whitespace(s, ch);                 /* find the device or level */
        if (ch == '\0') {
@@ -341,18 +278,17 @@ main(
        }
 
        if(!isdigit((int)s[-1])) {
-           amfree(amdevice);
            amfree(qamdevice);
            qamdevice = s - 1;
            ch = *qamdevice;
            skip_quoted_string(s, ch);
            s[-1] = '\0';
            qamdevice = stralloc(qamdevice);
-           amdevice = unquote_string(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 */
@@ -361,6 +297,7 @@ main(
            goto err;                           /* bad level */
        }
        skip_integer(s, ch);
+       dle->level = g_slist_append(dle->level, GINT_TO_POINTER(level));
 
        skip_whitespace(s, ch);                 /* find the dump date */
        if(ch == '\0') {
@@ -392,63 +329,95 @@ main(
     }
     amfree(line);
     if (g_options == NULL) {
-       printf(_("ERROR [Missing OPTIONS line in sendbackup input]\n"));
+       g_printf(_("ERROR [Missing OPTIONS line in sendbackup input]\n"));
        error(_("Missing OPTIONS line in sendbackup input\n"));
        /*NOTREACHED*/
     }
 
-    if (prog       == NULL ||
-       disk       == NULL ||
-       amdevice   == NULL ||
-       dumpdate   == NULL ||
-       stroptions == NULL) {
+    if (am_has_feature(g_options->features, fe_req_xml)) {
+       char *errmsg = NULL;
+
+       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);
+    }
+    gdle = dle;
+
+    if (dle->program == NULL ||
+       dle->disk    == NULL ||
+       dle->device  == NULL ||
+       dle->level   == NULL ||
+       dumpdate     == NULL) {
        err_extra = _("no valid sendbackup request");
        goto err;
     }
-       
-    dbprintf(_("  Parsed request as: program `%s'\n"), prog);
+
+    if (g_slist_length(dle->level) != 1) {
+       err_extra = _("Too many level");
+       goto err;
+    }
+
+    level = GPOINTER_TO_INT(dle->level->data);
+    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);
 
-    if(program_is_backup_api==1) {
-       /* check that the backup_api exist */
-    }
-    else {
+    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, prog) == 0) {
+           if (strcmp(programs[i]->name, dle->program) == 0) {
                break;
            }
        }
        if (programs[i] == NULL) {
-           dbprintf(_("ERROR [%s: unknown program %s]\n"), get_pname(), prog);
-           error(_("ERROR [%s: unknown program %s]"), get_pname(), prog);
+           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];
     }
 
-    options = parse_options(stroptions, disk, amdevice, g_options->features, 0);
-
     if(!interactive) {
        datafd = DATA_FD_OFFSET + 0;
        mesgfd = DATA_FD_OFFSET + 2;
        indexfd = DATA_FD_OFFSET + 4;
     }
-    if (!options->createindex)
+    if (!dle->create_index)
        indexfd = -1;
 
-    if(options->auth && amandad_auth) {
-       if(strcasecmp(options->auth, amandad_auth) != 0) {
+    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, options->auth);
+                  amandad_auth, dle->auth);
            exit(-1);
        }
     }
 
-    if (options->kencrypt) {
+    if (dle->kencrypt) {
        g_printf("KENCRYPT\n");
     }
 
@@ -462,9 +431,6 @@ main(
     if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
        g_printf("hostname=%s;", g_options->hostname);
     }
-    if(am_has_feature(g_options->features, fe_rep_options_sendbackup_options)) {
-       g_printf("%s", optionstr(options));
-    }
     g_printf("\n");
     fflush(stdout);
     if (freopen("/dev/null", "w", stdout) == NULL) {
@@ -484,56 +450,135 @@ main(
     }
 
     if(!interactive) {
-      if(datafd == -1 || mesgfd == -1 || (options->createindex && indexfd == -1)) {
+      if(datafd == -1 || mesgfd == -1 || (dle->create_index && indexfd == -1)) {
         dbclose();
         exit(1);
       }
     }
 
-    if(program_is_backup_api==1) {
-       pid_t backup_api_pid;
-       int i, j;
+    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) {
+       int i, j, k;
        char *cmd=NULL;
-       char *argvchild[20];
+       char **argvchild;
        char levelstr[20];
-       int property_pipe[2];
        backup_support_option_t *bsu;
-
-       if (pipe(property_pipe) < 0) {
-           error(_("Can't create pipe: %s"),strerror(errno));
-           /*NOTREACHED*/
+       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;
        }
-       bsu = backup_support_option(prog, g_options, disk, amdevice);
 
-       switch(backup_api_pid=fork()) {
-       case 0:
-           aclose(property_pipe[1]);
-           if(dup2(property_pipe[0], 0) == -1) {
-               error(_("Can't dup2: %s"),strerror(errno));
-               /*NOTREACHED*/
+       /*  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;
            }
-           if(dup2(datafd, 1) == -1) {
-               error(_("Can't dup2: %s"),strerror(errno));
-               /*NOTREACHED*/
+#endif
+           comppid = pipespawn(COMPRESS_PATH, STDIN_PIPE, 0,
+                               &dumpout, &compout, &mesgfd,
+                               COMPRESS_PATH, compopt, NULL);
+           dbprintf(_("gnutar: pid %ld: %s"), (long)comppid, COMPRESS_PATH);
+           if(compopt != skip_argument) {
+               dbprintf(_("pid %ld: %s %s\n"),
+                        (long)comppid, COMPRESS_PATH, compopt);
+           } else {
+               dbprintf(_("pid %ld: %s\n"), (long)comppid, COMPRESS_PATH);
            }
-           if(dup2(mesgfd, 2) == -1) {
-               error(_("Can't dup2: %s"),strerror(errno));
-               /*NOTREACHED*/
+       } 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);
            }
-           if(indexfd != 0) {
-               if(dup2(indexfd, 3) == -1) {
-                   error(_("Can't dup2: %s"),strerror(errno));
-                   /*NOTREACHED*/
+       } 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 (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:
+           cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
+           k = application_property_argv_size(dle);
+           for (scriptlist = dle->scriptlist; scriptlist != NULL;
+                scriptlist = scriptlist->next) {
+               script = (script_t *)scriptlist->data;
+               if (script->result && script->result->proplist) {
+                   k += property_argv_size(script->result->proplist);
                }
-               fcntl(indexfd, F_SETFD, 0);
-               fcntl(3, F_SETFD, 0);
-               safe_fd(3, 1);
-           } else {
-               safe_fd(-1, 0);
            }
-           cmd = vstralloc(DUMPER_DIR, "/", prog, NULL);
+           argvchild = g_new0(char *, 20 + k);
            i=0;
-           argvchild[i++] = prog;
+           argvchild[i++] = dle->program;
            argvchild[i++] = "backup";
            if (bsu->message_line == 1) {
                argvchild[i++] = "--message";
@@ -547,59 +592,110 @@ main(
                argvchild[i++] = "--host";
                argvchild[i++] = g_options->hostname;
            }
-           if (disk && bsu->disk == 1) {
+           if (dle->disk && bsu->disk == 1) {
                argvchild[i++] = "--disk";
-               argvchild[i++] = disk;
+               argvchild[i++] = dle->disk;
            }
            argvchild[i++] = "--device";
-           argvchild[i++] = amdevice;
+           argvchild[i++] = dle->device;
            if (level <= bsu->max_level) {
                argvchild[i++] = "--level";
                g_snprintf(levelstr,19,"%d",level);
                argvchild[i++] = levelstr;
            }
-           if (indexfd != 0 && bsu->index_line == 1) {
+           if (indexfd != -1 && bsu->index_line == 1) {
                argvchild[i++] = "--index";
                argvchild[i++] = "line";
            }
-           if (!options->no_record && bsu->record == 1) {
+           if (dle->record && bsu->record == 1) {
                argvchild[i++] = "--record";
            }
+           i += application_property_add_to_argv(&argvchild[i], dle, bsu);
+
+           for (scriptlist = dle->scriptlist; scriptlist != NULL;
+                scriptlist = scriptlist->next) {
+               script = (script_t *)scriptlist->data;
+               if (script->result && script->result->proplist) {
+                   i += property_add_to_argv(&argvchild[i],
+                                             script->result->proplist);
+               }
+           }
+
            argvchild[i] = NULL;
-           dbprintf(_("%s: running \"%s"), get_pname(), cmd);
-           for(j=1;j<i;j++) dbprintf(" %s",argvchild[j]);
+           dbprintf(_("%s: running \"%s\n"), get_pname(), cmd);
+           for(j=1;j<i;j++) dbprintf(" %s\n",argvchild[j]);
            dbprintf(_("\"\n"));
-           backup_api_info_tapeheader(mesgfd, prog, options);
+           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, argvchild, safe_env());
            exit(1);
            break;
  
        default:
-           aclose(property_pipe[0]);
-           toolin = fdopen(property_pipe[1],"w");
-           if (!toolin) {
-               error(_("Can't fdopen: %s"), strerror(errno));
-               /*NOTREACHED*/
-           }
-           output_tool_property(toolin, options);
-           fflush(toolin);
-           fclose(toolin);
            break;
        case -1:
            error(_("%s: fork returned: %s"), get_pname(), strerror(errno));
        }
-       amfree(bsu);
-       if (waitpid(backup_api_pid, &status, 0) < 0) {
-           if (!WIFEXITED(status)) {
-               dbprintf(_("Tool exited with signal %d"), WTERMSIG(status));
-           } else if (WEXITSTATUS(status) != 0) {
-               dbprintf(_("Tool exited with status %d"), WEXITSTATUS(status));
+
+       close(errfd[1]);
+       dumperr = fdopen(errfd[0],"r");
+       if (!dumperr) {
+           error(_("Can't fdopen: %s"), strerror(errno));
+           /*NOTREACHED*/
+       }
+
+       while ((line = agets(dumperr)) != NULL) {
+           if (strlen(line) > 0) {
+               fdprintf(mesgfd, "sendbackup: error [%s]\n", line);
+               dbprintf("error: %s\n", line);
+           }
+           amfree(line);
+       }
+
+       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 {
-               dbprintf(_("waitpid returned negative value"));
+               if (dle->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));
+               }
            }
        }
-     }
-    else {
+       amfree(bsu);
+     } else {
        if(!interactive) {
            /* redirect stderr */
            if(dup2(mesgfd, 2) == -1) {
@@ -616,17 +712,17 @@ main(
            error(_("error [opening mesg pipe: %s]"), s);
        }
 
-       program->start_backup(g_options->hostname, disk, amdevice, level,
-                             dumpdate, datafd, mesgpipe[1], indexfd);
+       program->start_backup(dle, g_options->hostname,
+                             datafd, mesgpipe[1], indexfd);
        dbprintf(_("Started backup\n"));
-       parse_backup_messages(mesgpipe[0]);
+       parse_backup_messages(dle, mesgpipe[0]);
        dbprintf(_("Parsed backup messages\n"));
     }
 
-    amfree(prog);
-    amfree(disk);
+    run_client_scripts(EXECUTE_ON_POST_DLE_BACKUP, g_options, dle, mesgstream);
+    fflush(mesgstream);
+
     amfree(qdisk);
-    amfree(amdevice);
     amfree(qamdevice);
     amfree(dumpdate);
     amfree(stroptions);
@@ -640,10 +736,20 @@ main(
     return 0;
 
  err:
-    g_printf(_("FORMAT ERROR IN REQUEST PACKET\n"));
-    dbprintf(_("REQ packet is bogus%s%s\n"),
-             err_extra ? ": " : "",
-             err_extra ? err_extra : "");
+    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"));
+    }
+
+    amfree(qdisk);
+    amfree(qamdevice);
+    amfree(dumpdate);
+    amfree(stroptions);
+    amfree(our_feature_string);
+
     dbclose();
     return 1;
 }
@@ -662,6 +768,13 @@ childstr(
     if(pid == comppid) return "compress";
     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";
 }
 
@@ -675,7 +788,8 @@ childstr(
 int
 check_status(
     pid_t      pid,
-    amwait_t   w)
+    amwait_t   w,
+    int                mesgfd)
 {
     char *thiserr = NULL;
     char *str, *strX;
@@ -698,11 +812,11 @@ check_status(
         * but the failure is noted.
         */
        if(ret != 0) {
-           g_fprintf(stderr, _("? index %s returned %d\n"), str, ret);
+           fdprintf(mesgfd, _("? index %s returned %d\n"), str, ret);
            rc = 0;
        }
        indexpid = -1;
-       strX = "index ";
+       strX = "index";
     } else if(pid == comppid) {
        /*
         * compress returns 2 sometimes, but it is ok.
@@ -713,7 +827,7 @@ check_status(
        }
 #endif
        comppid = -1;
-       strX = "compress ";
+       strX = "compress";
     } else if(pid == dumppid && tarpid == -1) {
         /*
         * Ultrix dump returns 1 sometimes, but it is ok.
@@ -724,7 +838,7 @@ check_status(
        }
 #endif
        dumppid = -1;
-       strX = "dump ";
+       strX = "dump";
     } else if(pid == tarpid) {
        if (ret == 1) {
            rc = 0;
@@ -738,9 +852,11 @@ check_status(
        }
 #endif
        dumppid = tarpid = -1;
-       strX = "dump ";
+       strX = "dump";
+    } else if(pid == application_api_pid) {
+       strX = "Application";
     } else {
-       strX = "unknown ";
+       strX = "unknown";
     }
 
     if(rc == 0) {
@@ -754,6 +870,8 @@ check_status(
        thiserr = vstrallocf(_("%s (%d) %s returned %d"), strX, (int)pid, str, ret);
     }
 
+    fdprintf(mesgfd, "? %s\n", thiserr);
+
     if(errorstr) {
        errorstr =  newvstrallocf(errorstr, "%s, %s", errorstr, thiserr);
        amfree(thiserr);
@@ -769,12 +887,13 @@ check_status(
  *Send header info to the message file.
  */
 void
-info_tapeheader(void)
+info_tapeheader(
+    dle_t *dle)
 {
     g_fprintf(stderr, "%s: info BACKUP=%s\n", get_pname(), program->backup_name);
 
     g_fprintf(stderr, "%s: info RECOVER_CMD=", get_pname());
-    if (options->compress == COMP_FAST || options->compress == COMP_BEST)
+    if (dle->compress == COMP_FAST || dle->compress == COMP_BEST)
        g_fprintf(stderr, "%s %s |", UNCOMPRESS_PATH,
 #ifdef UNCOMPRESS_OPT
                UNCOMPRESS_OPT
@@ -785,7 +904,7 @@ info_tapeheader(void)
 
     g_fprintf(stderr, "%s -xpGf - ...\n", program->restore_name);
 
-    if (options->compress == COMP_FAST || options->compress == COMP_BEST)
+    if (dle->compress == COMP_FAST || dle->compress == COMP_BEST)
        g_fprintf(stderr, "%s: info COMPRESS_SUFFIX=%s\n",
                        get_pname(), COMPRESS_SUFFIX);
 
@@ -793,32 +912,32 @@ info_tapeheader(void)
 }
 
 void
-backup_api_info_tapeheader(
+application_api_info_tapeheader(
     int       mesgfd,
     char     *prog,
-    option_t *options)
+    dle_t *dle)
 {
     char line[1024];
 
-    g_snprintf(line, 1024, "%s: info BACKUP=DUMPER\n", get_pname());
-    if (fullwrite(mesgfd, line, strlen(line)) != (ssize_t)strlen(line)) {
+    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;
     }
 
-    g_snprintf(line, 1024, "%s: info DUMPER=%s\n", get_pname(), prog);
-    if (fullwrite(mesgfd, line, strlen(line)) != (ssize_t)strlen(line)) {
+    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 (fullwrite(mesgfd, line, strlen(line)) != (ssize_t)strlen(line)) {
+    if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
        dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
        return;
     }
 
-    if (options->compress) {
+    if (dle->compress == COMP_FAST || dle->compress == COMP_BEST) {
        g_snprintf(line, 1024, "%s %s |", UNCOMPRESS_PATH,
 #ifdef UNCOMPRESS_OPT
                 UNCOMPRESS_OPT
@@ -826,28 +945,29 @@ backup_api_info_tapeheader(
                 ""
 #endif
                 );
-       if (fullwrite(mesgfd, line, strlen(line)) != (ssize_t)strlen(line)) {
+       if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
            dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
            return;
        }
     }
-    g_snprintf(line, 1024, "%s -f... -\n", prog);
-    if (fullwrite(mesgfd, line, strlen(line)) != (ssize_t)strlen(line)) {
+    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 (options->compress) {
+    if (dle->compress) {
        g_snprintf(line, 1024, "%s: info COMPRESS_SUFFIX=%s\n",
                 get_pname(), COMPRESS_SUFFIX);
-       if (fullwrite(mesgfd, line, strlen(line)) != (ssize_t)strlen(line)) {
+       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 (fullwrite(mesgfd, line, strlen(line)) != (ssize_t)strlen(line)) {
+    if (full_write(mesgfd, line, strlen(line)) != strlen(line)) {
        dbprintf(_("error writing to mesgfd socket: %s"), strerror(errno));
        return;
     }
@@ -905,35 +1025,25 @@ pipefork(
     return pid;
 }
 
-void
-parse_backup_messages(
-    int                mesgin)
+int
+check_result(
+    int mesgfd)
 {
     int goterror;
     pid_t wpid;
     amwait_t retstat;
-    char *line;
 
     goterror = 0;
-    amfree(errorstr);
-
-    for(; (line = areads(mesgin)) != NULL; free(line)) {
-       process_dumpline(line);
-    }
 
-    if(errno) {
-       error(_("error [read mesg pipe: %s]"), strerror(errno));
-       /*NOTREACHED*/
-    }
 
     while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
-       if(check_status(wpid, retstat)) goterror = 1;
+       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)) goterror = 1;
+           if(check_status(wpid, retstat, mesgfd)) goterror = 1;
        }
     }
     if (dumppid != -1) {
@@ -948,7 +1058,7 @@ parse_backup_messages(
        }
        sleep(5);
        while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
-           if(check_status(wpid, retstat)) goterror = 1;
+           if(check_status(wpid, retstat, mesgfd)) goterror = 1;
        }
     }
     if (dumppid != -1) {
@@ -963,10 +1073,34 @@ parse_backup_messages(
        }
        sleep(5);
        while((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
-           if(check_status(wpid, retstat)) goterror = 1;
+           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)) {
+       process_dumpline(line);
+    }
+
+    if(errno) {
+       error(_("error [read mesg pipe: %s]"), strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    goterror = check_result(mesgfd);
+
     if(errorstr) {
        error(_("error [%s]"), errorstr);
        /*NOTREACHED*/
@@ -975,39 +1109,10 @@ parse_backup_messages(
        /*NOTREACHED*/
     }
 
-    program->end_backup(goterror);
+    program->end_backup(dle, goterror);
 
-    g_fprintf(stderr, _("%s: size %ld\n"), get_pname(), dump_size);
-    g_fprintf(stderr, _("%s: end\n"), get_pname());
-}
-
-
-/*
- * Returns the value of the first integer in a string.
- */
-
-double
-the_num(
-    char *     str,
-    int         pos)
-{
-    char *num;
-    int ch;
-    double d;
-
-    do {
-       ch = *str++;
-       while(ch && !isdigit(ch)) ch = *str++;
-       if (pos == 1) break;
-       pos--;
-       while(ch && (isdigit(ch) || ch == '.')) ch = *str++;
-    } while (ch);
-    num = str - 1;
-    while(isdigit(ch) || ch == '.') ch = *str++;
-    str[-1] = '\0';
-    d = atof(num);
-    str[-1] = (char)ch;
-    return d;
+    fdprintf(mesgfd, _("%s: size %ld\n"), get_pname(), dump_size);
+    fdprintf(mesgfd, _("%s: end\n"), get_pname());
 }
 
 
@@ -1057,7 +1162,7 @@ process_dumpline(
              type,
              startchr,
              str);
-    g_fprintf(stderr, "%c %s\n", startchr, str);
+    fdprintf(mesgfd, "%c %s\n", startchr, str);
 }
 
 
@@ -1161,7 +1266,7 @@ start_index(
     char buffer[BUFSIZ], *ptr;
     ssize_t bytes_read;
     size_t bytes_written;
-    ssize_t just_written;
+    size_t just_written;
 
     do {
        bytes_read = read(0, buffer, SIZEOF(buffer));
@@ -1178,8 +1283,8 @@ start_index(
     /* write the stuff to the subprocess */
     ptr = buffer;
     bytes_written = 0;
-    just_written = fullwrite(fileno(pipe_fp), ptr, (size_t)bytes_read);
-    if (just_written < 0) {
+    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.
         */
@@ -1196,8 +1301,8 @@ start_index(
        occurs */
     ptr = buffer;
     bytes_written = 0;
-    just_written = fullwrite(3, ptr, (size_t)bytes_read);
-    if (just_written < 0) {
+    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 {