Imported Upstream version 3.2.0
[debian/amanda] / client-src / sendsize.c
index b7bf68815c8a3298806f372738a169d05eacd1ad..8eb5c046c56afe7ca5e13589afefbadf3445b137 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /* 
- * $Id: sendsize.c,v 1.97.2.13.4.6.2.23.2.3 2005/04/06 12:31:04 martinea Exp $
+ * $Id: sendsize.c 10421 2008-03-06 18:48:30Z martineau $
  *
  * send estimated backup sizes using dump
  */
 
 #include "amanda.h"
+#include "match.h"
 #include "pipespawn.h"
 #include "amfeatures.h"
 #include "amandates.h"
 #include "clock.h"
 #include "util.h"
 #include "getfsent.h"
-#include "version.h"
 #include "client_util.h"
+#include "conffile.h"
 
 #ifdef SAMBA_CLIENT
 #include "findpass.h"
 #endif
 
+#define sendsize_debug(i, ...) do {    \
+       if ((i) <= debug_sebdsize) {    \
+           dbprintf(__VA_ARGS__);      \
+       }                               \
+} while (0)
+
 #ifdef HAVE_SETPGID
 #  define SETPGRP      setpgid(getpid(), getpid())
 #  define SETPGRP_FAILED() do {                                                \
-    dbprintf(("setpgid(%ld,%ld) failed: %s\n",                         \
-             (long)getpid(), (long)getpid(), strerror(errno)));        \
+    dbprintf(_("setpgid(%ld,%ld) failed: %s\n"),                               \
+             (long)getpid(), (long)getpid(), strerror(errno)); \
 } while(0)
 
 #else /* () line 0 */
 #if defined(SETPGRP_VOID)
 #  define SETPGRP      setpgrp()
 #  define SETPGRP_FAILED() do {                                                \
-    dbprintf(("setpgrp() failed: %s\n", strerror(errno)));             \
+    dbprintf(_("setpgrp() failed: %s\n"), strerror(errno));            \
 } while(0)
 
 #else
 #  define SETPGRP      setpgrp(0, getpid())
 #  define SETPGRP_FAILED() do {                                                \
-    dbprintf(("setpgrp(0,%ld) failed: %s\n",                           \
-             (long)getpid(), strerror(errno)));                        \
+    dbprintf(_("setpgrp(0,%ld) failed: %s\n"),                         \
+             (long)getpid(), strerror(errno));                 \
 } while(0)
 
 #endif
@@ -71,19 +78,18 @@ typedef struct level_estimates_s {
     time_t dumpsince;
     int estsize;
     int needestimate;
+    int server;                /* server can do estimate */
 } level_estimate_t;
 
 typedef struct disk_estimates_s {
     struct disk_estimates_s *next;
-    char *amname;
-    char *amdevice;
+    char *qamname;
+    char *qamdevice;
     char *dirname;
-    char *program;
-    char *calcprog;
-    int spindle;
+    char *qdirname;
     pid_t child;
     int done;
-    option_t *options;
+    dle_t *dle;
     level_estimate_t est[DUMP_LEVELS];
 } disk_estimates_t;
 
@@ -92,26 +98,27 @@ disk_estimates_t *est_list;
 static am_feature_t *our_features = NULL;
 static char *our_feature_string = NULL;
 static g_option_t *g_options = NULL;
+static gboolean amandates_started = FALSE;
 
 /* local functions */
-int main P((int argc, char **argv));
-void add_diskest P((char *disk, char *amdevice, int level, int spindle,
-                   char *prog, char *calcprog, option_t *options));
-void calc_estimates P((disk_estimates_t *est));
-void free_estimates P((disk_estimates_t *est));
-void dump_calc_estimates P((disk_estimates_t *));
-void smbtar_calc_estimates P((disk_estimates_t *));
-void gnutar_calc_estimates P((disk_estimates_t *));
-void generic_calc_estimates P((disk_estimates_t *));
-
-
-int main(argc, argv)
-int argc;
-char **argv;
+int main(int argc, char **argv);
+void dle_add_diskest(dle_t *dle);
+void calc_estimates(disk_estimates_t *est);
+void free_estimates(disk_estimates_t *est);
+void dump_calc_estimates(disk_estimates_t *);
+void star_calc_estimates(disk_estimates_t *);
+void smbtar_calc_estimates(disk_estimates_t *);
+void gnutar_calc_estimates(disk_estimates_t *);
+void application_api_calc_estimate(disk_estimates_t *);
+void generic_calc_estimates(disk_estimates_t *);
+
+int
+main(
+    int                argc,
+    char **    argv)
 {
-    int level, spindle;
-    char *prog, *calcprog, *disk, *amdevice, *dumpdate;
-    option_t *options = NULL;
+    int level;
+    char *dumpdate;
     disk_estimates_t *est;
     disk_estimates_t *est1;
     disk_estimates_t *est_prev;
@@ -119,50 +126,59 @@ char **argv;
     char *s, *fp;
     int ch;
     char *err_extra = NULL;
-    int fd;
-    unsigned long malloc_hist_1, malloc_size_1;
-    unsigned long malloc_hist_2, malloc_size_2;
     int done;
     int need_wait;
     int dumpsrunning;
+    char *qdisk = NULL;
+    char *qlist = NULL;
+    char *qamdevice = NULL;
+    dle_t *dle;
+    GSList *errlist;
+    level_t *alevel;
 
+    (void)argc;        /* Quiet unused parameter warning */
+    (void)argv;        /* Quiet unused parameter warning */
 
     /* 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).
-        */
-       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(-1, 0);
+    openbsd_fd_inform();
     safe_cd();
 
     set_pname("sendsize");
 
-    malloc_size_1 = malloc_inuse(&malloc_hist_1);
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
 
-    erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
-    dbopen();
+    add_amanda_log_handler(amanda_log_stderr);
+    add_amanda_log_handler(amanda_log_syslog);
+    dbopen(DBG_SUBDIR_CLIENT);
     startclock();
-    dbprintf(("%s: version %s\n", get_pname(), version()));
+    dbprintf(_("version %s\n"), VERSION);
 
     our_features = am_init_feature_set();
     our_feature_string = am_feature_to_string(our_features);
 
-    set_debug_prefix_pid(getpid());
+    config_init(CONFIG_INIT_CLIENT, NULL);
+    /* (check for config errors comes later) */
 
-    /* handle all service requests */
+    check_running_as(RUNNING_AS_CLIENT_LOGIN);
 
-    start_amandates(0);
+    /* handle all service requests */
 
     for(; (line = agets(stdin)) != NULL; free(line)) {
-#define sc "OPTIONS "
-       if(strncmp(line, sc, sizeof(sc)-1) == 0) {
-#undef sc
+       if (line[0] == '\0')
+           continue;
+       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);
@@ -170,139 +186,237 @@ char **argv;
                g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
            }
 
-           printf("OPTIONS ");
+           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_maxdumps)) {
-               printf("maxdumps=%d;", g_options->maxdumps);
+               g_printf("maxdumps=%d;", g_options->maxdumps);
            }
            if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
-               printf("hostname=%s;", g_options->hostname);
+               g_printf("hostname=%s;", g_options->hostname);
            }
-           printf("\n");
+           g_printf("\n");
            fflush(stdout);
+
+           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;
        }
 
+       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 = stralloc(_("no program name"));
            goto err;                           /* no program name */
        }
-       prog = s - 1;
+       dle->program = s - 1;
        skip_non_whitespace(s, ch);
        s[-1] = '\0';
 
-       if(strncmp(prog, "CALCSIZE", 8) == 0) {
+       dle->program_is_application_api=0;
+       if(strncmp_const(dle->program, "CALCSIZE") == 0) {
            skip_whitespace(s, ch);             /* find the program name */
            if(ch == '\0') {
-               err_extra = "no program name";
+               err_extra = stralloc(_("no program name"));
                goto err;
            }
-           calcprog = s - 1;
+           dle->estimatelist = g_slist_append(dle->estimatelist,
+                                              GINT_TO_POINTER(ES_CALCSIZE));
+           dle->program = s - 1;
            skip_non_whitespace(s, ch);
            s[-1] = '\0';
+           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';
+           }
        }
        else {
-           calcprog = NULL;
+           dle->estimatelist = g_slist_append(dle->estimatelist,
+                                              GINT_TO_POINTER(ES_CLIENT));
+           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 = stralloc(_("no disk name"));
            goto err;                           /* no disk name */
        }
-       disk = s - 1;
-       skip_non_whitespace(s, ch);
-       s[-1] = '\0';
+
+       if (qdisk != NULL)
+           amfree(qdisk);
+
+       fp = s - 1;
+       skip_quoted_string(s, ch);
+       s[-1] = '\0';                           /* terminate the disk name */
+       qdisk = stralloc(fp);
+       dle->disk = unquote_string(qdisk);
 
        skip_whitespace(s, ch);                 /* find the device or level */
        if (ch == '\0') {
-           err_extra = "bad level";
+           err_extra = stralloc(_("bad level"));
            goto err;
        }
        if(!isdigit((int)s[-1])) {
            fp = s - 1;
-           skip_non_whitespace(s, ch);
+           skip_quoted_string(s, ch);
            s[-1] = '\0';
-           amdevice = stralloc(fp);
+           qamdevice = stralloc(fp);
+           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 = stralloc(_("bad level"));
            goto err;                           /* bad level */
        }
+       if (level < 0 || level >= DUMP_LEVELS) {
+           err_extra = stralloc(_("bad level"));
+           goto err;
+       }
        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 = stralloc(_("no dumpdate"));
            goto err;                           /* no dumpdate */
        }
        dumpdate = s - 1;
        skip_non_whitespace(s, ch);
        s[-1] = '\0';
+       (void)dumpdate;                         /* XXX: Set but not used */
 
-       spindle = 0;                            /* default spindle */
+       dle->spindle = 0;                       /* default spindle */
 
        skip_whitespace(s, ch);                 /* find the spindle */
        if(ch != '\0') {
-           if(sscanf(s - 1, "%d", &spindle) != 1) {
-               err_extra = "bad spindle";
+           if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
+               err_extra = stralloc(_("bad spindle"));
                goto err;                       /* bad spindle */
            }
            skip_integer(s, ch);
 
-           skip_whitespace(s, ch);             /* find the exclusion list */
+           skip_whitespace(s, ch);             /* find the parameters */
            if(ch != '\0') {
-               if(strncmp(s-1, "OPTIONS |;",10) == 0) {
-                   options = parse_options(s + 8,
-                                           disk,
-                                           amdevice,
-                                           g_options->features,
-                                           0);
+               if(strncmp_const(s-1, "OPTIONS |;") == 0) {
+                   parse_options(s + 8,
+                                 dle,
+                                 g_options->features,
+                                 0);
                }
                else {
-                   options = alloc(sizeof(option_t));
-                   init_options(options);
-                   if(strncmp(s-1, "exclude-file=", 13) == 0) {
-                       options->exclude_file =
-                               append_sl(options->exclude_file, s+12);
-                   }
-                   if(strncmp(s-1, "exclude-list=", 13) == 0) {
-                       options->exclude_list =
-                               append_sl(options->exclude_list, s+12);
-                   }
-
-                   skip_non_whitespace(s, ch);
-                   if(ch) {
-                       err_extra = "extra text at end";
-                       goto err;               /* should have gotten to end */
+                   while (ch != '\0') {
+                       if(strncmp_const(s-1, "exclude-file=") == 0) {
+                           qlist = unquote_string(s+12);
+                           dle->exclude_file =
+                               append_sl(dle->exclude_file, qlist);
+                           amfree(qlist);
+                       } else if(strncmp_const(s-1, "exclude-list=") == 0) {
+                           qlist = unquote_string(s+12);
+                           dle->exclude_list =
+                               append_sl(dle->exclude_list, qlist);
+                           amfree(qlist);
+                       } else if(strncmp_const(s-1, "include-file=") == 0) {
+                           qlist = unquote_string(s+12);
+                           dle->include_file =
+                               append_sl(dle->include_file, qlist);
+                           amfree(qlist);
+                       } else if(strncmp_const(s-1, "include-list=") == 0) {
+                           qlist = unquote_string(s+12);
+                           dle->include_list =
+                               append_sl(dle->include_list, qlist);
+                           amfree(qlist);
+                       } else {
+                           err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
+                           goto err;           /* should have gotten to end */
+                       }
+                       skip_quoted_string(s, ch);
+                       skip_whitespace(s, ch); /* find the inclusion list */
+                       amfree(qlist);
                    }
                }
            }
-           else {
-               options = alloc(sizeof(option_t));
-               init_options(options);
-           }
        }
 
-       add_diskest(disk, amdevice, level, spindle, prog, calcprog, options);
-       amfree(amdevice);
+       /*@ignore@*/
+       dle_add_diskest(dle);
+       /*@end@*/
+    }
+    if (g_options == NULL) {
+       g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
+       error(_("Missing OPTIONS line in sendsize input\n"));
+       /*NOTREACHED*/
     }
     amfree(line);
 
-    finish_amandates();
-    free_amandates();
+    if (am_has_feature(g_options->features, fe_req_xml)) {
+       char    *errmsg = NULL;
+       dle_t   *dles, *dle;
+
+       dles = amxml_parse_node_FILE(stdin, &errmsg);
+       if (errmsg) {
+           err_extra = errmsg;
+           goto err;
+       }
+       for (dle = dles; dle != NULL; dle = dle->next) {
+           dle_add_diskest(dle);
+       }
+    }
+
+    if (amandates_started) {
+       finish_amandates();
+       free_amandates();
+       amandates_started = FALSE;
+    }
+
+    for(est = est_list; est != NULL; est = est->next) {
+       run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, g_options, est->dle,
+                          stdout);
+    }
 
     dumpsrunning = 0;
     need_wait = 0;
@@ -316,30 +430,24 @@ char **argv;
        if(need_wait) {
            pid_t child_pid;
            amwait_t child_status;
-           int exit_code;
 
            need_wait = 0;
-           dbprintf(("%s: waiting for any estimate child: %d running\n",
-                     debug_prefix_time(NULL), dumpsrunning));
+           dbprintf(_("waiting for any estimate child: %d running\n"),
+                     dumpsrunning);
            child_pid = wait(&child_status);
            if(child_pid == -1) {
-               error("wait failed: %s", strerror(errno));
+               error(_("wait failed: %s"), strerror(errno));
+               /*NOTREACHED*/
            }
-           if(WIFSIGNALED(child_status)) {
-               dbprintf(("%s: child %ld terminated with signal %d\n",
-                         debug_prefix_time(NULL),
-                         (long) child_pid, WTERMSIG(child_status)));
-           } else {
-               exit_code = WEXITSTATUS(child_status);
-               if(exit_code == 0) {
-                   dbprintf(("%s: child %ld terminated normally\n",
-                             debug_prefix_time(NULL), (long) child_pid));
-               } else {
-                   dbprintf(("%s: child %ld terminated with code %d\n",
-                             debug_prefix_time(NULL),
-                             (long) child_pid, exit_code));
-               }
+
+           if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
+               char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
+               char *child_status_str = str_exit_status(child_name, child_status);
+               dbprintf("%s\n", child_status_str);
+               amfree(child_status_str);
+               amfree(child_name);
            }
+
            /*
             * Find the child and mark it done.
             */
@@ -349,12 +457,13 @@ char **argv;
                }
            }
            if(est == NULL) {
-               dbprintf(("%s: unexpected child %ld\n",
-                         debug_prefix_time(NULL), (long)child_pid));
+               dbprintf(_("unexpected child %ld\n"), (long)child_pid);
            } else {
                est->done = 1;
                est->child = 0;
                dumpsrunning--;
+               run_client_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
+                                  est->dle, stdout);
            }
        }
        /*
@@ -379,7 +488,7 @@ char **argv;
            /*
             * Make sure there is no spindle conflict.
             */
-           if(est->spindle != -1) {
+           if(est->dle->spindle != -1) {
                for(est1 = est_list; est1 != NULL; est1 = est1->next) {
                    if(est1->child == 0 || est == est1 || est1->done) {
                        /*
@@ -388,7 +497,7 @@ char **argv;
                         */
                        continue;
                    }
-                   if(est1->spindle == est->spindle) {
+                   if(est1->dle->spindle == est->dle->spindle) {
                        break;                  /* oops -- they match */
                    }
                }
@@ -404,17 +513,25 @@ char **argv;
            }
        } else {
            done = 0;
+           run_client_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
+                              est->dle, stdout);
+
            if((est->child = fork()) == 0) {
-               set_debug_prefix_pid(getpid());
                calc_estimates(est);            /* child does the estimate */
                exit(0);
            } else if(est->child == -1) {
-               error("calc_estimates fork failed: %s", strerror(errno));
+               error(_("calc_estimates fork failed: %s"), strerror(errno));
+               /*NOTREACHED*/
            }
            dumpsrunning++;                     /* parent */
        }
     }
 
+    for(est = est_list; est != NULL; est = est->next) {
+       run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, g_options, est->dle,
+                          stdout);
+    }
+
     est_prev = NULL;
     for(est = est_list; est != NULL; est = est->next) {
        free_estimates(est);
@@ -425,104 +542,159 @@ char **argv;
     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->str);
-    amfree(g_options->hostname);
-    amfree(g_options);
-
-    malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
-    if(malloc_size_1 != malloc_size_2) {
-#if defined(USE_DBMALLOC)
-       malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
-#endif
-    }
+    free_g_options(g_options);
 
     dbclose();
     return 0;
  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 : ""));
+    if (err_extra) {
+       g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
+       dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
+       amfree(err_extra);
+    } else {
+       g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
+       dbprintf(_("REQ packet is bogus\n"));
+    }
+
+    free_g_options(g_options);
+
     dbclose();
     return 1;
 }
 
 
-void add_diskest(disk, amdevice, level, spindle, prog, calcprog, options)
-char *disk, *amdevice, *prog, *calcprog;
-int level, spindle;
-option_t *options;
+void
+dle_add_diskest(
+    dle_t    *dle)
 {
     disk_estimates_t *newp, *curp;
     amandates_t *amdp;
     int dumplev, estlev;
     time_t dumpdate;
+    levellist_t levellist;
+    char *amandates_file;
+    gboolean need_amandates = FALSE;
+    estimatelist_t el;
+
+    if (dle->levellist == NULL) {
+       g_printf(_("ERROR Missing level in request\n"));
+       return;
+    }
+
+    /* should we use amandates for this? */
+    for (el = dle->estimatelist; el != NULL; el=el->next) {
+       estimate_t estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+       if (estimate == ES_CALCSIZE)
+           need_amandates = TRUE;
+    }
+
+    if (strcmp(dle->program, "GNUTAR") == 0) {
+       /* GNUTAR only needs amandates if gnutar_list_dir is NULL */
+       char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
+       if (!gnutar_list_dir || !*gnutar_list_dir)
+           need_amandates = TRUE;
+    }
+
+    /* start amandates here, before adding this DLE to est_list, in case
+     * we encounter an error. */
+    if (need_amandates) {
+       if (!amandates_started) {
+           amandates_file = getconf_str(CNF_AMANDATES);
+           if(!start_amandates(amandates_file, 0)) {
+               char *errstr = strerror(errno);
+               char *qamname = quote_string(dle->disk);
+               char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
+               char *qerrmsg = quote_string(errmsg);
+               g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
+               amfree(qamname);
+               amfree(errmsg);
+               amfree(qerrmsg);
+               return;
+           }
+           amandates_started = TRUE;
+       }
+    }
+
+    levellist = dle->levellist;
+    while (levellist != NULL) {
+       level_t *alevel = (level_t *)levellist->data;
+       if (alevel->level < 0)
+           alevel->level = 0;
+       if (alevel->level >= DUMP_LEVELS)
+           alevel->level = DUMP_LEVELS - 1;
+       levellist = g_slist_next(levellist);
+    }
 
     for(curp = est_list; curp != NULL; curp = curp->next) {
-       if(strcmp(curp->amname, disk) == 0) {
+       if(strcmp(curp->dle->disk, dle->disk) == 0) {
            /* already have disk info, just note the level request */
-           curp->est[level].needestimate = 1;
-           if(options) {
-               free_sl(options->exclude_file);
-               free_sl(options->exclude_list);
-               free_sl(options->include_file);
-               free_sl(options->include_list);
-               amfree(options->str);
-               amfree(options);
+           levellist = dle->levellist;
+           while (levellist != NULL) {
+               level_t *alevel = (level_t *)levellist->data;
+               int      level  = alevel->level;
+               curp->est[level].needestimate = 1;
+               curp->est[level].server = alevel->server;
+               levellist = g_slist_next(levellist);
            }
+
            return;
        }
     }
 
-    newp = (disk_estimates_t *) alloc(sizeof(disk_estimates_t));
-    memset(newp, 0, sizeof(*newp));
+    newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
+    memset(newp, 0, SIZEOF(*newp));
     newp->next = est_list;
     est_list = newp;
-    newp->amname = stralloc(disk);
-    newp->amdevice = stralloc(amdevice);
-    newp->dirname = amname_to_dirname(newp->amdevice);
-    newp->program = stralloc(prog);
-    if(calcprog != NULL)
-       newp->calcprog = stralloc(calcprog);
-    else
-       newp->calcprog = NULL;
-    newp->spindle = spindle;
-    newp->est[level].needestimate = 1;
-    newp->options = options;
+    newp->qamname = quote_string(dle->disk);
+    if (dle->device) {
+       newp->qamdevice = quote_string(dle->device);
+       newp->dirname = amname_to_dirname(dle->device);
+       newp->qdirname = quote_string(newp->dirname);
+    } else {
+       newp->qamdevice = stralloc("");
+       newp->dirname = stralloc("");
+       newp->qdirname = stralloc("");
+    }
+    levellist = dle->levellist;
+    while (levellist != NULL) {
+       level_t *alevel = (level_t *)levellist->data;
+       newp->est[alevel->level].needestimate = 1;
+       newp->est[alevel->level].server = alevel->server;
+       levellist = g_slist_next(levellist);
+    }
+    newp->dle = dle;
 
     /* fill in dump-since dates */
-
-    amdp = amandates_lookup(newp->amname);
-
-    newp->est[0].dumpsince = EPOCH;
-    for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
-       dumpdate = amdp->dates[dumplev];
-       for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
-           if(dumpdate > newp->est[estlev].dumpsince)
-               newp->est[estlev].dumpsince = dumpdate;
+    if (need_amandates) {
+       amdp = amandates_lookup(newp->dle->disk);
+
+       newp->est[0].dumpsince = EPOCH;
+       for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
+           dumpdate = amdp->dates[dumplev];
+           for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
+               if(dumpdate > newp->est[estlev].dumpsince)
+                   newp->est[estlev].dumpsince = dumpdate;
+           }
+       }
+    } else {
+       /* just zero everything out */
+       for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
+           newp->est[dumplev].dumpsince = 0;
        }
     }
 }
 
 
-void free_estimates(est)
-disk_estimates_t *est;
+void
+free_estimates(
+    disk_estimates_t * est)
 {
-    amfree(est->amname);
-    amfree(est->amdevice);
+    amfree(est->qamname);
+    amfree(est->qamdevice);
     amfree(est->dirname);
-    amfree(est->program);
-    if(est->options) {
-       free_sl(est->options->exclude_file);
-       free_sl(est->options->exclude_list);
-       free_sl(est->options->include_file);
-       free_sl(est->options->include_list);
-       amfree(est->options->str);
-       amfree(est->options);
+    amfree(est->qdirname);
+    if (est->dle) {
+       free_dle(est->dle);
     }
 }
 
@@ -531,184 +703,475 @@ disk_estimates_t *est;
  *
  */
 
-void calc_estimates(est)
-disk_estimates_t *est;
+void
+calc_estimates(
+    disk_estimates_t * est)
 {
-    dbprintf(("%s: calculating for amname '%s', dirname '%s', spindle %d\n",
-             debug_prefix_time(NULL),
-             est->amname, est->dirname, est->spindle));
+    dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
+            est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
+
+    if(est->dle->program_is_application_api ==  1)
+       application_api_calc_estimate(est);
+    else {
+       estimatelist_t el;
+       estimate_t     estimate;
+       int            level;
+       estimate_t     estimate_method = ES_ES;
+       estimate_t     client_method = ES_ES;
+
+       /* find estimate method to use */
+       for (el = est->dle->estimatelist; el != NULL; el = el->next) {
+           estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+           if (estimate == ES_SERVER) {
+               if (estimate_method == ES_ES)
+                   estimate_method = ES_SERVER;
+           }
+           if (estimate == ES_CLIENT || 
+               (estimate == ES_CALCSIZE &&
+                (est->dle->device[0] != '/' || est->dle->device[1] != '/'))) {
+               if (client_method == ES_ES)
+                   client_method = estimate;
+               if (estimate_method == ES_ES)
+                   estimate_method = estimate;
+           }
+       }
+
+       /* do server estimate */
+       if (estimate_method == ES_SERVER) {
+           for (level = 0; level < DUMP_LEVELS; level++) {
+               if (est->est[level].needestimate) {
+                   if (est->est[level].server || client_method == ES_ES) {
+                       g_printf(_("%s %d SIZE -1\n"), est->qamname, level);
+                       est->est[level].needestimate = 0;
+                   }
+               }
+           }
+       }
 
+       if (client_method == ES_ES && estimate_method != ES_SERVER) {
+           g_printf(_("%s %d SIZE -2\n"), est->qamname, 0);
+           dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
+                    est->qamname, est->qdirname);
+       } else if (client_method == ES_CALCSIZE) {
+           generic_calc_estimates(est);
+       } else if (client_method == ES_CLIENT) {
 #ifndef USE_GENERIC_CALCSIZE
-    if(strcmp(est->program, "DUMP") == 0)
-       dump_calc_estimates(est);
-    else
+           if (strcmp(est->dle->program, "DUMP") == 0)
+               dump_calc_estimates(est);
+           else
 #endif
 #ifdef SAMBA_CLIENT
-      if (strcmp(est->program, "GNUTAR") == 0 &&
-         est->amdevice[0] == '/' && est->amdevice[1] == '/')
-       smbtar_calc_estimates(est);
-      else
+           if (strcmp(est->dle->program, "GNUTAR") == 0 &&
+               est->dle->device[0] == '/' && est->dle->device[1] == '/')
+               smbtar_calc_estimates(est);
+           else
 #endif
 #ifdef GNUTAR
-       if (strcmp(est->program, "GNUTAR") == 0)
-         gnutar_calc_estimates(est);
-       else
-#endif
-#ifdef SAMBA_CLIENT
-         if (est->amdevice[0] == '/' && est->amdevice[1] == '/')
-           dbprintf(("%s: Can't use CALCSIZE for samba estimate: %s %s\n",
-                     est->amname, est->dirname));
-         else
+           if (strcmp(est->dle->program, "GNUTAR") == 0)
+               gnutar_calc_estimates(est);
+           else
 #endif
-           generic_calc_estimates(est);
+               dbprintf(_("Invalid program: %s %s %s\n"),
+                        est->qamname, est->qdirname, est->dle->program);
+       }
+    }
+
+    dbprintf(_("done with amname %s dirname %s spindle %d\n"),
+             est->qamname, est->qdirname, est->dle->spindle);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ */
+
+/* local functions */
+off_t getsize_dump(dle_t *dle, int level, char **errmsg);
+off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
+off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
+off_t getsize_application_api(disk_estimates_t *est, int nb_level,
+                             int *levels, backup_support_option_t *bsu);
+off_t handle_dumpline(char *str);
+double first_num(char *str);
+
+void
+application_api_calc_estimate(
+    disk_estimates_t * est)
+{
+    int    level;
+    int    i;
+    int    levels[DUMP_LEVELS];
+    int    nb_level = 0;
+    backup_support_option_t *bsu;
+    GPtrArray               *errarray;
+    estimatelist_t el;
+    estimate_t     estimate;
+    estimate_t     estimate_method = ES_ES;
+    estimate_t     client_method = ES_ES;
+    int            has_calcsize = 0;
+    int            has_client = 0;
+
+    bsu = backup_support_option(est->dle->program, g_options, est->dle->disk,
+                               est->dle->device, &errarray);
+    if (!bsu) {
+       guint  i;
+       for (i=0; i < errarray->len; i++) {
+           char  *line;
+           char  *errmsg;
+           char  *qerrmsg;
+           line = g_ptr_array_index(errarray, i);
+           if(am_has_feature(g_options->features,
+                             fe_rep_sendsize_quoted_error)) {
+               errmsg = g_strdup_printf(_("Application '%s': %s"),
+                                    est->dle->program, line);
+               qerrmsg = quote_string(errmsg);
+               for (level = 0; level < DUMP_LEVELS; level++) {
+                   if (est->est[level].needestimate) {
+                       g_printf(_("%s %d ERROR %s\n"),
+                                est->dle->disk, level, qerrmsg);
+                       dbprintf(_("%s %d ERROR %s\n"),
+                                est->qamname, level, qerrmsg);
+                   }
+               }
+               amfree(errmsg);
+               amfree(qerrmsg);
+           }
+       }
+       if (i == 0) { /* nothing in errarray */
+           char  *errmsg;
+           char  *qerrmsg;
+           errmsg = g_strdup_printf(
+               _("Application '%s': cannon execute support command"),
+               est->dle->program);
+           qerrmsg = quote_string(errmsg);
+           for (level = 0; level < DUMP_LEVELS; level++) {
+               if (est->est[level].needestimate) {
+                   g_printf(_("%s %d ERROR %s\n"),
+                            est->dle->disk, level, qerrmsg);
+                   dbprintf(_("%s %d ERROR %s\n"),
+                            est->qamname, level, qerrmsg);
+               }
+           }
+           amfree(errmsg);
+           amfree(qerrmsg);
+       }
+       for (level = 0; level < DUMP_LEVELS; level++) {
+           est->est[level].needestimate = 0;
+       }
+       g_ptr_array_free(errarray, TRUE);
+    }
+
+    if (est->dle->data_path == DATA_PATH_AMANDA &&
+       (bsu->data_path_set & DATA_PATH_AMANDA)==0) {
+       g_printf("%s %d ERROR application %s doesn't support amanda data-path\n", est->qamname, 0, est->dle->program);
+       amfree(bsu);
+       return;
+    }
+    if (est->dle->data_path == DATA_PATH_DIRECTTCP &&
+       (bsu->data_path_set & DATA_PATH_DIRECTTCP)==0) {
+       g_printf("%s %d ERROR application %s doesn't support directtcp data-path\n", est->qamname, 0, est->dle->program);
+       amfree(bsu);
+       return;
+    }
+
+    /* find estimate method to use */
+    for (el = est->dle->estimatelist; el != NULL; el = el->next) {
+       estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+       if (estimate == ES_CLIENT)
+           has_client = 1;
+       if (estimate == ES_CALCSIZE)
+           has_calcsize = 1;
+       if (estimate == ES_SERVER) {
+           if (estimate_method == ES_ES)
+               estimate_method = ES_SERVER;
+       }
+       if ((estimate == ES_CLIENT && bsu->client_estimate) || 
+           (estimate == ES_CALCSIZE && bsu->calcsize)) {
+           if (client_method == ES_ES)
+               client_method = estimate;
+           if (estimate_method == ES_ES)
+               estimate_method = estimate;
+       }
+    }
+
+    for(level = 0; level < DUMP_LEVELS; level++) {
+       if (est->est[level].needestimate) {
+           if (level > bsu->max_level) {
+               /* planner will not even consider this level */
+               g_printf("%s %d SIZE %lld\n", est->qamname, level,
+                        (long long)-2);
+               est->est[level].needestimate = 0;
+               dbprintf(_("Application '%s' can't estimate level %d\n"),
+                         est->dle->program, level);
+           } else if (estimate_method == ES_ES) {
+               g_printf("%s %d SIZE %lld\n", est->qamname, level,
+                        (long long)-2);
+               est->est[level].needestimate = 0;
+               if (am_has_feature(g_options->features,
+                                  fe_rep_sendsize_quoted_error)) {
+                   char *errmsg, *qerrmsg;
+                   if (has_client && !bsu->client_estimate &&
+                       has_calcsize && !bsu->calcsize) {
+                       errmsg = vstrallocf(_("Application '%s' can't do CLIENT or CALCSIZE estimate"),
+                                           est->dle->program);
+                   } else if (has_client && !bsu->client_estimate) {
+                       errmsg = vstrallocf(_("Application '%s' can't do CLIENT estimate"),
+                                           est->dle->program);
+                   } else if (has_calcsize && !bsu->calcsize) {
+                       errmsg = vstrallocf(_("Application '%s' can't do CALCSIZE estimate"),
+                                           est->dle->program);
+                   } else {
+                       errmsg = vstrallocf(_("Application '%s' can't do estimate"),
+                                           est->dle->program);
+                   }
+                   qerrmsg = quote_string(errmsg);
+                   dbprintf(_("errmsg is %s\n"), errmsg);
+                   g_printf("%s %d ERROR %s\n",
+                            est->qamname, 0, qerrmsg);
+                   amfree(errmsg);
+                   amfree(qerrmsg);
+               }
+           } else if (estimate_method == ES_SERVER &&
+                      (est->est[level].server || client_method == ES_ES)) {
+               /* planner will consider this level, */
+               /* but use a server-side estimate    */
+               g_printf("%s %d SIZE -1\n", est->qamname, level);
+               est->est[level].needestimate = 0;
+           } else if (client_method == ES_CLIENT) {
+               levels[nb_level++] = level;
+           } else if (client_method == ES_CALCSIZE) {
+               levels[nb_level++] = level;
+           }
+       }
+    }
+
+    if (nb_level == 0) {
+       amfree(bsu);
+       return;
+    }
+
+    if (bsu->multi_estimate) {
+       for (i=0;i<nb_level;i++) {
+           dbprintf(_("getting size via application API for %s %s level %d\n"),
+                    est->qamname, est->qamdevice, levels[i]);
+       }
+       getsize_application_api(est, nb_level, levels, bsu);
+
+    } else {
+       for(level = 0; level < DUMP_LEVELS; level++) {
+           if (est->est[level].needestimate) {
+               dbprintf(
+                   _("getting size via application API for %s %s level %d\n"),
+                   est->qamname, est->qamdevice, level);
+               levels[0] = level;
+               getsize_application_api(est, 1, levels, bsu);
+           }
+       }
+    }
 
-    dbprintf(("%s: done with amname '%s', dirname '%s', spindle %d\n",
-             debug_prefix_time(NULL),
-             est->amname, est->dirname, est->spindle));
+    amfree(bsu);
 }
 
-void generic_calc_estimates(est)
-disk_estimates_t *est;
+
+void
+generic_calc_estimates(
+    disk_estimates_t * est)
 {
     int pipefd = -1, nullfd = -1;
     char *cmd;
-    char *my_argv[DUMP_LEVELS*2+20];
+    char *cmdline;
+    char *command;
+    GPtrArray *argv_ptr = g_ptr_array_new();
     char number[NUM_STR_SIZE];
-    int i, level, my_argc, calcpid;
+    unsigned int i;
+    int level;
+    pid_t calcpid;
     int nb_exclude = 0;
     int nb_include = 0;
     char *file_exclude = NULL;
     char *file_include = NULL;
     times_t start_time;
     FILE *dumpout = NULL;
-    long size = 1;
+    off_t size = (off_t)1;
     char *line = NULL;
     char *match_expr;
+    amwait_t wait_status;
+    char *errmsg = NULL, *qerrmsg;
+    char tmppath[PATH_MAX];
+    int len;
 
-    cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
-
-    my_argc = 0;
-    my_argv[my_argc++] = stralloc("calcsize");
-    my_argv[my_argc++] = stralloc(est->calcprog);
-
-    my_argv[my_argc++] = stralloc(est->amname);
-    my_argv[my_argc++] = stralloc(est->dirname);
-
-
-    if(est->options->exclude_file)
-       nb_exclude += est->options->exclude_file->nb_element;
-    if(est->options->exclude_list)
-       nb_exclude += est->options->exclude_list->nb_element;
-    if(est->options->include_file)
-       nb_include += est->options->include_file->nb_element;
-    if(est->options->include_list)
-       nb_include += est->options->include_list->nb_element;
+    cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
 
-    if(nb_exclude > 0)
-       file_exclude = build_exclude(est->amname, est->amdevice,est->options,0);
-    if(nb_include > 0)
-       file_include = build_include(est->amname, est->amdevice,est->options,0);
+    g_ptr_array_add(argv_ptr, stralloc("calcsize"));
+    if (g_options->config)
+       g_ptr_array_add(argv_ptr, stralloc(g_options->config));
+    else
+       g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
+
+    g_ptr_array_add(argv_ptr, stralloc(est->dle->program));
+    canonicalize_pathname(est->dle->disk, tmppath);
+    g_ptr_array_add(argv_ptr, stralloc(tmppath));
+    canonicalize_pathname(est->dirname, tmppath);
+    g_ptr_array_add(argv_ptr, stralloc(tmppath));
+
+    if (est->dle->exclude_file)
+       nb_exclude += est->dle->exclude_file->nb_element;
+    if (est->dle->exclude_list)
+       nb_exclude += est->dle->exclude_list->nb_element;
+    if (est->dle->include_file)
+       nb_include += est->dle->include_file->nb_element;
+    if (est->dle->include_list)
+       nb_include += est->dle->include_list->nb_element;
+
+    if (nb_exclude > 0)
+       file_exclude = build_exclude(est->dle, 0);
+    if (nb_include > 0)
+       file_include = build_include(est->dle, 0);
 
     if(file_exclude) {
-       my_argv[my_argc++] = stralloc("-X");
-       my_argv[my_argc++] = file_exclude;
+       g_ptr_array_add(argv_ptr, stralloc("-X"));
+       g_ptr_array_add(argv_ptr, stralloc(file_exclude));
     }
 
     if(file_include) {
-       my_argv[my_argc++] = stralloc("-I");
-       my_argv[my_argc++] = file_include;
+       g_ptr_array_add(argv_ptr, stralloc("-I"));
+       g_ptr_array_add(argv_ptr, stralloc(file_include));
     }
-
     start_time = curclock();
 
-    dbprintf(("%s: running cmd: %s", debug_prefix_time(NULL), my_argv[0]));
-    for(i=0; i<my_argc; ++i)
-       dbprintf((" %s", my_argv[i]));
+    command = (char *)g_ptr_array_index(argv_ptr, 0);
+    cmdline = stralloc(command);
+    for(i = 1; i < argv_ptr->len-1; i++) {
+       cmdline = vstrextend(&cmdline, " ",
+                            (char *)g_ptr_array_index(argv_ptr, i), NULL);
+    }
+    dbprintf(_("running: \"%s\"\n"), cmdline);
+    amfree(cmdline);
 
     for(level = 0; level < DUMP_LEVELS; level++) {
        if(est->est[level].needestimate) {
-           ap_snprintf(number, sizeof(number), "%d", level);
-           my_argv[my_argc++] = stralloc(number); 
-           dbprintf((" %s", number));
-           ap_snprintf(number, sizeof(number),
+           g_snprintf(number, SIZEOF(number), "%d", level);
+           g_ptr_array_add(argv_ptr, stralloc(number));
+           dbprintf(" %s", number);
+           g_snprintf(number, SIZEOF(number),
                        "%ld", (long)est->est[level].dumpsince);
-           my_argv[my_argc++] = stralloc(number); 
-           dbprintf((" %s", number));
+           g_ptr_array_add(argv_ptr, stralloc(number));
+           dbprintf(" %s", number);
        }
     }
-    my_argv[my_argc] = NULL;
-    dbprintf(("\n"));
+    g_ptr_array_add(argv_ptr, NULL);
+    dbprintf("\n");
 
     fflush(stderr); fflush(stdout);
 
-    nullfd = open("/dev/null", O_RDWR);
-    calcpid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
+    if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
+       errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
+                           strerror(errno));
+       dbprintf("%s\n", errmsg);
+       goto common_exit;
+    }
+
+    calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
+                        &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
     amfree(cmd);
 
     dumpout = fdopen(pipefd,"r");
-    match_expr = vstralloc(est->amname," %d SIZE %ld", NULL);
-    for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
-       if(sscanf(line, match_expr, &level, &size) == 2) {
-           printf("%s\n", line); /* write to amandad */
-           dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
-                     debug_prefix(NULL),
-                     est->amname,
+    if (!dumpout) {
+       error(_("Can't fdopen: %s"), strerror(errno));
+       /*NOTREACHED*/
+    }
+    match_expr = vstralloc(" %d SIZE %lld", NULL);
+    len = strlen(est->qamname);
+    for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
+       long long size_ = (long long)0;
+       if (line[0] == '\0' || (int)strlen(line) <= len)
+           continue;
+       /* Don't use sscanf for est->qamname because it can have a '%'. */
+       if (strncmp(line, est->qamname, len) == 0 &&
+           sscanf(line+len, match_expr, &level, &size_) == 2) {
+           g_printf("%s\n", line); /* write to amandad */
+           dbprintf(_("estimate size for %s level %d: %lld KB\n"),
+                     est->qamname,
                      level,
-                     size));
+                     size_);
        }
+       size = (off_t)size_;
     }
     amfree(match_expr);
 
-    dbprintf(("%s: waiting for %s \"%s\" child\n",
-             debug_prefix_time(NULL), my_argv[0], est->amdevice));
-    wait(NULL);
-    dbprintf(("%s: after %s \"%s\" wait\n",
-             debug_prefix_time(NULL), my_argv[0], est->amdevice));
+    dbprintf(_("waiting for %s %s child (pid=%d)\n"),
+            command, est->qamdevice, (int)calcpid);
+    waitpid(calcpid, &wait_status, 0);
+    if (WIFSIGNALED(wait_status)) {
+       errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
+                           "calcsize", WTERMSIG(wait_status),
+                           dbfn());
+    } else if (WIFEXITED(wait_status)) {
+       if (WEXITSTATUS(wait_status) != 0) {
+           errmsg = vstrallocf(_("%s exited with status %d: see %s"),
+                               "calcsize", WEXITSTATUS(wait_status),
+                               dbfn());
+       } else {
+           /* Normal exit */
+       }
+    } else {
+       errmsg = vstrallocf(_("%s got bad exit: see %s"),
+                            "calcsize", dbfn());
+    }
+    dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
+            command, est->qamdevice,
+             (int)calcpid, WEXITSTATUS(wait_status));
 
-    dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
-    dbprintf(("%s: estimate time for %s: %s\n",
-             debug_prefix(NULL),
-             est->amname,
-             walltime_str(timessub(curclock(), start_time))));
+    dbprintf(_(".....\n"));
+    dbprintf(_("estimate time for %s: %s\n"),
+             est->qamname,
+             walltime_str(timessub(curclock(), start_time)));
 
-    for(i = 0; i < my_argc; i++) {
-       amfree(my_argv[i]);
+common_exit:
+    if (errmsg && errmsg[0] != '\0') {
+       if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
+           qerrmsg = quote_string(errmsg);
+           dbprintf(_("errmsg is %s\n"), errmsg);
+           g_printf("%s %d ERROR %s\n",
+                   est->qamname, 0, qerrmsg);
+           amfree(qerrmsg);
+       }
     }
+    amfree(errmsg);
+    g_ptr_array_free_full(argv_ptr);
     amfree(cmd);
 }
 
 
-/*
- * ------------------------------------------------------------------------
- *
- */
-
-/* local functions */
-void dump_calc_estimates P((disk_estimates_t *est));
-long getsize_dump P((char *disk, char *amdevice, int level, option_t *options));
-long getsize_smbtar P((char *disk, char *amdevice, int level, option_t *options));
-long getsize_gnutar P((char *disk, char *amdevice, int level,
-                      option_t *options, time_t dumpsince));
-long handle_dumpline P((char *str));
-double first_num P((char *str));
-
-void dump_calc_estimates(est)
-disk_estimates_t *est;
+void
+dump_calc_estimates(
+    disk_estimates_t * est)
 {
     int level;
-    long size;
+    off_t size;
+    char *errmsg=NULL, *qerrmsg;
 
     for(level = 0; level < DUMP_LEVELS; level++) {
        if(est->est[level].needestimate) {
-           dbprintf(("%s: getting size via dump for %s level %d\n",
-                     debug_prefix_time(NULL), est->amname, level));
-           size = getsize_dump(est->amname, est->amdevice,level, est->options);
+           dbprintf(_("getting size via dump for %s level %d\n"),
+                     est->qamname, level);
+           size = getsize_dump(est->dle, level, &errmsg);
 
            amflock(1, "size");
 
-           fseek(stdout, (off_t)0, SEEK_SET);
-
-           printf("%s %d SIZE %ld\n", est->amname, level, size);
+           g_printf(_("%s %d SIZE %lld\n"),
+                  est->qamname, level, (long long)size);
+           if (errmsg && errmsg[0] != '\0') {
+               if(am_has_feature(g_options->features,
+                                 fe_rep_sendsize_quoted_error)) {
+                   qerrmsg = quote_string(errmsg);
+                   dbprintf(_("errmsg is %s\n"), errmsg);
+                   g_printf("%s %d ERROR %s\n",
+                          est->qamname, level, qerrmsg);
+                   amfree(qerrmsg);
+               }
+           }
+           amfree(errmsg);
            fflush(stdout);
 
            amfunlock(1, "size");
@@ -717,23 +1180,35 @@ disk_estimates_t *est;
 }
 
 #ifdef SAMBA_CLIENT
-void smbtar_calc_estimates(est)
-disk_estimates_t *est;
+void
+smbtar_calc_estimates(
+    disk_estimates_t * est)
 {
     int level;
-    long size;
+    off_t size;
+    char  *errmsg = NULL, *qerrmsg;
 
     for(level = 0; level < DUMP_LEVELS; level++) {
        if(est->est[level].needestimate) {
-           dbprintf(("%s: getting size via smbclient for %s level %d\n",
-                     debug_prefix_time(NULL), est->amname, level));
-           size = getsize_smbtar(est->amname, est->amdevice, level, est->options);
+           dbprintf(_("getting size via smbclient for %s level %d\n"),
+                     est->qamname, level);
+           size = getsize_smbtar(est->dle, level, &errmsg);
 
            amflock(1, "size");
 
-           fseek(stdout, (off_t)0, SEEK_SET);
-
-           printf("%s %d SIZE %ld\n", est->amname, level, size);
+           g_printf(_("%s %d SIZE %lld\n"),
+                  est->qamname, level, (long long)size);
+           if (errmsg && errmsg[0] != '\0') {
+               if(am_has_feature(g_options->features,
+                                 fe_rep_sendsize_quoted_error)) {
+                   qerrmsg = quote_string(errmsg);
+                   dbprintf(_("errmsg is %s\n"), errmsg);
+                   g_printf("%s %d ERROR %s\n",
+                          est->qamname, level, qerrmsg);
+                   amfree(qerrmsg);
+               }
+           }
+           amfree(errmsg);
            fflush(stdout);
 
            amfunlock(1, "size");
@@ -743,43 +1218,57 @@ disk_estimates_t *est;
 #endif
 
 #ifdef GNUTAR
-void gnutar_calc_estimates(est)
-disk_estimates_t *est;
+void
+gnutar_calc_estimates(
+    disk_estimates_t * est)
 {
-  int level;
-  long size;
-
-  for(level = 0; level < DUMP_LEVELS; level++) {
-      if (est->est[level].needestimate) {
-         dbprintf(("%s: getting size via gnutar for %s level %d\n",
-                   debug_prefix_time(NULL), est->amname, level));
-         size = getsize_gnutar(est->amname, est->amdevice, level,
-                               est->options, est->est[level].dumpsince);
+    int level;
+    off_t size;
+    char *errmsg = NULL, *qerrmsg;
 
-         amflock(1, "size");
+    for(level = 0; level < DUMP_LEVELS; level++) {
+       if (est->est[level].needestimate) {
+           dbprintf(_("getting size via gnutar for %s level %d\n"),
+                     est->qamname, level);
+           size = getsize_gnutar(est->dle, level,
+                                 est->est[level].dumpsince,
+                                 &errmsg);
 
-         fseek(stdout, (off_t)0, SEEK_SET);
+           amflock(1, "size");
 
-         printf("%s %d SIZE %ld\n", est->amname, level, size);
-         fflush(stdout);
+           g_printf(_("%s %d SIZE %lld\n"),
+                  est->qamname, level, (long long)size);
+           if (errmsg && errmsg[0] != '\0') {
+               if(am_has_feature(g_options->features,
+                                 fe_rep_sendsize_quoted_error)) {
+                   qerrmsg = quote_string(errmsg);
+                   dbprintf(_("errmsg is %s\n"), errmsg);
+                   g_printf(_("%s %d ERROR %s\n"),
+                          est->qamname, level, qerrmsg);
+                   amfree(qerrmsg);
+               }
+           }
+           amfree(errmsg);
+           fflush(stdout);
 
-         amfunlock(1, "size");
-      }
-  }
+           amfunlock(1, "size");
+       }
+    }
 }
 #endif
 
-typedef struct regex_s {
+typedef struct regex_scale_s {
     char *regex;
     int scale;
-} regex_t;
+} regex_scale_t;
 
-regex_t re_size[] = {
+/*@ignore@*/
+regex_scale_t re_size[] = {
 #ifdef DUMP
     {"  DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
     {"  DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
-    {"  DUMP: [Ee]stimated [0-9][0-9]* bytes", 1},            /* Ultrix 4.4 */
-    {" UFSDUMP: estimated [0-9][0-9]* blocks", 512},           /* NEC EWS-UX */
+    {"  DUMP: [Ee]stimated [0-9][0-9]* bytes", 1},             /* Ultrix 4.4 */
+    {" UFSDUMP: estimated [0-9][0-9]* blocks", 512},           /* NEC EWS-UX */
     {"dump: Estimate: [0-9][0-9]* tape blocks", 1024},             /* OSF/1 */
     {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
     {"backup: estimated [0-9][0-9]* 1k blocks", 1024},               /* AIX */
@@ -787,37 +1276,33 @@ regex_t re_size[] = {
     {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024},  /* AIX */
     {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024},  /* AIX */
     {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
-                                                              /* DU 4.0 dump */
-    {"dump: Dumping [0-9][0-9]* bytes, ", 1},                /* DU 4.0 vdump */
-    {"DUMP: estimated [0-9][0-9]* KB output", 1024},                 /* HPUX */
-    {"DUMP: estimated [0-9][0-9]* KB\\.", 1024},                 /* NetApp */
-    {"  UFSDUMP: estimated [0-9][0-9]* blocks", 512},               /* Sinix */
+                                                             /* DU 4.0 dump  */
+    {"dump: Dumping [0-9][0-9]* bytes, ", 1},                /* DU 4.0 vdump */
+    {"DUMP: estimated [0-9][0-9]* KB output", 1024},                 /* HPUX */
+    {"DUMP: estimated [0-9][0-9]* KB\\.", 1024},                   /* NetApp */
+    {"  UFSDUMP: estimated [0-9][0-9]* blocks", 512},               /* Sinix */
 
 #ifdef HAVE_DUMP_ESTIMATE
     {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
-                                                          /* DU 3.2g dump -E */
-    {"^[0-9][0-9]* blocks$", 1024},                      /* DU 4.0 dump  -E */
-    {"^[0-9][0-9]*$", 1},                             /* Solaris ufsdump -S */
+                                                          /* DU 3.2g dump -E */
+    {"^[0-9][0-9]* blocks$", 1024},                       /* DU 4.0 dump  -E */
+    {"^[0-9][0-9]*$", 1},                              /* Solaris ufsdump -S */
 #endif
 #endif
 
 #ifdef VDUMP
-    {"vdump: Dumping [0-9][0-9]* bytes, ", 1},               /* OSF/1 vdump */
+    {"vdump: Dumping [0-9][0-9]* bytes, ", 1},                /* OSF/1 vdump */
 #endif
     
 #ifdef VXDUMP
-    {"vxdump: estimated [0-9][0-9]* blocks", 512},          /* HPUX's vxdump */
-    {"  VXDUMP: estimated [0-9][0-9]* blocks", 512},                /* Sinix */
+    {"vxdump: estimated [0-9][0-9]* blocks", 512},          /* HPUX's vxdump */
+    {"  VXDUMP: estimated [0-9][0-9]* blocks", 512},                /* Sinix */
 #endif
 
 #ifdef XFSDUMP
     {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1},  /* Irix 6.2 xfs */
 #endif
 
-#ifdef USE_QUICK_AND_DIRTY_ESTIMATES
-    {"amqde estimate: [0-9][0-9]* kb", 1024},                      /* amqde */
-#endif
-    
 #ifdef GNUTAR
     {"Total bytes written: [0-9][0-9]*", 1},               /* Gnutar client */
 #endif
@@ -834,16 +1319,17 @@ regex_t re_size[] = {
 
     { NULL, 0 }
 };
+/*@end@*/
 
-
-long getsize_dump(disk, amdevice, level, options)
-    char *disk, *amdevice;
-    int level;
-    option_t *options;
+off_t
+getsize_dump(
+    dle_t      *dle,
+    int                level,
+    char      **errmsg)
 {
     int pipefd[2], nullfd, stdoutfd, killctl[2];
     pid_t dumppid;
-    long size;
+    off_t size;
     FILE *dumpout;
     char *dumpkeys = NULL;
     char *device = NULL;
@@ -855,21 +1341,57 @@ long getsize_dump(disk, amdevice, level, options)
     char level_str[NUM_STR_SIZE];
     int s;
     times_t start_time;
+    char *qdisk = quote_string(dle->disk);
+    char *qdevice;
+    char *config;
+    amwait_t wait_status;
+#if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
+    int is_rundump = 1;
+#endif
 
-    ap_snprintf(level_str, sizeof(level_str), "%d", level);
+    if (level > 9)
+       return -2; /* planner will not even consider this level */
 
-    device = amname_to_devname(amdevice);
-    fstype = amname_to_fstype(amdevice);
+    g_snprintf(level_str, SIZEOF(level_str), "%d", level);
 
-    dbprintf(("%s: calculating for device '%s' with '%s'\n",
-             debug_prefix_time(NULL), device, fstype));
+    device = amname_to_devname(dle->device);
+    qdevice = quote_string(device);
+    fstype = amname_to_fstype(dle->device);
 
-    cmd = vstralloc(libexecdir, "/rundump", versionsuffix(), NULL);
-    rundump_cmd = stralloc(cmd);
+    dbprintf(_("calculating for device %s with %s\n"),
+             qdevice, fstype);
 
-    stdoutfd = nullfd = open("/dev/null", O_RDWR);
+    cmd = vstralloc(amlibexecdir, "/rundump", NULL);
+    rundump_cmd = stralloc(cmd);
+    if (g_options->config)
+        config = g_options->config;
+    else
+        config = "NOCONFIG";
+    if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
+       *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
+                            strerror(errno));
+       dbprintf("%s\n", *errmsg);
+       amfree(cmd);
+       amfree(rundump_cmd);
+       amfree(fstype);
+       amfree(device);
+       amfree(qdevice);
+       amfree(qdisk);
+       return(-1);
+    }
     pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
-    pipe(pipefd);
+    if (pipe(pipefd) < 0) {
+       *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
+                            strerror(errno));
+       dbprintf("%s\n", *errmsg);
+       amfree(cmd);
+       amfree(rundump_cmd);
+       amfree(fstype);
+       amfree(device);
+       amfree(qdevice);
+       amfree(qdisk);
+       return(-1);
+    }
 
 #ifdef XFSDUMP                                         /* { */
 #ifdef DUMP                                            /* { */
@@ -878,9 +1400,9 @@ long getsize_dump(disk, amdevice, level, options)
     if (1)
 #endif                                                 /* } */
     {
-        name = stralloc(" (xfsdump)");
-       dbprintf(("%s: running \"%s%s -F -J -l %s - %s\"\n",
-                 debug_prefix_time(NULL), cmd, name, level_str, device));
+       name = stralloc(" (xfsdump)");
+       dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
+                 cmd, name, level_str, qdevice);
     }
     else
 #endif                                                 /* } */
@@ -892,14 +1414,16 @@ long getsize_dump(disk, amdevice, level, options)
 #endif                                                 /* } */
     {
 #ifdef USE_RUNDUMP
-        name = stralloc(" (vxdump)");
+       name = stralloc(" (vxdump)");
 #else
        name = stralloc("");
        cmd = newstralloc(cmd, VXDUMP);
+       config = skip_argument;
+       is_rundump = 0;
 #endif
        dumpkeys = vstralloc(level_str, "s", "f", NULL);
-        dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
-                 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
+       dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
+                 cmd, name, dumpkeys, qdevice);
     }
     else
 #endif                                                 /* } */
@@ -911,11 +1435,9 @@ long getsize_dump(disk, amdevice, level, options)
 #endif                                                 /* } */
     {
        name = stralloc(" (vdump)");
-       amfree(device);
-       device = amname_to_dirname(amdevice);
        dumpkeys = vstralloc(level_str, "b", "f", NULL);
-       dbprintf(("%s: running \"%s%s %s 60 - %s\"\n",
-                 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
+       dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
+                 cmd, name, dumpkeys, qdevice);
     }
     else
 #endif                                                 /* } */
@@ -930,20 +1452,28 @@ long getsize_dump(disk, amdevice, level, options)
 # else                                                 /* } { */
        name = stralloc("");
        cmd = newstralloc(cmd, DUMP);
+        config = skip_argument;
+       is_rundump = 0;
 # endif                                                        /* } */
 
 # ifdef AIX_BACKUP                                     /* { */
        dumpkeys = vstralloc("-", level_str, "f", NULL);
-       dbprintf(("%s: running \"%s%s %s - %s\"\n",
-                 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
+       dbprintf(_("running \"%s%s %s - %s\"\n"),
+                 cmd, name, dumpkeys, qdevice);
 # else                                                 /* } { */
+#  ifdef HAVE_DUMP_ESTIMATE
+#    define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
+#  else
+#    define PARAM_DUMP_ESTIMATE ""
+#  endif
+#  ifdef HAVE_HONOR_NODUMP
+#    define PARAM_HONOR_NODUMP "h"
+#  else
+#    define PARAM_HONOR_NODUMP ""
+#  endif
        dumpkeys = vstralloc(level_str,
-#  ifdef HAVE_DUMP_ESTIMATE                            /* { */
-                            HAVE_DUMP_ESTIMATE,
-#  endif                                               /* } */
-#  ifdef HAVE_HONOR_NODUMP                             /* { */
-                            "h",
-#  endif                                               /* } */
+                            PARAM_DUMP_ESTIMATE,
+                            PARAM_HONOR_NODUMP,
                             "s", "f", NULL);
 
 #  ifdef HAVE_DUMP_ESTIMATE
@@ -951,32 +1481,41 @@ long getsize_dump(disk, amdevice, level, options)
 #  endif
 
 #  ifdef HAVE_HONOR_NODUMP                             /* { */
-       dbprintf(("%s: running \"%s%s %s 0 1048576 - %s\"\n",
-                 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
+       dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
+                 cmd, name, dumpkeys, qdevice);
 #  else                                                        /* } { */
-       dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
-                 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
+       dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
+                 cmd, name, dumpkeys, qdevice);
 #  endif                                               /* } */
 # endif                                                        /* } */
     }
     else
 #endif                                                 /* } */
     {
-       error("no dump program available");
+       error(_("no dump program available"));
+       /*NOTREACHED*/
     }
 
-    pipe(killctl);
+    if (pipe(killctl) < 0) {
+       dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
+       /* Message will be printed later... */
+       killctl[0] = killctl[1] = -1;
+    }
 
     start_time = curclock();
     switch(dumppid = fork()) {
     case -1:
-       dbprintf(("%s: cannot fork for killpgrp: %s\n",
-                 debug_prefix(NULL), strerror(errno)));
+       *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
+                            strerror(errno));
+       dbprintf("%s\n", *errmsg);
        amfree(dumpkeys);
        amfree(cmd);
        amfree(rundump_cmd);
        amfree(device);
+       amfree(qdevice);
+       amfree(qdisk);
        amfree(name);
+       amfree(fstype);
        return -1;
     default:
        break; 
@@ -984,21 +1523,18 @@ long getsize_dump(disk, amdevice, level, options)
        if(SETPGRP == -1)
            SETPGRP_FAILED();
        else if (killctl[0] == -1 || killctl[1] == -1)
-           dbprintf(("%s: pipe for killpgrp failed, trying without killpgrp\n",
-                     debug_prefix(NULL)));
+           dbprintf(_("Trying without killpgrp\n"));
        else {
            switch(fork()) {
            case -1:
-               dbprintf(("%s: fork failed, trying without killpgrp\n",
-                         debug_prefix(NULL)));
+               dbprintf(_("fork failed, trying without killpgrp\n"));
                break;
 
            default:
            {
-               char *killpgrp_cmd = vstralloc(libexecdir, "/killpgrp",
-                                              versionsuffix(), NULL);
-               dbprintf(("%s: running %s\n",
-                         debug_prefix_time(NULL), killpgrp_cmd));
+               char *config;
+               char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp", NULL);
+               dbprintf(_("running %s\n"), killpgrp_cmd);
                dup2(killctl[0], 0);
                dup2(nullfd, 1);
                dup2(nullfd, 2);
@@ -1006,9 +1542,15 @@ long getsize_dump(disk, amdevice, level, options)
                close(pipefd[1]);
                close(killctl[1]);
                close(nullfd);
-               execle(killpgrp_cmd, killpgrp_cmd, (char *)0, safe_env());
-               dbprintf(("%s: cannot execute %s: %s\n",
-                         debug_prefix(NULL), killpgrp_cmd, strerror(errno)));
+               if (g_options->config)
+                   config = g_options->config;
+               else
+                   config = "NOCONFIG";
+               safe_fd(-1, 0);
+               execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
+                      safe_env());
+               dbprintf(_("cannot execute %s: %s\n"),
+                         killpgrp_cmd, strerror(errno));
                exit(-1);
            }
 
@@ -1025,6 +1567,7 @@ long getsize_dump(disk, amdevice, level, options)
            aclose(killctl[0]);
        if (killctl[1] != -1)
            aclose(killctl[1]);
+       safe_fd(-1, 0);
 
 #ifdef XFSDUMP
 #ifdef DUMP
@@ -1032,8 +1575,12 @@ long getsize_dump(disk, amdevice, level, options)
 #else
        if (1)
 #endif
-           execle(cmd, "xfsdump", "-F", "-J", "-l", level_str, "-", device,
-                  (char *)0, safe_env());
+           if (is_rundump)
+               execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
+                      level_str, "-", device, (char *)0, safe_env());
+           else
+               execle(cmd, "xfsdump", "-F", "-J", "-l",
+                      level_str, "-", device, (char *)0, safe_env());
        else
 #endif
 #ifdef VXDUMP
@@ -1042,8 +1589,12 @@ long getsize_dump(disk, amdevice, level, options)
 #else
        if (1)
 #endif
-           execle(cmd, "vxdump", dumpkeys, "1048576", "-", device, (char *)0,
-                  safe_env());
+           if (is_rundump)
+               execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
+                      "-", device, (char *)0, safe_env());
+           else
+               execle(cmd, "vxdump", dumpkeys, "1048576", "-",
+                      device, (char *)0, safe_env());
        else
 #endif
 #ifdef VDUMP
@@ -1052,24 +1603,42 @@ long getsize_dump(disk, amdevice, level, options)
 #else
        if (1)
 #endif
-           execle(cmd, "vdump", dumpkeys, "60", "-", device, (char *)0,
-                  safe_env());
+           if (is_rundump)
+               execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
+                      device, (char *)0, safe_env());
+           else
+               execle(cmd, "vdump", dumpkeys, "60", "-",
+                      device, (char *)0, safe_env());
        else
 #endif
 #ifdef DUMP
 # ifdef AIX_BACKUP
-           execle(cmd, "backup", dumpkeys, "-", device, (char *)0, safe_env());
+           if (is_rundump)
+               execle(cmd, "rundump", config, "backup", dumpkeys, "-",
+                      device, (char *)0, safe_env());
+           else
+               execle(cmd, "backup", dumpkeys, "-",
+                      device, (char *)0, safe_env());
 # else
-           execle(cmd, "dump", dumpkeys, 
+           if (is_rundump) {
+               execle(cmd, "rundump", config, "dump", dumpkeys, 
+#ifdef HAVE_HONOR_NODUMP
+                      "0",
+#endif
+                      "1048576", "-", device, (char *)0, safe_env());
+           } else {
+               execle(cmd, "dump", dumpkeys, 
 #ifdef HAVE_HONOR_NODUMP
-                  "0",
+                      "0",
 #endif
-                  "1048576", "-", device, (char *)0, safe_env());
+                      "1048576", "-", device, (char *)0, safe_env());
+           }
 # endif
 #endif
        {
-           error("exec %s failed or no dump program available: %s",
+           error(_("exec %s failed or no dump program available: %s"),
                  cmd, strerror(errno));
+           /*NOTREACHED*/
        }
     }
 
@@ -1080,44 +1649,58 @@ long getsize_dump(disk, amdevice, level, options)
     if (killctl[0] != -1)
        aclose(killctl[0]);
     dumpout = fdopen(pipefd[0],"r");
+    if (!dumpout) {
+       error(_("Can't fdopen: %s"), strerror(errno));
+       /*NOTREACHED*/
+    }
 
-    for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
-       dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
+    for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
+       if (line[0] == '\0')
+           continue;
+       dbprintf("%s\n", line);
        size = handle_dumpline(line);
-       if(size > -1) {
+       if(size > (off_t)-1) {
            amfree(line);
-           if((line = agets(dumpout)) != NULL) {
-               dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
+           while ((line = agets(dumpout)) != NULL) {
+               if (line[0] != '\0')
+                   break;
+               amfree(line);
+           }
+           if (line != NULL) {
+               dbprintf("%s\n", line);
            }
            break;
        }
     }
     amfree(line);
 
-    dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
-    dbprintf(("%s: estimate time for %s level %d: %s\n",
-             debug_prefix(NULL),
-             disk,
+    dbprintf(".....\n");
+    dbprintf(_("estimate time for %s level %d: %s\n"),
+             qdisk,
              level,
-             walltime_str(timessub(curclock(), start_time))));
-    if(size == -1) {
-       dbprintf(("%s: no size line match in %s%s output for \"%s\"\n",
-                 debug_prefix(NULL), cmd, name, disk));
-       dbprintf(("%s: .....\n", debug_prefix(NULL)));
-    } else if(size == 0 && level == 0) {
-       dbprintf(("%s: possible %s%s problem -- is \"%s\" really empty?\n",
-                 debug_prefix(NULL), cmd, name, disk));
-       dbprintf(("%s: .....\n", debug_prefix(NULL)));
-    }
-    dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
-             debug_prefix(NULL),
-             disk,
+             walltime_str(timessub(curclock(), start_time)));
+    if(size == (off_t)-1) {
+       *errmsg = vstrallocf(_("no size line match in %s%s output"),
+                            cmd, name);
+       dbprintf(_("%s for %s\n"),
+                 *errmsg, qdisk);
+
+       dbprintf(".....\n");
+       dbprintf(_("Run %s%s manually to check for errors\n"),
+                   cmd, name);
+    } else if(size == (off_t)0 && level == 0) {
+       dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
+                 cmd, name, dle->disk);
+       dbprintf(".....\n");
+    } else {
+           dbprintf(_("estimate size for %s level %d: %lld KB\n"),
+             qdisk,
              level,
-             size));
+             (long long)size);
+    }
 
     if (killctl[1] != -1) {
-       dbprintf(("%s: asking killpgrp to terminate\n",
-                 debug_prefix_time(NULL)));
+       dbprintf(_("asking killpgrp to terminate\n"));
        aclose(killctl[1]);
        for(s = 5; s > 0; --s) {
            sleep(1);
@@ -1130,11 +1713,9 @@ long getsize_dump(disk, amdevice, level, options)
      * First, try to kill the dump process nicely.  If it ignores us
      * for several seconds, hit it harder.
      */
-    dbprintf(("%s: sending SIGTERM to process group %ld\n",
-             debug_prefix_time(NULL), (long)dumppid));
+    dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
     if (kill(-dumppid, SIGTERM) == -1) {
-       dbprintf(("%s: kill failed: %s\n",
-                 debug_prefix(NULL), strerror(errno)));
+       dbprintf(_("kill failed: %s\n"), strerror(errno));
     }
     /* Now check whether it dies */
     for(s = 5; s > 0; --s) {
@@ -1143,11 +1724,9 @@ long getsize_dump(disk, amdevice, level, options)
            goto terminated;
     }
 
-    dbprintf(("%s: sending SIGKILL to process group %ld\n",
-             debug_prefix_time(NULL), (long)dumppid));
+    dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
     if (kill(-dumppid, SIGKILL) == -1) {
-       dbprintf(("%s: kill failed: %s\n",
-                 debug_prefix(NULL), strerror(errno)));
+       dbprintf(_("kill failed: %s\n"), strerror(errno));
     }
     for(s = 5; s > 0; --s) {
        sleep(1);
@@ -1155,11 +1734,23 @@ long getsize_dump(disk, amdevice, level, options)
            goto terminated;
     }
 
-    dbprintf(("%s: waiting for %s%s \"%s\" child\n",
-             debug_prefix_time(NULL), cmd, name, disk));
-    wait(NULL);
-    dbprintf(("%s: after %s%s \"%s\" wait\n",
-             debug_prefix_time(NULL), cmd, name, disk));
+    dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
+    waitpid(dumppid, &wait_status, 0);
+    if (WIFSIGNALED(wait_status)) {
+       *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
+                            cmd, WTERMSIG(wait_status), dbfn());
+    } else if (WIFEXITED(wait_status)) {
+       if (WEXITSTATUS(wait_status) != 0) {
+           *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
+                                cmd, WEXITSTATUS(wait_status), dbfn());
+       } else {
+           /* Normal exit */
+       }
+    } else {
+       *errmsg = vstrallocf(_("%s got bad exit: see %s"),
+                            cmd, dbfn());
+    }
+    dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
 
  terminated:
 
@@ -1167,6 +1758,8 @@ long getsize_dump(disk, amdevice, level, options)
     afclose(dumpout);
 
     amfree(device);
+    amfree(qdevice);
+    amfree(qdisk);
     amfree(fstype);
 
     amfree(cmd);
@@ -1176,41 +1769,49 @@ long getsize_dump(disk, amdevice, level, options)
 }
 
 #ifdef SAMBA_CLIENT
-long getsize_smbtar(disk, amdevice, level, optionns)
-    char *disk, *amdevice;
-    int level;
-    option_t *optionns;
+off_t
+getsize_smbtar(
+    dle_t      *dle,
+    int                level,
+    char      **errmsg)
 {
     int pipefd = -1, nullfd = -1, passwdfd = -1;
-    int dumppid;
-    long size;
+    pid_t dumppid;
+    off_t size;
     FILE *dumpout;
     char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
     char *share = NULL, *subdir = NULL;
-    int lpass;
+    size_t lpass;
     char *pwtext;
-    int pwtext_len;
+    size_t pwtext_len;
     char *line;
     char *pw_fd_env;
     times_t start_time;
     char *error_pn = NULL;
+    char *qdisk = quote_string(dle->disk);
+    amwait_t wait_status;
 
     error_pn = stralloc2(get_pname(), "-smbclient");
 
-    parsesharename(amdevice, &share, &subdir);
+    if (level > 1)
+       return -2; /* planner will not even consider this level */
+
+    parsesharename(dle->device, &share, &subdir);
     if (!share) {
        amfree(share);
        amfree(subdir);
        set_pname(error_pn);
        amfree(error_pn);
-       error("cannot parse disk entry '%s' for share/subdir", disk);
+       error(_("cannot parse disk entry %s for share/subdir"), qdisk);
+       /*NOTREACHED*/
     }
     if ((subdir) && (SAMBA_VERSION < 2)) {
        amfree(share);
        amfree(subdir);
        set_pname(error_pn);
        amfree(error_pn);
-       error("subdirectory specified for share '%s' but samba not v2 or better", disk);
+       error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
+       /*NOTREACHED*/
     }
     if ((user_and_password = findpass(share, &domain)) == NULL) {
 
@@ -1220,11 +1821,12 @@ long getsize_smbtar(disk, amdevice, level, optionns)
        }
        set_pname(error_pn);
        amfree(error_pn);
-       error("cannot find password for %s", disk);
+       error(_("cannot find password for %s"), dle->disk);
+       /*NOTREACHED*/
     }
     lpass = strlen(user_and_password);
     if ((pwtext = strchr(user_and_password, '%')) == NULL) {
-       memset(user_and_password, '\0', lpass);
+       memset(user_and_password, '\0', (size_t)lpass);
        amfree(user_and_password);
        if(domain) {
            memset(domain, '\0', strlen(domain));
@@ -1232,12 +1834,25 @@ long getsize_smbtar(disk, amdevice, level, optionns)
        }
        set_pname(error_pn);
        amfree(error_pn);
-       error("password field not \'user%%pass\' for %s", disk);
+       error(_("password field not \'user%%pass\' for %s"), dle->disk);
+       /*NOTREACHED*/
     }
     *pwtext++ = '\0';
     pwtext_len = strlen(pwtext);
     if ((sharename = makesharename(share, 0)) == NULL) {
-       memset(user_and_password, '\0', lpass);
+       memset(user_and_password, '\0', (size_t)lpass);
+       amfree(user_and_password);
+       if(domain) {
+           memset(domain, '\0', strlen(domain));
+           amfree(domain);
+       }
+       set_pname(error_pn);
+       amfree(error_pn);
+       error(_("cannot make share name of %s"), share);
+       /*NOTREACHED*/
+    }
+    if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
+       memset(user_and_password, '\0', (size_t)lpass);
        amfree(user_and_password);
        if(domain) {
            memset(domain, '\0', strlen(domain));
@@ -1245,9 +1860,11 @@ long getsize_smbtar(disk, amdevice, level, optionns)
        }
        set_pname(error_pn);
        amfree(error_pn);
-       error("cannot make share name of %s", share);
+       amfree(sharename);
+       error(_("could not open /dev/null: %s\n"),
+             strerror(errno));
+       /*NOTREACHED*/
     }
-    nullfd = open("/dev/null", O_RDWR);
 
 #if SAMBA_VERSION >= 2
     if (level == 0)
@@ -1268,7 +1885,7 @@ long getsize_smbtar(disk, amdevice, level, optionns)
     } else {
        pw_fd_env = "dummy_PASSWD_FD";
     }
-    dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
+    dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
              &nullfd, &nullfd, &pipefd, 
              pw_fd_env, &passwdfd,
              "smbclient",
@@ -1290,17 +1907,18 @@ long getsize_smbtar(disk, amdevice, level, optionns)
        amfree(domain);
     }
     aclose(nullfd);
-    if(pwtext_len > 0 && fullwrite(passwdfd, pwtext, pwtext_len) < 0) {
+    if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
        int save_errno = errno;
 
-       memset(user_and_password, '\0', lpass);
+       memset(user_and_password, '\0', (size_t)lpass);
        amfree(user_and_password);
        aclose(passwdfd);
        set_pname(error_pn);
        amfree(error_pn);
-       error("password write failed: %s", strerror(save_errno));
+       error(_("password write failed: %s"), strerror(save_errno));
+       /*NOTREACHED*/
     }
-    memset(user_and_password, '\0', lpass);
+    memset(user_and_password, '\0', (size_t)lpass);
     amfree(user_and_password);
     aclose(passwdfd);
     amfree(sharename);
@@ -1308,67 +1926,94 @@ long getsize_smbtar(disk, amdevice, level, optionns)
     amfree(subdir);
     amfree(error_pn);
     dumpout = fdopen(pipefd,"r");
+    if (!dumpout) {
+       error(_("Can't fdopen: %s"), strerror(errno));
+       /*NOTREACHED*/
+    }
 
-    for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
-       dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
+    for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
+       if (line[0] == '\0')
+           continue;
+       dbprintf("%s\n", line);
        size = handle_dumpline(line);
        if(size > -1) {
            amfree(line);
-           if((line = agets(dumpout)) != NULL) {
-               dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
+           while ((line = agets(dumpout)) != NULL) {
+               if (line[0] != '\0')
+                   break;
+               amfree(line);
+           }
+           if(line != NULL) {
+               dbprintf("%s\n", line);
            }
            break;
        }
     }
     amfree(line);
 
-    dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
-    dbprintf(("%s: estimate time for %s level %d: %s\n",
-             debug_prefix(NULL),
-             disk,
+    dbprintf(".....\n");
+    dbprintf(_("estimate time for %s level %d: %s\n"),
+             qdisk,
              level,
-             walltime_str(timessub(curclock(), start_time))));
-    if(size == -1) {
-       dbprintf(("%s: no size line match in %s output for \"%s\"\n",
-                 debug_prefix(NULL), SAMBA_CLIENT, disk));
-       dbprintf(("%s: .....\n", debug_prefix(NULL)));
-    } else if(size == 0 && level == 0) {
-       dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
-                 debug_prefix(NULL), SAMBA_CLIENT, disk));
-       dbprintf(("%s: .....\n", debug_prefix(NULL)));
-    }
-    dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
-             debug_prefix(NULL),
-             disk,
+             walltime_str(timessub(curclock(), start_time)));
+    if(size == (off_t)-1) {
+       *errmsg = vstrallocf(_("no size line match in %s output"),
+                            SAMBA_CLIENT);
+       dbprintf(_("%s for %s\n"),
+                 *errmsg, qdisk);
+       dbprintf(".....\n");
+    } else if(size == (off_t)0 && level == 0) {
+       dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
+                 SAMBA_CLIENT, dle->disk);
+       dbprintf(".....\n");
+    }
+    dbprintf(_("estimate size for %s level %d: %lld KB\n"),
+             qdisk,
              level,
-             size));
+             (long long)size);
 
     kill(-dumppid, SIGTERM);
 
-    dbprintf(("%s: waiting for %s \"%s\" child\n",
-             debug_prefix_time(NULL), SAMBA_CLIENT, disk));
-    wait(NULL);
-    dbprintf(("%s: after %s \"%s\" wait\n",
-             debug_prefix_time(NULL), SAMBA_CLIENT, disk));
+    dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
+    waitpid(dumppid, &wait_status, 0);
+    if (WIFSIGNALED(wait_status)) {
+       *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
+                            SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
+    } else if (WIFEXITED(wait_status)) {
+       if (WEXITSTATUS(wait_status) != 0) {
+           *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
+                                SAMBA_CLIENT, WEXITSTATUS(wait_status),
+                                dbfn());
+       } else {
+           /* Normal exit */
+       }
+    } else {
+       *errmsg = vstrallocf(_("%s got bad exit: see %s"),
+                            SAMBA_CLIENT, dbfn());
+    }
+    dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
 
     afclose(dumpout);
     pipefd = -1;
 
     amfree(error_pn);
+    amfree(qdisk);
 
     return size;
 }
 #endif
 
 #ifdef GNUTAR
-long getsize_gnutar(disk, amdevice, level, options, dumpsince)
-char *disk, *amdevice;
-int level;
-option_t *options;
-time_t dumpsince;
+off_t
+getsize_gnutar(
+    dle_t      *dle,
+    int                level,
+    time_t     dumpsince,
+    char      **errmsg)
 {
-    int pipefd = -1, nullfd = -1, dumppid;
-    long size = -1;
+    int pipefd = -1, nullfd = -1;
+    pid_t dumppid;
+    off_t size = (off_t)-1;
     FILE *dumpout = NULL;
     char *incrname = NULL;
     char *basename = NULL;
@@ -1378,49 +2023,50 @@ time_t dumpsince;
     FILE *out = NULL;
     char *line = NULL;
     char *cmd = NULL;
+    char *command = NULL;
     char dumptimestr[80];
     struct tm *gmtm;
     int nb_exclude = 0;
     int nb_include = 0;
-    char **my_argv;
-    int i;
+    GPtrArray *argv_ptr = g_ptr_array_new();
     char *file_exclude = NULL;
     char *file_include = NULL;
     times_t start_time;
-
-    if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
-    if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
-    if(options->include_file) nb_include += options->include_file->nb_element;
-    if(options->include_list) nb_include += options->include_list->nb_element;
-
-    if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
-    if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
-
-    my_argv = alloc(sizeof(char *) * 21);
-    i = 0;
-
-#ifdef GNUTAR_LISTED_INCREMENTAL_DIR
-    {
+    int infd, outfd;
+    ssize_t nb;
+    char buf[32768];
+    char *qdisk = quote_string(dle->disk);
+    char *gnutar_list_dir;
+    amwait_t wait_status;
+    char tmppath[PATH_MAX];
+
+    if (level > 9)
+       return -2; /* planner will not even consider this level */
+
+    if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
+    if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
+    if(dle->include_file) nb_include += dle->include_file->nb_element;
+    if(dle->include_list) nb_include += dle->include_list->nb_element;
+
+    if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
+    if(nb_include > 0) file_include = build_include(dle, 0);
+
+    gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
+    if (strlen(gnutar_list_dir) == 0)
+       gnutar_list_dir = NULL;
+    if (gnutar_list_dir) {
        char number[NUM_STR_SIZE];
-       char *s;
-       int ch;
        int baselevel;
+       char *sdisk = sanitise_filename(dle->disk);
 
-       basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
+       basename = vstralloc(gnutar_list_dir,
                             "/",
                             g_options->hostname,
-                            disk,
+                            sdisk,
                             NULL);
-       /*
-        * The loop starts at the first character of the host name,
-        * not the '/'.
-        */
-       s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
-       while((ch = *s++) != '\0') {
-           if(ch == '/' || isspace(ch)) s[-1] = '_';
-       }
+       amfree(sdisk);
 
-       ap_snprintf(number, sizeof(number), "%d", level);
+       g_snprintf(number, SIZEOF(number), "%d", level);
        incrname = vstralloc(basename, "_", number, ".new", NULL);
        unlink(incrname);
 
@@ -1430,193 +2076,215 @@ time_t dumpsince;
         * be true for a level 0), arrange to read from /dev/null.
         */
        baselevel = level;
-       while (in == NULL) {
+       infd = -1;
+       while (infd == -1) {
            if (--baselevel >= 0) {
-               ap_snprintf(number, sizeof(number), "%d", baselevel);
+               g_snprintf(number, SIZEOF(number), "%d", baselevel);
                inputname = newvstralloc(inputname,
                                         basename, "_", number, NULL);
            } else {
                inputname = newstralloc(inputname, "/dev/null");
            }
-           if ((in = fopen(inputname, "r")) == NULL) {
-               int save_errno = errno;
+           if ((infd = open(inputname, O_RDONLY)) == -1) {
 
-               dbprintf(("%s: gnutar: error opening %s: %s\n",
-                         debug_prefix(NULL), inputname, strerror(save_errno)));
+               *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
+                                    inputname, strerror(errno));
+               dbprintf("%s\n", *errmsg);
                if (baselevel < 0) {
                    goto common_exit;
                }
+               amfree(*errmsg);
            }
        }
 
        /*
         * Copy the previous listed incremental file to the new one.
         */
-       if ((out = fopen(incrname, "w")) == NULL) {
-           dbprintf(("%s: opening %s: %s\n",
-                     debug_prefix(NULL), incrname, strerror(errno)));
+       if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
+           *errmsg = vstrallocf(_("opening %s: %s"),
+                                incrname, strerror(errno));
+           dbprintf("%s\n", *errmsg);
            goto common_exit;
        }
 
-       for (; (line = agets(in)) != NULL; free(line)) {
-           if (fputs(line, out) == EOF || putc('\n', out) == EOF) {
-               dbprintf(("%s: writing to %s: %s\n",
-                          debug_prefix(NULL), incrname, strerror(errno)));
+       while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
+           if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
+               *errmsg = vstrallocf(_("writing to %s: %s"),
+                                    incrname, strerror(errno));
+               dbprintf("%s\n", *errmsg);
                goto common_exit;
            }
        }
-       amfree(line);
 
-       if (ferror(in)) {
-           dbprintf(("%s: reading from %s: %s\n",
-                     debug_prefix(NULL), inputname, strerror(errno)));
+       if (nb < 0) {
+           *errmsg = vstrallocf(_("reading from %s: %s"),
+                                inputname, strerror(errno));
+           dbprintf("%s\n", *errmsg);
            goto common_exit;
        }
-       if (fclose(in) == EOF) {
-           dbprintf(("%s: closing %s: %s\n",
-                     debug_prefix(NULL), inputname, strerror(errno)));
-           in = NULL;
+
+       if (close(infd) != 0) {
+           *errmsg = vstrallocf(_("closing %s: %s"),
+                                inputname, strerror(errno));
+           dbprintf("%s\n", *errmsg);
            goto common_exit;
        }
-       in = NULL;
-       if (fclose(out) == EOF) {
-           dbprintf(("%s: closing %s: %s\n",
-                     debug_prefix(NULL), incrname, strerror(errno)));
-           out = NULL;
+       if (close(outfd) != 0) {
+           *errmsg = vstrallocf(_("closing %s: %s"),
+                                incrname, strerror(errno));
+           dbprintf("%s\n", *errmsg);
            goto common_exit;
        }
-       out = NULL;
 
        amfree(inputname);
        amfree(basename);
     }
-#endif
 
     gmtm = gmtime(&dumpsince);
-    ap_snprintf(dumptimestr, sizeof(dumptimestr),
+    g_snprintf(dumptimestr, SIZEOF(dumptimestr),
                "%04d-%02d-%02d %2d:%02d:%02d GMT",
                gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
                gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
 
-    dirname = amname_to_dirname(amdevice);
+    dirname = amname_to_dirname(dle->device);
 
+    cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
+    g_ptr_array_add(argv_ptr, stralloc("runtar"));
+    if (g_options->config)
+       g_ptr_array_add(argv_ptr, stralloc(g_options->config));
+    else
+       g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
 
-
-#ifdef USE_QUICK_AND_DIRTY_ESTIMATES
-    ap_snprintf(dumptimestr, sizeof(dumptimestr), "%ld", dumpsince);
-
-    cmd = vstralloc(libexecdir, "/", "amqde", versionsuffix(), NULL);
-
-    my_argv[i++] = vstralloc(libexecdir, "/", "amqde", versionsuffix(), NULL);
-    my_argv[i++] = "-s";
-    my_argv[i++] = dumptimestr;
-    if(file_exclude) { /* at present, this is not used... */
-       my_argv[i++] = "-x";
-       my_argv[i++] = file_exclude;
-    }
-    /* [XXX] need to also consider implementation of --files-from */
-    my_argv[i++] = dirname;
-    my_argv[i++] = NULL;
-#else
 #ifdef GNUTAR
-    cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
-
-    my_argv[i++] = GNUTAR;
-#else
-    my_argv[i++] = "tar";
-#endif
-    my_argv[i++] = "--create";
-    my_argv[i++] = "--file";
-    my_argv[i++] = "/dev/null";
-    my_argv[i++] = "--directory";
-    my_argv[i++] = dirname;
-    my_argv[i++] = "--one-file-system";
-#ifdef GNUTAR_LISTED_INCREMENTAL_DIR
-    my_argv[i++] = "--listed-incremental";
-    my_argv[i++] = incrname;
+    g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
 #else
-    my_argv[i++] = "--incremental";
-    my_argv[i++] = "--newer";
-    my_argv[i++] = dumptimestr;
+    g_ptr_array_add(argv_ptr, stralloc("tar"));
 #endif
+    g_ptr_array_add(argv_ptr, stralloc("--create"));
+    g_ptr_array_add(argv_ptr, stralloc("--file"));
+    g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
+    /* use --numeric-owner for estimates, to reduce the number of user/group
+     * lookups required */
+    g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
+    g_ptr_array_add(argv_ptr, stralloc("--directory"));
+    canonicalize_pathname(dirname, tmppath);
+    g_ptr_array_add(argv_ptr, stralloc(tmppath));
+    g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
+    if (gnutar_list_dir) {
+       g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
+       g_ptr_array_add(argv_ptr, stralloc(incrname));
+    } else {
+       g_ptr_array_add(argv_ptr, stralloc("--incremental"));
+       g_ptr_array_add(argv_ptr, stralloc("--newer"));
+       g_ptr_array_add(argv_ptr, stralloc(dumptimestr));
+    }
 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
     /* --atime-preserve causes gnutar to call
      * utime() after reading files in order to
      * adjust their atime.  However, utime()
      * updates the file's ctime, so incremental
      * dumps will think the file has changed. */
-    my_argv[i++] = "--atime-preserve";
+    g_ptr_array_add(argv_ptr, stralloc("--atime-preserve"));
 #endif
-    my_argv[i++] = "--sparse";
-    my_argv[i++] = "--ignore-failed-read";
-    my_argv[i++] = "--totals";
+    g_ptr_array_add(argv_ptr, stralloc("--sparse"));
+    g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
+    g_ptr_array_add(argv_ptr, stralloc("--totals"));
 
     if(file_exclude) {
-       my_argv[i++] = "--exclude-from";
-       my_argv[i++] = file_exclude;
+       g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
+       g_ptr_array_add(argv_ptr, stralloc(file_exclude));
     }
 
     if(file_include) {
-       my_argv[i++] = "--files-from";
-       my_argv[i++] = file_include;
+       g_ptr_array_add(argv_ptr, stralloc("--files-from"));
+       g_ptr_array_add(argv_ptr, stralloc(file_include));
     }
     else {
-       my_argv[i++] = ".";
+       g_ptr_array_add(argv_ptr, stralloc("."));
     }
-#endif /* USE_QUICK_AND_DIRTY_ESTIMATES */
-    my_argv[i++] = NULL;
+    g_ptr_array_add(argv_ptr, NULL);
 
     start_time = curclock();
 
-    nullfd = open("/dev/null", O_RDWR);
-    dumppid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
-    amfree(cmd);
-    amfree(file_exclude);
-    amfree(file_include);
+    if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
+       *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
+                            strerror(errno));
+       dbprintf("%s\n", *errmsg);
+       goto common_exit;
+    }
+
+    command = (char *)g_ptr_array_index(argv_ptr, 0);
+    dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
+                        &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
 
     dumpout = fdopen(pipefd,"r");
+    if (!dumpout) {
+       error(_("Can't fdopen: %s"), strerror(errno));
+       /*NOTREACHED*/
+    }
 
-    for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
-       dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
+    for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
+       if (line[0] == '\0')
+           continue;
+       dbprintf("%s\n", line);
        size = handle_dumpline(line);
-       if(size > -1) {
+       if(size > (off_t)-1) {
            amfree(line);
-           if((line = agets(dumpout)) != NULL) {
-               dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
+           while ((line = agets(dumpout)) != NULL) {
+               if (line[0] != '\0') {
+                   break;
+               }
+               amfree(line);
+           }
+           if (line != NULL) {
+               dbprintf("%s\n", line);
+               break;
            }
            break;
        }
     }
     amfree(line);
 
-    dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
-    dbprintf(("%s: estimate time for %s level %d: %s\n",
-             debug_prefix(NULL),
-             disk,
+    dbprintf(".....\n");
+    dbprintf(_("estimate time for %s level %d: %s\n"),
+             qdisk,
              level,
-             walltime_str(timessub(curclock(), start_time))));
-    if(size == -1) {
-       dbprintf(("%s: no size line match in %s output for \"%s\"\n",
-                 debug_prefix(NULL), my_argv[0], disk));
-       dbprintf(("%s: .....\n", debug_prefix(NULL)));
-    } else if(size == 0 && level == 0) {
-       dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
-                 debug_prefix(NULL), my_argv[0], disk));
-       dbprintf(("%s: .....\n", debug_prefix(NULL)));
-    }
-    dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
-             debug_prefix(NULL),
-             disk,
+             walltime_str(timessub(curclock(), start_time)));
+    if(size == (off_t)-1) {
+       *errmsg = vstrallocf(_("no size line match in %s output"),
+                            command);
+       dbprintf(_("%s for %s\n"), *errmsg, qdisk);
+       dbprintf(".....\n");
+    } else if(size == (off_t)0 && level == 0) {
+       dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
+                 command, dle->disk);
+       dbprintf(".....\n");
+    }
+    dbprintf(_("estimate size for %s level %d: %lld KB\n"),
+             qdisk,
              level,
-             size));
+             (long long)size);
 
     kill(-dumppid, SIGTERM);
 
-    dbprintf(("%s: waiting for %s \"%s\" child\n",
-             debug_prefix_time(NULL), my_argv[0], disk));
-    wait(NULL);
-    dbprintf(("%s: after %s \"%s\" wait\n",
-             debug_prefix_time(NULL), my_argv[0], disk));
+    dbprintf(_("waiting for %s \"%s\" child\n"),
+            command, qdisk);
+    waitpid(dumppid, &wait_status, 0);
+    if (WIFSIGNALED(wait_status)) {
+       *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
+                            cmd, WTERMSIG(wait_status), dbfn());
+    } else if (WIFEXITED(wait_status)) {
+       if (WEXITSTATUS(wait_status) != 0) {
+           *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
+                                cmd, WEXITSTATUS(wait_status), dbfn());
+       } else {
+           /* Normal exit */
+       }
+    } else {
+       *errmsg = vstrallocf(_("%s got bad exit: see %s"),
+                            cmd, dbfn());
+    }
+    dbprintf(_("after %s %s wait\n"), command, qdisk);
 
 common_exit:
 
@@ -1627,7 +2295,11 @@ common_exit:
     amfree(basename);
     amfree(dirname);
     amfree(inputname);
-    amfree(my_argv);
+    g_ptr_array_free_full(argv_ptr);
+    amfree(qdisk);
+    amfree(cmd);
+    amfree(file_exclude);
+    amfree(file_include);
 
     aclose(nullfd);
     afclose(dumpout);
@@ -1638,12 +2310,276 @@ common_exit:
 }
 #endif
 
+off_t
+getsize_application_api(
+    disk_estimates_t         *est,
+    int                              nb_level,
+    int                             *levels,
+    backup_support_option_t  *bsu)
+{
+    dle_t *dle = est->dle;
+    int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
+    pid_t dumppid;
+    off_t size = (off_t)-1;
+    FILE *dumpout;
+    FILE *dumperr;
+    char *line = NULL;
+    char *cmd = NULL;
+    char *cmdline;
+    guint i;
+    int   j;
+    GPtrArray *argv_ptr = g_ptr_array_new();
+    char *newoptstr = NULL;
+    off_t size1, size2;
+    times_t start_time;
+    char *qdisk = quote_string(dle->disk);
+    char *qamdevice = quote_string(dle->device);
+    amwait_t wait_status;
+    char levelstr[NUM_STR_SIZE];
+    GSList   *scriptlist;
+    script_t *script;
+    char     *errmsg = NULL;
+    estimate_t     estimate;
+    estimatelist_t el;
+
+    cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
+
+    g_ptr_array_add(argv_ptr, stralloc(dle->program));
+    g_ptr_array_add(argv_ptr, stralloc("estimate"));
+    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));
+    }
+    g_ptr_array_add(argv_ptr, stralloc("--device"));
+    g_ptr_array_add(argv_ptr, stralloc(dle->device));
+    if (dle->disk && bsu->disk == 1) {
+       g_ptr_array_add(argv_ptr, stralloc("--disk"));
+       g_ptr_array_add(argv_ptr, stralloc(dle->disk));
+    }
+    for (j=0; j < nb_level; j++) {
+       g_ptr_array_add(argv_ptr, stralloc("--level"));
+       g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
+       g_ptr_array_add(argv_ptr, stralloc(levelstr));
+    }
+    /* find the first in ES_CLIENT and ES_CALCSIZE */
+    estimate = ES_CLIENT;
+    for (el = dle->estimatelist; el != NULL; el = el->next) {
+       estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+       if ((estimate == ES_CLIENT && bsu->client_estimate) ||
+           (estimate == ES_CALCSIZE && bsu->calcsize))
+           break;
+       estimate = ES_CLIENT;
+    }
+    if (estimate == ES_CALCSIZE && bsu->calcsize) {
+       g_ptr_array_add(argv_ptr, stralloc("--calcsize"));
+    }
+
+    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);
+
+    cmdline = stralloc(cmd);
+    for(i = 1; i < argv_ptr->len-1; i++)
+       cmdline = vstrextend(&cmdline, " ",
+                            (char *)g_ptr_array_index(argv_ptr, i), NULL);
+    dbprintf("running: \"%s\"\n", cmdline);
+    amfree(cmdline);
+
+    if (pipe(pipeerrfd) < 0) {
+       errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
+                           strerror(errno));
+       goto common_exit;
+    }
+
+    if (pipe(pipeinfd) < 0) {
+       errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
+                           strerror(errno));
+       goto common_exit;
+    }
+
+    if (pipe(pipeoutfd) < 0) {
+       errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
+                           strerror(errno));
+       goto common_exit;
+    }
+
+    start_time = curclock();
+
+    switch(dumppid = fork()) {
+    case -1:
+      size = (off_t)-1;
+      goto common_exit;
+    default:
+      break; /* parent */
+    case 0:
+      dup2(pipeinfd[0], 0);
+      dup2(pipeoutfd[1], 1);
+      dup2(pipeerrfd[1], 2);
+      aclose(pipeinfd[1]);
+      aclose(pipeoutfd[0]);
+      aclose(pipeerrfd[0]);
+      safe_fd(-1, 0);
+
+      execve(cmd, (char **)argv_ptr->pdata, safe_env());
+      error(_("exec %s failed: %s"), cmd, strerror(errno));
+      /*NOTREACHED*/
+    }
+    amfree(newoptstr);
+
+    aclose(pipeinfd[0]);
+    aclose(pipeoutfd[1]);
+    aclose(pipeerrfd[1]);
+    aclose(pipeinfd[1]);
+
+    dumpout = fdopen(pipeoutfd[0],"r");
+    if (!dumpout) {
+       error(_("Can't fdopen: %s"), strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
+       long long size1_ = (long long)0;
+       long long size2_ = (long long)0;
+       int  level = 0;
+       if (line[0] == '\0')
+           continue;
+       dbprintf("%s\n", line);
+       if (strncmp(line,"ERROR ", 6) == 0) {
+           char *errmsg, *qerrmsg;
+
+           errmsg = stralloc(line+6);
+           qerrmsg = quote_string(errmsg);
+           dbprintf(_("errmsg is %s\n"), errmsg);
+           g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
+           amfree(qerrmsg);
+           continue;
+       }
+       i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
+       if (i != 3) {
+           i = sscanf(line, "%lld %lld", &size1_, &size2_);
+           level = levels[0];
+           if (i != 2) {
+               char *errmsg, *qerrmsg;
+
+               errmsg = vstrallocf(_("bad line %s"), line);
+               qerrmsg = quote_string(errmsg);
+               dbprintf(_("errmsg is %s\n"), errmsg);
+               g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
+               amfree(qerrmsg);
+               continue;
+           }
+       }
+       size1 = (off_t)size1_;
+       size2 = (off_t)size2_;
+       if (size1 <= 0 || size2 <=0)
+           size = -1;
+       else if (size1 * size2 > 0)
+           size = size1 * size2;
+       dbprintf(_("estimate size for %s level %d: %lld KB\n"),
+                qamdevice,
+                level,
+                (long long)size);
+       g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
+    }
+    amfree(line);
+
+    dumperr = fdopen(pipeerrfd[0],"r");
+    if (!dumperr) {
+       error(_("Can't fdopen: %s"), strerror(errno));
+       /*NOTREACHED*/
+    }
+
+    while ((line = agets(dumperr)) != NULL) {
+           if (strlen(line) > 0) {
+           char *err =  g_strdup_printf(_("Application '%s': %s"),
+                                        dle->program, line);
+           char *qerr = quote_string(err);
+           for (j=0; j < nb_level; j++) {
+               fprintf(stdout, "%s %d ERROR %s\n",
+                       est->qamname, levels[j], qerr);
+           }
+           dbprintf("ERROR %s", qerr);
+           amfree(err);
+           amfree(qerr);
+       }
+       amfree(line);
+    }
+
+    dbprintf(".....\n");
+    if (nb_level == 1) {
+       dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
+                levels[0], walltime_str(timessub(curclock(), start_time)));
+    } else {
+       dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
+                walltime_str(timessub(curclock(), start_time)));
+    }
+
+    kill(-dumppid, SIGTERM);
+
+    dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
+    waitpid(dumppid, &wait_status, 0);
+    if (WIFSIGNALED(wait_status)) {
+       errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
+                           cmd, WTERMSIG(wait_status), dbfn());
+    } else if (WIFEXITED(wait_status)) {
+       if (WEXITSTATUS(wait_status) != 0) {
+           errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
+                               WEXITSTATUS(wait_status), dbfn());
+       } else {
+           /* Normal exit */
+       }
+    } else {
+       errmsg = vstrallocf(_("%s got bad exit: see %s"),
+                           cmd, dbfn());
+    }
+    dbprintf(_("after %s %s wait\n"), cmd, qdisk);
+
+    afclose(dumpout);
+    afclose(dumperr);
+
+common_exit:
+
+    amfree(cmd);
+    g_ptr_array_free_full(argv_ptr);
+    amfree(newoptstr);
+    amfree(qdisk);
+    amfree(qamdevice);
+    if (errmsg) {
+       char *qerrmsg = quote_string(errmsg);
+       dbprintf(_("errmsg is %s\n"), errmsg);
+       for (j=0; j < nb_level; j++) {
+           g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
+       }
+       amfree(errmsg);
+       amfree(qerrmsg);
+    }
+    return size;
+}
+
 
-double first_num(str)
-char *str;
 /*
  * Returns the value of the first integer in a string.
  */
+
+double
+first_num(
+    char *     str)
 {
     char *start;
     int ch;
@@ -1655,27 +2591,32 @@ char *str;
     while(isdigit(ch) || (ch == '.')) ch = *str++;
     str[-1] = '\0';
     d = atof(start);
-    str[-1] = ch;
+    str[-1] = (char)ch;
     return d;
 }
 
 
-long handle_dumpline(str)
-char *str;
 /*
  * Checks the dump output line against the error and size regex tables.
  */
+
+off_t
+handle_dumpline(
+    char *     str)
 {
-    regex_t *rp;
+    regex_scale_t *rp;
     double size;
 
     /* check for size match */
+    /*@ignore@*/
     for(rp = re_size; rp->regex != NULL; rp++) {
        if(match(rp->regex, str)) {
            size = ((first_num(str)*rp->scale+1023.0)/1024.0);
-           if(size < 0) size = 1;              /* found on NeXT -- sigh */
-           return (long) size;
+           if(size < 0.0)
+               size = 1.0;             /* found on NeXT -- sigh */
+           return (off_t)size;
        }
     }
-    return -1;
+    /*@end@*/
+    return (off_t)-1;
 }