ea01687d2152fd35a71ecc744230f5273b06dbe2
[debian/amanda] / client-src / sendsize.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* 
27  * $Id: sendsize.c 10421 2008-03-06 18:48:30Z martineau $
28  *
29  * send estimated backup sizes using dump
30  */
31
32 #include "amanda.h"
33 #include "pipespawn.h"
34 #include "amfeatures.h"
35 #include "amandates.h"
36 #include "clock.h"
37 #include "util.h"
38 #include "getfsent.h"
39 #include "version.h"
40 #include "client_util.h"
41 #include "conffile.h"
42 #include "amandad.h"
43
44 #ifdef SAMBA_CLIENT
45 #include "findpass.h"
46 #endif
47
48 #define sendsize_debug(i, ...) do {     \
49         if ((i) <= debug_sebdsize) {    \
50             dbprintf(__VA_ARGS__);      \
51         }                               \
52 } while (0)
53
54 #ifdef HAVE_SETPGID
55 #  define SETPGRP       setpgid(getpid(), getpid())
56 #  define SETPGRP_FAILED() do {                                         \
57     dbprintf(_("setpgid(%ld,%ld) failed: %s\n"),                                \
58               (long)getpid(), (long)getpid(), strerror(errno)); \
59 } while(0)
60
61 #else /* () line 0 */
62 #if defined(SETPGRP_VOID)
63 #  define SETPGRP       setpgrp()
64 #  define SETPGRP_FAILED() do {                                         \
65     dbprintf(_("setpgrp() failed: %s\n"), strerror(errno));             \
66 } while(0)
67
68 #else
69 #  define SETPGRP       setpgrp(0, getpid())
70 #  define SETPGRP_FAILED() do {                                         \
71     dbprintf(_("setpgrp(0,%ld) failed: %s\n"),                          \
72               (long)getpid(), strerror(errno));                 \
73 } while(0)
74
75 #endif
76 #endif
77
78 typedef struct level_estimates_s {
79     time_t dumpsince;
80     int estsize;
81     int needestimate;
82 } level_estimate_t;
83
84 typedef struct disk_estimates_s {
85     struct disk_estimates_s *next;
86     char *qamname;
87     char *qamdevice;
88     char *dirname;
89     char *qdirname;
90     pid_t child;
91     int done;
92     dle_t *dle;
93     level_estimate_t est[DUMP_LEVELS];
94 } disk_estimates_t;
95
96 disk_estimates_t *est_list;
97
98 static am_feature_t *our_features = NULL;
99 static char *our_feature_string = NULL;
100 static g_option_t *g_options = NULL;
101 static gboolean amandates_started = FALSE;
102
103 /* local functions */
104 int main(int argc, char **argv);
105 void dle_add_diskest(dle_t *dle);
106 void calc_estimates(disk_estimates_t *est);
107 void free_estimates(disk_estimates_t *est);
108 void dump_calc_estimates(disk_estimates_t *);
109 void star_calc_estimates(disk_estimates_t *);
110 void smbtar_calc_estimates(disk_estimates_t *);
111 void gnutar_calc_estimates(disk_estimates_t *);
112 void application_api_calc_estimate(disk_estimates_t *);
113 void generic_calc_estimates(disk_estimates_t *);
114
115 int
116 main(
117     int         argc,
118     char **     argv)
119 {
120     int level;
121     char *dumpdate;
122     disk_estimates_t *est;
123     disk_estimates_t *est1;
124     disk_estimates_t *est_prev;
125     char *line = NULL;
126     char *s, *fp;
127     int ch;
128     char *err_extra = NULL;
129     int done;
130     int need_wait;
131     int dumpsrunning;
132     char *qdisk = NULL;
133     char *qlist = NULL;
134     char *qamdevice = NULL;
135     dle_t *dle;
136     GSList *errlist;
137
138     (void)argc; /* Quiet unused parameter warning */
139     (void)argv; /* Quiet unused parameter warning */
140
141     /* initialize */
142
143     /*
144      * Configure program for internationalization:
145      *   1) Only set the message locale for now.
146      *   2) Set textdomain for all amanda related programs to "amanda"
147      *      We don't want to be forced to support dozens of message catalogs.
148      */  
149     setlocale(LC_MESSAGES, "C");
150     textdomain("amanda"); 
151
152     safe_fd(-1, 0);
153     safe_cd();
154
155     set_pname("sendsize");
156
157     /* Don't die when child closes pipe */
158     signal(SIGPIPE, SIG_IGN);
159
160     erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
161     dbopen(DBG_SUBDIR_CLIENT);
162     startclock();
163     dbprintf(_("version %s\n"), version());
164
165     our_features = am_init_feature_set();
166     our_feature_string = am_feature_to_string(our_features);
167
168     config_init(CONFIG_INIT_CLIENT, NULL);
169     /* (check for config errors comes later) */
170
171     check_running_as(RUNNING_AS_CLIENT_LOGIN);
172
173     /* handle all service requests */
174
175     for(; (line = agets(stdin)) != NULL; free(line)) {
176         if (line[0] == '\0')
177             continue;
178         if(strncmp_const(line, "OPTIONS ") == 0) {
179             g_options = parse_g_options(line+8, 1);
180             if(!g_options->hostname) {
181                 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
182                 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
183                 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
184             }
185
186             g_printf("OPTIONS ");
187             if(am_has_feature(g_options->features, fe_rep_options_features)) {
188                 g_printf("features=%s;", our_feature_string);
189             }
190             if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
191                 g_printf("maxdumps=%d;", g_options->maxdumps);
192             }
193             if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
194                 g_printf("hostname=%s;", g_options->hostname);
195             }
196             g_printf("\n");
197             fflush(stdout);
198
199             if (g_options->config) {
200                 /* overlay this configuration on the existing (nameless) configuration */
201                 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
202                             g_options->config);
203
204                 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
205             }
206
207             /* check for any config errors now */
208             if (config_errors(&errlist) >= CFGERR_ERRORS) {
209                 char *errstr = config_errors_to_error_string(errlist);
210                 g_printf("%s\n", errstr);
211                 dbclose();
212                 return 1;
213             }
214
215             if (am_has_feature(g_options->features, fe_req_xml)) {
216                 break;
217             }
218             continue;
219         }
220
221         dle = alloc_dle();
222         s = line;
223         ch = *s++;
224
225         skip_whitespace(s, ch);                 /* find the program name */
226         if(ch == '\0') {
227             err_extra = stralloc(_("no program name"));
228             goto err;                           /* no program name */
229         }
230         dle->program = s - 1;
231         skip_non_whitespace(s, ch);
232         s[-1] = '\0';
233
234         dle->program_is_application_api=0;
235         if(strncmp_const(dle->program, "CALCSIZE") == 0) {
236             skip_whitespace(s, ch);             /* find the program name */
237             if(ch == '\0') {
238                 err_extra = stralloc(_("no program name"));
239                 goto err;
240             }
241             dle->calcsize = 1;
242             dle->program = s - 1;
243             skip_non_whitespace(s, ch);
244             s[-1] = '\0';
245             if (strcmp(dle->program,"APPLICATION") == 0) {
246                 dle->program_is_application_api=1;
247                 skip_whitespace(s, ch);         /* find dumper name */
248                 if (ch == '\0') {
249                     goto err;                   /* no program */
250                 }
251                 dle->program = s - 1;
252                 skip_non_whitespace(s, ch);
253                 s[-1] = '\0';
254             }
255         }
256         else {
257             dle->calcsize = 0;
258             if (strcmp(dle->program,"APPLICATION") == 0) {
259                 dle->program_is_application_api=1;
260                 skip_whitespace(s, ch);         /* find dumper name */
261                 if (ch == '\0') {
262                     goto err;                   /* no program */
263                 }
264                 dle->program = s - 1;
265                 skip_non_whitespace(s, ch);
266                 s[-1] = '\0';
267             }
268         }
269         dle->program = stralloc(dle->program);
270
271         skip_whitespace(s, ch);                 /* find the disk name */
272         if(ch == '\0') {
273             err_extra = stralloc(_("no disk name"));
274             goto err;                           /* no disk name */
275         }
276
277         if (qdisk != NULL)
278             amfree(qdisk);
279
280         fp = s - 1;
281         skip_quoted_string(s, ch);
282         s[-1] = '\0';                           /* terminate the disk name */
283         qdisk = stralloc(fp);
284         dle->disk = unquote_string(qdisk);
285
286         skip_whitespace(s, ch);                 /* find the device or level */
287         if (ch == '\0') {
288             err_extra = stralloc(_("bad level"));
289             goto err;
290         }
291         if(!isdigit((int)s[-1])) {
292             fp = s - 1;
293             skip_quoted_string(s, ch);
294             s[-1] = '\0';
295             qamdevice = stralloc(fp);
296             dle->device = unquote_string(qamdevice);
297             skip_whitespace(s, ch);             /* find level number */
298         }
299         else {
300             dle->device = stralloc(dle->disk);
301             qamdevice = stralloc(qdisk);
302         }
303
304                                                 /* find the level number */
305         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
306             err_extra = stralloc(_("bad level"));
307             goto err;                           /* bad level */
308         }
309         if (level < 0 || level >= DUMP_LEVELS) {
310             err_extra = stralloc(_("bad level"));
311             goto err;
312         }
313         skip_integer(s, ch);
314         dle->level = g_slist_append(dle->level, GINT_TO_POINTER(level));
315
316         skip_whitespace(s, ch);                 /* find the dump date */
317         if(ch == '\0') {
318             err_extra = stralloc(_("no dumpdate"));
319             goto err;                           /* no dumpdate */
320         }
321         dumpdate = s - 1;
322         skip_non_whitespace(s, ch);
323         s[-1] = '\0';
324         (void)dumpdate;                         /* XXX: Set but not used */
325
326         dle->spindle = 0;                       /* default spindle */
327
328         skip_whitespace(s, ch);                 /* find the spindle */
329         if(ch != '\0') {
330             if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
331                 err_extra = stralloc(_("bad spindle"));
332                 goto err;                       /* bad spindle */
333             }
334             skip_integer(s, ch);
335
336             skip_whitespace(s, ch);             /* find the parameters */
337             if(ch != '\0') {
338                 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
339                     parse_options(s + 8,
340                                   dle,
341                                   g_options->features,
342                                   0);
343                 }
344                 else {
345                     while (ch != '\0') {
346                         if(strncmp_const(s-1, "exclude-file=") == 0) {
347                             qlist = unquote_string(s+12);
348                             dle->exclude_file =
349                                 append_sl(dle->exclude_file, qlist);
350                             amfree(qlist);
351                         } else if(strncmp_const(s-1, "exclude-list=") == 0) {
352                             qlist = unquote_string(s+12);
353                             dle->exclude_list =
354                                 append_sl(dle->exclude_list, qlist);
355                             amfree(qlist);
356                         } else if(strncmp_const(s-1, "include-file=") == 0) {
357                             qlist = unquote_string(s+12);
358                             dle->include_file =
359                                 append_sl(dle->include_file, qlist);
360                             amfree(qlist);
361                         } else if(strncmp_const(s-1, "include-list=") == 0) {
362                             qlist = unquote_string(s+12);
363                             dle->include_list =
364                                 append_sl(dle->include_list, qlist);
365                             amfree(qlist);
366                         } else {
367                             err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
368                             goto err;           /* should have gotten to end */
369                         }
370                         skip_quoted_string(s, ch);
371                         skip_whitespace(s, ch); /* find the inclusion list */
372                         amfree(qlist);
373                     }
374                 }
375             }
376         }
377
378         /*@ignore@*/
379         dle_add_diskest(dle);
380         /*@end@*/
381     }
382     if (g_options == NULL) {
383         g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
384         error(_("Missing OPTIONS line in sendsize input\n"));
385         /*NOTREACHED*/
386     }
387     amfree(line);
388
389     if (am_has_feature(g_options->features, fe_req_xml)) {
390         char    *errmsg = NULL;
391         dle_t   *dles, *dle;
392
393         dles = amxml_parse_node_FILE(stdin, &errmsg);
394         if (errmsg) {
395             err_extra = errmsg;
396             goto err;
397         }
398         for (dle = dles; dle != NULL; dle = dle->next) {
399             dle_add_diskest(dle);
400         }
401     }
402
403     if (amandates_started) {
404         finish_amandates();
405         free_amandates();
406         amandates_started = FALSE;
407     }
408
409     for(est = est_list; est != NULL; est = est->next) {
410         run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, g_options, est->dle,
411                            stdout);
412     }
413
414     dumpsrunning = 0;
415     need_wait = 0;
416     done = 0;
417     while(! done) {
418         done = 1;
419         /*
420          * See if we need to wait for a child before we can do anything
421          * else in this pass.
422          */
423         if(need_wait) {
424             pid_t child_pid;
425             amwait_t child_status;
426
427             need_wait = 0;
428             dbprintf(_("waiting for any estimate child: %d running\n"),
429                       dumpsrunning);
430             child_pid = wait(&child_status);
431             if(child_pid == -1) {
432                 error(_("wait failed: %s"), strerror(errno));
433                 /*NOTREACHED*/
434             }
435
436             if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
437                 char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
438                 char *child_status_str = str_exit_status(child_name, child_status);
439                 dbprintf("%s\n", child_status_str);
440                 amfree(child_status_str);
441                 amfree(child_name);
442             }
443
444             /*
445              * Find the child and mark it done.
446              */
447             for(est = est_list; est != NULL; est = est->next) {
448                 if(est->child == child_pid) {
449                     break;
450                 }
451             }
452             if(est == NULL) {
453                 dbprintf(_("unexpected child %ld\n"), (long)child_pid);
454             } else {
455                 est->done = 1;
456                 est->child = 0;
457                 dumpsrunning--;
458                 run_client_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
459                                    est->dle, stdout);
460             }
461         }
462         /*
463          * If we are already running the maximum number of children
464          * go back and wait until one of them finishes.
465          */
466         if(dumpsrunning >= g_options->maxdumps) {
467             done = 0;
468             need_wait = 1;
469             continue;                           /* have to wait first */
470         }
471         /*
472          * Find a new child to start.
473          */
474         for(est = est_list; est != NULL; est = est->next) {
475             if(est->done == 0) {
476                 done = 0;                       /* more to do */
477             }
478             if(est->child != 0 || est->done) {
479                 continue;                       /* child is running or done */
480             }
481             /*
482              * Make sure there is no spindle conflict.
483              */
484             if(est->dle->spindle != -1) {
485                 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
486                     if(est1->child == 0 || est == est1 || est1->done) {
487                         /*
488                          * Ignore anything not yet started, ourself,
489                          * and anything completed.
490                          */
491                         continue;
492                     }
493                     if(est1->dle->spindle == est->dle->spindle) {
494                         break;                  /* oops -- they match */
495                     }
496                 }
497                 if(est1 != NULL) {
498                     continue;                   /* spindle conflict */
499                 }
500             }
501             break;                              /* start this estimate */
502         }
503         if(est == NULL) {
504             if(dumpsrunning > 0) {
505                 need_wait = 1;                  /* nothing to do but wait */
506             }
507         } else {
508             done = 0;
509             run_client_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
510                                est->dle, stdout);
511
512             if((est->child = fork()) == 0) {
513                 calc_estimates(est);            /* child does the estimate */
514                 exit(0);
515             } else if(est->child == -1) {
516                 error(_("calc_estimates fork failed: %s"), strerror(errno));
517                 /*NOTREACHED*/
518             }
519             dumpsrunning++;                     /* parent */
520         }
521     }
522
523     for(est = est_list; est != NULL; est = est->next) {
524         run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, g_options, est->dle,
525                            stdout);
526     }
527
528     est_prev = NULL;
529     for(est = est_list; est != NULL; est = est->next) {
530         free_estimates(est);
531         amfree(est_prev);
532         est_prev = est;
533     }
534     amfree(est_prev);
535     amfree(our_feature_string);
536     am_release_feature_set(our_features);
537     our_features = NULL;
538     free_g_options(g_options);
539
540     dbclose();
541     return 0;
542  err:
543     if (err_extra) {
544         g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
545         dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
546         amfree(err_extra);
547     } else {
548         g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
549         dbprintf(_("REQ packet is bogus\n"));
550     }
551
552     free_g_options(g_options);
553
554     dbclose();
555     return 1;
556 }
557
558
559 void
560 dle_add_diskest(
561     dle_t    *dle)
562 {
563     disk_estimates_t *newp, *curp;
564     amandates_t *amdp;
565     int dumplev, estlev;
566     time_t dumpdate;
567     GSList *level;
568     char *amandates_file;
569     gboolean need_amandates = FALSE;
570
571     level = dle->level;
572     if (level == NULL) {
573         g_printf(_("ERROR Missing level in request\n"));
574         return;
575     }
576
577     /* should we use amandates for this? */
578     if (dle->calcsize)
579         need_amandates = TRUE;
580     if (strcmp(dle->program, "GNUTAR") == 0) {
581         /* GNUTAR only needs amandates if gnutar_list_dir is NULL */
582         char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
583         if (!gnutar_list_dir || !*gnutar_list_dir)
584             need_amandates = TRUE;
585     }
586
587     /* start amandates here, before adding this DLE to est_list, in case
588      * we encounter an error. */
589     if (need_amandates) {
590         if (!amandates_started) {
591             amandates_file = getconf_str(CNF_AMANDATES);
592             if(!start_amandates(amandates_file, 0)) {
593                 char *errstr = strerror(errno);
594                 char *qamname = quote_string(dle->disk);
595                 char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
596                 char *qerrmsg = quote_string(errmsg);
597                 g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
598                 amfree(qamname);
599                 amfree(errmsg);
600                 amfree(qerrmsg);
601                 return;
602             }
603             amandates_started = TRUE;
604         }
605     }
606
607     while (level != NULL) {
608         if (GPOINTER_TO_INT(level->data) < 0)
609             level->data = GINT_TO_POINTER(0);
610         if (GPOINTER_TO_INT(level->data) >= DUMP_LEVELS)
611             level->data = GINT_TO_POINTER(DUMP_LEVELS - 1);
612         level = g_slist_next(level);
613     }
614
615     for(curp = est_list; curp != NULL; curp = curp->next) {
616         if(strcmp(curp->dle->disk, dle->disk) == 0) {
617             /* already have disk info, just note the level request */
618             level = dle->level;
619             while (level != NULL) {
620                 curp->est[GPOINTER_TO_INT(level->data)].needestimate = 1;
621                 level = g_slist_next(level);
622             }
623
624             return;
625         }
626     }
627
628     newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
629     memset(newp, 0, SIZEOF(*newp));
630     newp->next = est_list;
631     est_list = newp;
632     newp->qamname = quote_string(dle->disk);
633     if (dle->device) {
634         newp->qamdevice = quote_string(dle->device);
635         newp->dirname = amname_to_dirname(dle->device);
636         newp->qdirname = quote_string(newp->dirname);
637     } else {
638         newp->qamdevice = stralloc("");
639         newp->dirname = stralloc("");
640         newp->qdirname = stralloc("");
641     }
642     level = dle->level;
643     while (level != NULL) {
644         newp->est[GPOINTER_TO_INT(level->data)].needestimate = 1;
645         level = g_slist_next(level);
646     }
647     newp->dle = dle;
648
649     /* fill in dump-since dates */
650     if (need_amandates) {
651         amdp = amandates_lookup(newp->dle->disk);
652
653         newp->est[0].dumpsince = EPOCH;
654         for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
655             dumpdate = amdp->dates[dumplev];
656             for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
657                 if(dumpdate > newp->est[estlev].dumpsince)
658                     newp->est[estlev].dumpsince = dumpdate;
659             }
660         }
661     } else {
662         /* just zero everything out */
663         for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
664             newp->est[dumplev].dumpsince = 0;
665         }
666     }
667 }
668
669
670 void
671 free_estimates(
672     disk_estimates_t *  est)
673 {
674     amfree(est->qamname);
675     amfree(est->qamdevice);
676     amfree(est->dirname);
677     amfree(est->qdirname);
678     if(est->dle) {
679 /* free DLE */
680     }
681 }
682
683 /*
684  * ------------------------------------------------------------------------
685  *
686  */
687
688 void
689 calc_estimates(
690     disk_estimates_t *  est)
691 {
692     dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
693               est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
694
695     if(est->dle->program_is_application_api ==  1)
696         application_api_calc_estimate(est);
697     else
698     if(est->dle->calcsize ==  1)
699         if (est->dle->device[0] == '/' && est->dle->device[1] == '/')
700             dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
701                      est->qamname, est->qdirname);
702         else
703             generic_calc_estimates(est);
704     else
705 #ifndef USE_GENERIC_CALCSIZE
706     if(strcmp(est->dle->program, "DUMP") == 0)
707         dump_calc_estimates(est);
708     else
709 #endif
710 #ifdef SAMBA_CLIENT
711       if (strcmp(est->dle->program, "GNUTAR") == 0 &&
712           est->dle->device[0] == '/' && est->dle->device[1] == '/')
713         smbtar_calc_estimates(est);
714       else
715 #endif
716 #ifdef GNUTAR
717         if (strcmp(est->dle->program, "GNUTAR") == 0)
718           gnutar_calc_estimates(est);
719         else
720 #endif
721             dbprintf(_("Invalid program: %s %s %s\n"),
722                       est->qamname, est->qdirname, est->dle->program);
723
724     dbprintf(_("done with amname %s dirname %s spindle %d\n"),
725               est->qamname, est->qdirname, est->dle->spindle);
726 }
727
728 /*
729  * ------------------------------------------------------------------------
730  *
731  */
732
733 /* local functions */
734 off_t getsize_dump(dle_t *dle, int level, char **errmsg);
735 off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
736 off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
737 off_t getsize_star(dle_t *dle, int level, time_t dumpsince, char **errmsg);
738 off_t getsize_application_api(disk_estimates_t *est, int nb_level,
739                               int *levels, backup_support_option_t *bsu);
740 off_t handle_dumpline(char *str);
741 double first_num(char *str);
742
743 void
744 application_api_calc_estimate(
745     disk_estimates_t *  est)
746 {
747     int    level;
748     int    i;
749     int    levels[DUMP_LEVELS];
750     int    nb_level = 0;
751     backup_support_option_t *bsu;
752     GPtrArray               *errarray;
753
754     bsu = backup_support_option(est->dle->program, g_options, est->dle->disk,
755                                 est->dle->device, &errarray);
756     if (!bsu) {
757         guint  i;
758         for (i=0; i < errarray->len; i++) {
759             char  *line;
760             char  *errmsg;
761             char  *qerrmsg;
762             line = g_ptr_array_index(errarray, i);
763             if(am_has_feature(g_options->features,
764                               fe_rep_sendsize_quoted_error)) {
765                 errmsg = g_strdup_printf(_("Application '%s': %s"),
766                                      est->dle->program, line);
767                 qerrmsg = quote_string(errmsg);
768                 for (level = 0; level < DUMP_LEVELS; level++) {
769                     if (est->est[level].needestimate) {
770                         g_printf(_("%s %d ERROR %s\n"),
771                                  est->dle->disk, level, qerrmsg);
772                         dbprintf(_("%s %d ERROR A %s\n"),
773                                  est->qamname, level, qerrmsg);
774                     }
775                 }
776                 amfree(errmsg);
777                 amfree(qerrmsg);
778             }
779         }
780         if (i == 0) { /* nothing in errarray */
781             char  *errmsg;
782             char  *qerrmsg;
783             errmsg = g_strdup_printf(
784                 _("Application '%s': cannon execute support command"),
785                 est->dle->program);
786             qerrmsg = quote_string(errmsg);
787             for (level = 0; level < DUMP_LEVELS; level++) {
788                 if (est->est[level].needestimate) {
789                     g_printf(_("%s %d ERROR %s\n"),
790                              est->dle->disk, level, qerrmsg);
791                     dbprintf(_("%s %d ERROR %s\n"),
792                              est->qamname, level, qerrmsg);
793                 }
794             }
795             amfree(errmsg);
796             amfree(qerrmsg);
797         }
798         for (level = 0; level < DUMP_LEVELS; level++) {
799             est->est[level].needestimate = 0;
800         }
801         g_ptr_array_free(errarray, TRUE);
802     }
803
804     for(level = 0; level < DUMP_LEVELS; level++) {
805         if (est->est[level].needestimate) {
806             if (level > bsu->max_level) {
807                 /* planner will not even consider this level */
808                 g_printf("%s %d SIZE %lld\n", est->qamname, level,
809                          (long long)-2);
810                 est->est[level].needestimate = 0;
811             } else if (est->dle->estimate == ES_SERVER) {
812                 /* planner will consider this level, */
813                 /* but use a server-side estimate    */
814                 g_printf("%s %d SIZE %lld\n", est->qamname, level,
815                          (long long)-1);
816                 est->est[level].needestimate = 0;
817             } else {
818                 levels[nb_level++] = level;
819             }
820         }
821     }
822
823     if (nb_level == 0)
824         return;
825
826     if (bsu->multi_estimate) {
827         for (i=0;i<nb_level;i++) {
828             dbprintf(_("getting size via application API for %s %s level %d\n"),
829                      est->qamname, est->qamdevice, levels[i]);
830         }
831         getsize_application_api(est, nb_level, levels, bsu);
832
833     } else {
834         for(level = 0; level < DUMP_LEVELS; level++) {
835             if (est->est[level].needestimate) {
836                 dbprintf(
837                     _("getting size via application API for %s %s level %d\n"),
838                     est->qamname, est->qamdevice, level);
839                 levels[0] = level;
840                 getsize_application_api(est, 1, levels, bsu);
841             }
842         }
843     }
844 }
845
846
847 void
848 generic_calc_estimates(
849     disk_estimates_t *  est)
850 {
851     int pipefd = -1, nullfd = -1;
852     char *cmd;
853     char *cmdline;
854     char *my_argv[DUMP_LEVELS*2+22];
855     char number[NUM_STR_SIZE];
856     int i, level, my_argc;
857     pid_t calcpid;
858     int nb_exclude = 0;
859     int nb_include = 0;
860     char *file_exclude = NULL;
861     char *file_include = NULL;
862     times_t start_time;
863     FILE *dumpout = NULL;
864     off_t size = (off_t)1;
865     char *line = NULL;
866     char *match_expr;
867     amwait_t wait_status;
868     char *errmsg = NULL, *qerrmsg;
869     char tmppath[PATH_MAX];
870     int len;
871
872     cmd = vstralloc(amlibexecdir, "/", "calcsize", versionsuffix(), NULL);
873
874     my_argc = 0;
875
876     my_argv[my_argc++] = stralloc("calcsize");
877     if (g_options->config)
878         my_argv[my_argc++] = stralloc(g_options->config);
879     else
880         my_argv[my_argc++] = stralloc("NOCONFIG");
881
882     my_argv[my_argc++] = stralloc(est->dle->program);
883     canonicalize_pathname(est->dle->disk, tmppath);
884     my_argv[my_argc++] = stralloc(tmppath);
885     canonicalize_pathname(est->dirname, tmppath);
886     my_argv[my_argc++] = stralloc(tmppath);
887
888     if (est->dle->exclude_file)
889         nb_exclude += est->dle->exclude_file->nb_element;
890     if (est->dle->exclude_list)
891         nb_exclude += est->dle->exclude_list->nb_element;
892     if (est->dle->include_file)
893         nb_include += est->dle->include_file->nb_element;
894     if (est->dle->include_list)
895         nb_include += est->dle->include_list->nb_element;
896
897     if (nb_exclude > 0)
898         file_exclude = build_exclude(est->dle, 0);
899     if (nb_include > 0)
900         file_include = build_include(est->dle, 0);
901
902     if(file_exclude) {
903         my_argv[my_argc++] = stralloc("-X");
904         my_argv[my_argc++] = file_exclude;
905     }
906
907     if(file_include) {
908         my_argv[my_argc++] = stralloc("-I");
909         my_argv[my_argc++] = file_include;
910     }
911     start_time = curclock();
912
913     cmdline = stralloc(my_argv[0]);
914     for(i = 1; i < my_argc; i++)
915         cmdline = vstrextend(&cmdline, " ", my_argv[i], NULL);
916     dbprintf(_("running: \"%s\"\n"), cmdline);
917     amfree(cmdline);
918
919     for(level = 0; level < DUMP_LEVELS; level++) {
920         if(est->est[level].needestimate) {
921             g_snprintf(number, SIZEOF(number), "%d", level);
922             my_argv[my_argc++] = stralloc(number); 
923             dbprintf(" %s", number);
924             g_snprintf(number, SIZEOF(number),
925                         "%ld", (long)est->est[level].dumpsince);
926             my_argv[my_argc++] = stralloc(number); 
927             dbprintf(" %s", number);
928         }
929     }
930     my_argv[my_argc] = NULL;
931     dbprintf("\n");
932
933     fflush(stderr); fflush(stdout);
934
935     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
936         errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
937                             strerror(errno));
938         dbprintf("%s\n", errmsg);
939         goto common_exit;
940     }
941
942     calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
943                          &nullfd, &nullfd, &pipefd, my_argv);
944     amfree(cmd);
945
946     dumpout = fdopen(pipefd,"r");
947     if (!dumpout) {
948         error(_("Can't fdopen: %s"), strerror(errno));
949         /*NOTREACHED*/
950     }
951     match_expr = vstralloc(" %d SIZE %lld", NULL);
952     len = strlen(est->qamname);
953     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
954         long long size_ = (long long)0;
955         if (line[0] == '\0' || (int)strlen(line) <= len)
956             continue;
957         /* Don't use sscanf for est->qamname because it can have a '%'. */
958         if (strncmp(line, est->qamname, len) == 0 &&
959             sscanf(line+len, match_expr, &level, &size_) == 2) {
960             g_printf("%s\n", line); /* write to amandad */
961             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
962                       est->qamname,
963                       level,
964                       size_);
965         }
966         size = (off_t)size_;
967     }
968     amfree(match_expr);
969
970     dbprintf(_("waiting for %s %s child (pid=%d)\n"),
971               my_argv[0], est->qamdevice, (int)calcpid);
972     waitpid(calcpid, &wait_status, 0);
973     if (WIFSIGNALED(wait_status)) {
974         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
975                             "calcsize", WTERMSIG(wait_status),
976                             dbfn());
977     } else if (WIFEXITED(wait_status)) {
978         if (WEXITSTATUS(wait_status) != 0) {
979             errmsg = vstrallocf(_("%s exited with status %d: see %s"),
980                                 "calcsize", WEXITSTATUS(wait_status),
981                                 dbfn());
982         } else {
983             /* Normal exit */
984         }
985     } else {
986         errmsg = vstrallocf(_("%s got bad exit: see %s"),
987                              "calcsize", dbfn());
988     }
989     dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
990               my_argv[0], est->qamdevice,
991               (int)calcpid, WEXITSTATUS(wait_status));
992
993     dbprintf(_(".....\n"));
994     dbprintf(_("estimate time for %s: %s\n"),
995               est->qamname,
996               walltime_str(timessub(curclock(), start_time)));
997
998 common_exit:
999     if (errmsg && errmsg[0] != '\0') {
1000         if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
1001             qerrmsg = quote_string(errmsg);
1002             dbprintf(_("errmsg is %s\n"), errmsg);
1003             g_printf("%s %d ERROR %s\n",
1004                     est->qamname, 0, qerrmsg);
1005             amfree(qerrmsg);
1006         }
1007     }
1008     amfree(errmsg);
1009     for(i = 0; i < my_argc; i++) {
1010         amfree(my_argv[i]);
1011     }
1012     amfree(cmd);
1013 }
1014
1015
1016 void
1017 dump_calc_estimates(
1018     disk_estimates_t *  est)
1019 {
1020     int level;
1021     off_t size;
1022     char *errmsg=NULL, *qerrmsg;
1023
1024     for(level = 0; level < DUMP_LEVELS; level++) {
1025         if(est->est[level].needestimate) {
1026             dbprintf(_("getting size via dump for %s level %d\n"),
1027                       est->qamname, level);
1028             size = getsize_dump(est->dle, level, &errmsg);
1029
1030             amflock(1, "size");
1031
1032             g_printf(_("%s %d SIZE %lld\n"),
1033                    est->qamname, level, (long long)size);
1034             if (errmsg && errmsg[0] != '\0') {
1035                 if(am_has_feature(g_options->features,
1036                                   fe_rep_sendsize_quoted_error)) {
1037                     qerrmsg = quote_string(errmsg);
1038                     dbprintf(_("errmsg is %s\n"), errmsg);
1039                     g_printf("%s %d ERROR %s\n",
1040                            est->qamname, level, qerrmsg);
1041                     amfree(qerrmsg);
1042                 }
1043             }
1044             amfree(errmsg);
1045             fflush(stdout);
1046
1047             amfunlock(1, "size");
1048         }
1049     }
1050 }
1051
1052 #ifdef SAMBA_CLIENT
1053 void
1054 smbtar_calc_estimates(
1055     disk_estimates_t *  est)
1056 {
1057     int level;
1058     off_t size;
1059     char  *errmsg = NULL, *qerrmsg;
1060
1061     for(level = 0; level < DUMP_LEVELS; level++) {
1062         if(est->est[level].needestimate) {
1063             dbprintf(_("getting size via smbclient for %s level %d\n"),
1064                       est->qamname, level);
1065             size = getsize_smbtar(est->dle, level, &errmsg);
1066
1067             amflock(1, "size");
1068
1069             g_printf(_("%s %d SIZE %lld\n"),
1070                    est->qamname, level, (long long)size);
1071             if (errmsg && errmsg[0] != '\0') {
1072                 if(am_has_feature(g_options->features,
1073                                   fe_rep_sendsize_quoted_error)) {
1074                     qerrmsg = quote_string(errmsg);
1075                     dbprintf(_("errmsg is %s\n"), errmsg);
1076                     g_printf("%s %d ERROR %s\n",
1077                            est->qamname, level, qerrmsg);
1078                     amfree(qerrmsg);
1079                 }
1080             }
1081             amfree(errmsg);
1082             fflush(stdout);
1083
1084             amfunlock(1, "size");
1085         }
1086     }
1087 }
1088 #endif
1089
1090 #ifdef GNUTAR
1091 void
1092 gnutar_calc_estimates(
1093     disk_estimates_t *  est)
1094 {
1095     int level;
1096     off_t size;
1097     char *errmsg = NULL, *qerrmsg;
1098
1099     for(level = 0; level < DUMP_LEVELS; level++) {
1100         if (est->est[level].needestimate) {
1101             dbprintf(_("getting size via gnutar for %s level %d\n"),
1102                       est->qamname, level);
1103             size = getsize_gnutar(est->dle, level,
1104                                   est->est[level].dumpsince,
1105                                   &errmsg);
1106
1107             amflock(1, "size");
1108
1109             g_printf(_("%s %d SIZE %lld\n"),
1110                    est->qamname, level, (long long)size);
1111             if (errmsg && errmsg[0] != '\0') {
1112                 if(am_has_feature(g_options->features,
1113                                   fe_rep_sendsize_quoted_error)) {
1114                     qerrmsg = quote_string(errmsg);
1115                     dbprintf(_("errmsg is %s\n"), errmsg);
1116                     g_printf(_("%s %d ERROR %s\n"),
1117                            est->qamname, level, qerrmsg);
1118                     amfree(qerrmsg);
1119                 }
1120             }
1121             amfree(errmsg);
1122             fflush(stdout);
1123
1124             amfunlock(1, "size");
1125         }
1126     }
1127 }
1128 #endif
1129
1130 typedef struct regex_s {
1131     char *regex;
1132     int scale;
1133 } regex_scale_t;
1134
1135 /*@ignore@*/
1136 regex_scale_t re_size[] = {
1137 #ifdef DUMP
1138     {"  DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1139     {"  DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1140     {"  DUMP: [Ee]stimated [0-9][0-9]* bytes", 1},              /* Ultrix 4.4 */
1141     {" UFSDUMP: estimated [0-9][0-9]* blocks", 512},            /* NEC EWS-UX */
1142     {"dump: Estimate: [0-9][0-9]* tape blocks", 1024},              /* OSF/1 */
1143     {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1144     {"backup: estimated [0-9][0-9]* 1k blocks", 1024},                /* AIX */
1145     {"backup: estimated [0-9][0-9]* tape blocks", 1024},              /* AIX */
1146     {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024},  /* AIX */
1147     {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024},  /* AIX */
1148     {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1149                                                               /* DU 4.0 dump  */
1150     {"dump: Dumping [0-9][0-9]* bytes, ", 1},                 /* DU 4.0 vdump */
1151     {"DUMP: estimated [0-9][0-9]* KB output", 1024},                  /* HPUX */
1152     {"DUMP: estimated [0-9][0-9]* KB\\.", 1024},                    /* NetApp */
1153     {"  UFSDUMP: estimated [0-9][0-9]* blocks", 512},                /* Sinix */
1154
1155 #ifdef HAVE_DUMP_ESTIMATE
1156     {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1157                                                            /* DU 3.2g dump -E */
1158     {"^[0-9][0-9]* blocks$", 1024},                        /* DU 4.0 dump  -E */
1159     {"^[0-9][0-9]*$", 1},                               /* Solaris ufsdump -S */
1160 #endif
1161 #endif
1162
1163 #ifdef VDUMP
1164     {"vdump: Dumping [0-9][0-9]* bytes, ", 1},                 /* OSF/1 vdump */
1165 #endif
1166     
1167 #ifdef VXDUMP
1168     {"vxdump: estimated [0-9][0-9]* blocks", 512},           /* HPUX's vxdump */
1169     {"  VXDUMP: estimated [0-9][0-9]* blocks", 512},                 /* Sinix */
1170 #endif
1171
1172 #ifdef XFSDUMP
1173     {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1},  /* Irix 6.2 xfs */
1174 #endif
1175
1176 #ifdef GNUTAR
1177     {"Total bytes written: [0-9][0-9]*", 1},                /* Gnutar client */
1178 #endif
1179
1180 #ifdef SAMBA_CLIENT
1181 #if SAMBA_VERSION >= 2
1182 #define SAMBA_DEBUG_LEVEL "0"
1183     {"Total number of bytes: [0-9][0-9]*", 1},                   /* Samba du */
1184 #else
1185 #define SAMBA_DEBUG_LEVEL "3"
1186     {"Total bytes listed: [0-9][0-9]*", 1},                     /* Samba dir */
1187 #endif
1188 #endif
1189
1190     { NULL, 0 }
1191 };
1192 /*@end@*/
1193
1194 off_t
1195 getsize_dump(
1196     dle_t      *dle,
1197     int         level,
1198     char      **errmsg)
1199 {
1200     int pipefd[2], nullfd, stdoutfd, killctl[2];
1201     pid_t dumppid;
1202     off_t size;
1203     FILE *dumpout;
1204     char *dumpkeys = NULL;
1205     char *device = NULL;
1206     char *fstype = NULL;
1207     char *cmd = NULL;
1208     char *name = NULL;
1209     char *line = NULL;
1210     char *rundump_cmd = NULL;
1211     char level_str[NUM_STR_SIZE];
1212     int s;
1213     times_t start_time;
1214     char *qdisk = quote_string(dle->disk);
1215     char *qdevice;
1216     char *config;
1217     amwait_t wait_status;
1218 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1219     int is_rundump = 1;
1220 #endif
1221
1222     if (level > 9)
1223         return -2; /* planner will not even consider this level */
1224     if (dle->estimate == ES_SERVER)
1225         return -1; /* planner will consider this level, */
1226                    /* but use a server-side estimate    */
1227
1228     g_snprintf(level_str, SIZEOF(level_str), "%d", level);
1229
1230     device = amname_to_devname(dle->device);
1231     qdevice = quote_string(device);
1232     fstype = amname_to_fstype(dle->device);
1233
1234     dbprintf(_("calculating for device %s with %s\n"),
1235               qdevice, fstype);
1236
1237     cmd = vstralloc(amlibexecdir, "/rundump", versionsuffix(), NULL);
1238     rundump_cmd = stralloc(cmd);
1239     if (g_options->config)
1240         config = g_options->config;
1241     else
1242         config = "NOCONFIG";
1243     if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1244         *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
1245                              strerror(errno));
1246         dbprintf("%s\n", *errmsg);
1247         amfree(cmd);
1248         amfree(rundump_cmd);
1249         amfree(fstype);
1250         amfree(device);
1251         amfree(qdevice);
1252         amfree(qdisk);
1253         return(-1);
1254     }
1255     pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1256     if (pipe(pipefd) < 0) {
1257         *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
1258                              strerror(errno));
1259         dbprintf("%s\n", *errmsg);
1260         amfree(cmd);
1261         amfree(rundump_cmd);
1262         amfree(fstype);
1263         amfree(device);
1264         amfree(qdevice);
1265         amfree(qdisk);
1266         return(-1);
1267     }
1268
1269 #ifdef XFSDUMP                                          /* { */
1270 #ifdef DUMP                                             /* { */
1271     if (strcmp(fstype, "xfs") == 0)
1272 #else                                                   /* } { */
1273     if (1)
1274 #endif                                                  /* } */
1275     {
1276         name = stralloc(" (xfsdump)");
1277         dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
1278                   cmd, name, level_str, qdevice);
1279     }
1280     else
1281 #endif                                                  /* } */
1282 #ifdef VXDUMP                                           /* { */
1283 #ifdef DUMP                                             /* { */
1284     if (strcmp(fstype, "vxfs") == 0)
1285 #else                                                   /* } { */
1286     if (1)
1287 #endif                                                  /* } */
1288     {
1289 #ifdef USE_RUNDUMP
1290         name = stralloc(" (vxdump)");
1291 #else
1292         name = stralloc("");
1293         cmd = newstralloc(cmd, VXDUMP);
1294         config = skip_argument;
1295         is_rundump = 0;
1296 #endif
1297         dumpkeys = vstralloc(level_str, "s", "f", NULL);
1298         dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1299                   cmd, name, dumpkeys, qdevice);
1300     }
1301     else
1302 #endif                                                  /* } */
1303 #ifdef VDUMP                                            /* { */
1304 #ifdef DUMP                                             /* { */
1305     if (strcmp(fstype, "advfs") == 0)
1306 #else                                                   /* } { */
1307     if (1)
1308 #endif                                                  /* } */
1309     {
1310         name = stralloc(" (vdump)");
1311         dumpkeys = vstralloc(level_str, "b", "f", NULL);
1312         dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1313                   cmd, name, dumpkeys, qdevice);
1314     }
1315     else
1316 #endif                                                  /* } */
1317 #ifdef DUMP                                             /* { */
1318     if (1) {
1319 # ifdef USE_RUNDUMP                                     /* { */
1320 #  ifdef AIX_BACKUP                                     /* { */
1321         name = stralloc(" (backup)");
1322 #  else                                                 /* } { */
1323         name = vstralloc(" (", DUMP, ")", NULL);
1324 #  endif                                                /* } */
1325 # else                                                  /* } { */
1326         name = stralloc("");
1327         cmd = newstralloc(cmd, DUMP);
1328         config = skip_argument;
1329         is_rundump = 0;
1330 # endif                                                 /* } */
1331
1332 # ifdef AIX_BACKUP                                      /* { */
1333         dumpkeys = vstralloc("-", level_str, "f", NULL);
1334         dbprintf(_("running \"%s%s %s - %s\"\n"),
1335                   cmd, name, dumpkeys, qdevice);
1336 # else                                                  /* } { */
1337 #  ifdef HAVE_DUMP_ESTIMATE
1338 #    define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1339 #  else
1340 #    define PARAM_DUMP_ESTIMATE ""
1341 #  endif
1342 #  ifdef HAVE_HONOR_NODUMP
1343 #    define PARAM_HONOR_NODUMP "h"
1344 #  else
1345 #    define PARAM_HONOR_NODUMP ""
1346 #  endif
1347         dumpkeys = vstralloc(level_str,
1348                              PARAM_DUMP_ESTIMATE,
1349                              PARAM_HONOR_NODUMP,
1350                              "s", "f", NULL);
1351
1352 #  ifdef HAVE_DUMP_ESTIMATE
1353         stdoutfd = pipefd[1];
1354 #  endif
1355
1356 #  ifdef HAVE_HONOR_NODUMP                              /* { */
1357         dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1358                   cmd, name, dumpkeys, qdevice);
1359 #  else                                                 /* } { */
1360         dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1361                   cmd, name, dumpkeys, qdevice);
1362 #  endif                                                /* } */
1363 # endif                                                 /* } */
1364     }
1365     else
1366 #endif                                                  /* } */
1367     {
1368         error(_("no dump program available"));
1369         /*NOTREACHED*/
1370     }
1371
1372     if (pipe(killctl) < 0) {
1373         dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1374         /* Message will be printed later... */
1375         killctl[0] = killctl[1] = -1;
1376     }
1377
1378     start_time = curclock();
1379     switch(dumppid = fork()) {
1380     case -1:
1381         *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1382                              strerror(errno));
1383         dbprintf("%s\n", *errmsg);
1384         amfree(dumpkeys);
1385         amfree(cmd);
1386         amfree(rundump_cmd);
1387         amfree(device);
1388         amfree(qdevice);
1389         amfree(qdisk);
1390         amfree(name);
1391         amfree(fstype);
1392         return -1;
1393     default:
1394         break; 
1395     case 0:     /* child process */
1396         if(SETPGRP == -1)
1397             SETPGRP_FAILED();
1398         else if (killctl[0] == -1 || killctl[1] == -1)
1399             dbprintf(_("Trying without killpgrp\n"));
1400         else {
1401             switch(fork()) {
1402             case -1:
1403                 dbprintf(_("fork failed, trying without killpgrp\n"));
1404                 break;
1405
1406             default:
1407             {
1408                 char *config;
1409                 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp",
1410                                                versionsuffix(), NULL);
1411                 dbprintf(_("running %s\n"), killpgrp_cmd);
1412                 dup2(killctl[0], 0);
1413                 dup2(nullfd, 1);
1414                 dup2(nullfd, 2);
1415                 close(pipefd[0]);
1416                 close(pipefd[1]);
1417                 close(killctl[1]);
1418                 close(nullfd);
1419                 if (g_options->config)
1420                     config = g_options->config;
1421                 else
1422                     config = "NOCONFIG";
1423                 safe_fd(-1, 0);
1424                 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1425                        safe_env());
1426                 dbprintf(_("cannot execute %s: %s\n"),
1427                           killpgrp_cmd, strerror(errno));
1428                 exit(-1);
1429             }
1430
1431             case 0:  /* child process */
1432                 break;
1433             }
1434         }
1435
1436         dup2(nullfd, 0);
1437         dup2(stdoutfd, 1);
1438         dup2(pipefd[1], 2);
1439         aclose(pipefd[0]);
1440         if (killctl[0] != -1)
1441             aclose(killctl[0]);
1442         if (killctl[1] != -1)
1443             aclose(killctl[1]);
1444         safe_fd(-1, 0);
1445
1446 #ifdef XFSDUMP
1447 #ifdef DUMP
1448         if (strcmp(fstype, "xfs") == 0)
1449 #else
1450         if (1)
1451 #endif
1452             if (is_rundump)
1453                 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1454                        level_str, "-", device, (char *)0, safe_env());
1455             else
1456                 execle(cmd, "xfsdump", "-F", "-J", "-l",
1457                        level_str, "-", device, (char *)0, safe_env());
1458         else
1459 #endif
1460 #ifdef VXDUMP
1461 #ifdef DUMP
1462         if (strcmp(fstype, "vxfs") == 0)
1463 #else
1464         if (1)
1465 #endif
1466             if (is_rundump)
1467                 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1468                        "-", device, (char *)0, safe_env());
1469             else
1470                 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1471                        device, (char *)0, safe_env());
1472         else
1473 #endif
1474 #ifdef VDUMP
1475 #ifdef DUMP
1476         if (strcmp(fstype, "advfs") == 0)
1477 #else
1478         if (1)
1479 #endif
1480             if (is_rundump)
1481                 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1482                        device, (char *)0, safe_env());
1483             else
1484                 execle(cmd, "vdump", dumpkeys, "60", "-",
1485                        device, (char *)0, safe_env());
1486         else
1487 #endif
1488 #ifdef DUMP
1489 # ifdef AIX_BACKUP
1490             if (is_rundump)
1491                 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1492                        device, (char *)0, safe_env());
1493             else
1494                 execle(cmd, "backup", dumpkeys, "-",
1495                        device, (char *)0, safe_env());
1496 # else
1497             if (is_rundump) {
1498                 execle(cmd, "rundump", config, "dump", dumpkeys, 
1499 #ifdef HAVE_HONOR_NODUMP
1500                        "0",
1501 #endif
1502                        "1048576", "-", device, (char *)0, safe_env());
1503             } else {
1504                 execle(cmd, "dump", dumpkeys, 
1505 #ifdef HAVE_HONOR_NODUMP
1506                        "0",
1507 #endif
1508                        "1048576", "-", device, (char *)0, safe_env());
1509             }
1510 # endif
1511 #endif
1512         {
1513             error(_("exec %s failed or no dump program available: %s"),
1514                   cmd, strerror(errno));
1515             /*NOTREACHED*/
1516         }
1517     }
1518
1519     amfree(dumpkeys);
1520     amfree(rundump_cmd);
1521
1522     aclose(pipefd[1]);
1523     if (killctl[0] != -1)
1524         aclose(killctl[0]);
1525     dumpout = fdopen(pipefd[0],"r");
1526     if (!dumpout) {
1527         error(_("Can't fdopen: %s"), strerror(errno));
1528         /*NOTREACHED*/
1529     }
1530
1531     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1532         if (line[0] == '\0')
1533             continue;
1534         dbprintf("%s\n", line);
1535         size = handle_dumpline(line);
1536         if(size > (off_t)-1) {
1537             amfree(line);
1538             while ((line = agets(dumpout)) != NULL) {
1539                 if (line[0] != '\0')
1540                     break;
1541                 amfree(line);
1542             }
1543             if (line != NULL) {
1544                 dbprintf("%s\n", line);
1545             }
1546             break;
1547         }
1548     }
1549     amfree(line);
1550
1551     dbprintf(".....\n");
1552     dbprintf(_("estimate time for %s level %d: %s\n"),
1553               qdisk,
1554               level,
1555               walltime_str(timessub(curclock(), start_time)));
1556     if(size == (off_t)-1) {
1557         *errmsg = vstrallocf(_("no size line match in %s%s output"),
1558                              cmd, name);
1559         dbprintf(_("%s for %s\n"),
1560                   *errmsg, qdisk);
1561
1562         dbprintf(".....\n");
1563         dbprintf(_("Run %s%s manually to check for errors\n"),
1564                     cmd, name);
1565     } else if(size == (off_t)0 && level == 0) {
1566         dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1567                   cmd, name, dle->disk);
1568         dbprintf(".....\n");
1569     } else {
1570             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1571               qdisk,
1572               level,
1573               (long long)size);
1574     }
1575
1576     if (killctl[1] != -1) {
1577         dbprintf(_("asking killpgrp to terminate\n"));
1578         aclose(killctl[1]);
1579         for(s = 5; s > 0; --s) {
1580             sleep(1);
1581             if (waitpid(dumppid, NULL, WNOHANG) != -1)
1582                 goto terminated;
1583         }
1584     }
1585     
1586     /*
1587      * First, try to kill the dump process nicely.  If it ignores us
1588      * for several seconds, hit it harder.
1589      */
1590     dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1591     if (kill(-dumppid, SIGTERM) == -1) {
1592         dbprintf(_("kill failed: %s\n"), strerror(errno));
1593     }
1594     /* Now check whether it dies */
1595     for(s = 5; s > 0; --s) {
1596         sleep(1);
1597         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1598             goto terminated;
1599     }
1600
1601     dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1602     if (kill(-dumppid, SIGKILL) == -1) {
1603         dbprintf(_("kill failed: %s\n"), strerror(errno));
1604     }
1605     for(s = 5; s > 0; --s) {
1606         sleep(1);
1607         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1608             goto terminated;
1609     }
1610
1611     dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1612     waitpid(dumppid, &wait_status, 0);
1613     if (WIFSIGNALED(wait_status)) {
1614         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1615                              cmd, WTERMSIG(wait_status), dbfn());
1616     } else if (WIFEXITED(wait_status)) {
1617         if (WEXITSTATUS(wait_status) != 0) {
1618             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1619                                  cmd, WEXITSTATUS(wait_status), dbfn());
1620         } else {
1621             /* Normal exit */
1622         }
1623     } else {
1624         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1625                              cmd, dbfn());
1626     }
1627     dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1628
1629  terminated:
1630
1631     aclose(nullfd);
1632     afclose(dumpout);
1633
1634     amfree(device);
1635     amfree(qdevice);
1636     amfree(qdisk);
1637     amfree(fstype);
1638
1639     amfree(cmd);
1640     amfree(name);
1641
1642     return size;
1643 }
1644
1645 #ifdef SAMBA_CLIENT
1646 off_t
1647 getsize_smbtar(
1648     dle_t      *dle,
1649     int         level,
1650     char      **errmsg)
1651 {
1652     int pipefd = -1, nullfd = -1, passwdfd = -1;
1653     pid_t dumppid;
1654     off_t size;
1655     FILE *dumpout;
1656     char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1657     char *share = NULL, *subdir = NULL;
1658     size_t lpass;
1659     char *pwtext;
1660     size_t pwtext_len;
1661     char *line;
1662     char *pw_fd_env;
1663     times_t start_time;
1664     char *error_pn = NULL;
1665     char *qdisk = quote_string(dle->disk);
1666     amwait_t wait_status;
1667
1668     error_pn = stralloc2(get_pname(), "-smbclient");
1669
1670     if (level > 1)
1671         return -2; /* planner will not even consider this level */
1672     if (dle->estimate == ES_SERVER)
1673         return -1; /* planner will consider this level, */
1674                    /* but use a server-side estimate    */
1675
1676     parsesharename(dle->device, &share, &subdir);
1677     if (!share) {
1678         amfree(share);
1679         amfree(subdir);
1680         set_pname(error_pn);
1681         amfree(error_pn);
1682         error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1683         /*NOTREACHED*/
1684     }
1685     if ((subdir) && (SAMBA_VERSION < 2)) {
1686         amfree(share);
1687         amfree(subdir);
1688         set_pname(error_pn);
1689         amfree(error_pn);
1690         error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1691         /*NOTREACHED*/
1692     }
1693     if ((user_and_password = findpass(share, &domain)) == NULL) {
1694
1695         if(domain) {
1696             memset(domain, '\0', strlen(domain));
1697             amfree(domain);
1698         }
1699         set_pname(error_pn);
1700         amfree(error_pn);
1701         error(_("cannot find password for %s"), dle->disk);
1702         /*NOTREACHED*/
1703     }
1704     lpass = strlen(user_and_password);
1705     if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1706         memset(user_and_password, '\0', (size_t)lpass);
1707         amfree(user_and_password);
1708         if(domain) {
1709             memset(domain, '\0', strlen(domain));
1710             amfree(domain);
1711         }
1712         set_pname(error_pn);
1713         amfree(error_pn);
1714         error(_("password field not \'user%%pass\' for %s"), dle->disk);
1715         /*NOTREACHED*/
1716     }
1717     *pwtext++ = '\0';
1718     pwtext_len = strlen(pwtext);
1719     if ((sharename = makesharename(share, 0)) == NULL) {
1720         memset(user_and_password, '\0', (size_t)lpass);
1721         amfree(user_and_password);
1722         if(domain) {
1723             memset(domain, '\0', strlen(domain));
1724             amfree(domain);
1725         }
1726         set_pname(error_pn);
1727         amfree(error_pn);
1728         error(_("cannot make share name of %s"), share);
1729         /*NOTREACHED*/
1730     }
1731     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1732         memset(user_and_password, '\0', (size_t)lpass);
1733         amfree(user_and_password);
1734         if(domain) {
1735             memset(domain, '\0', strlen(domain));
1736             amfree(domain);
1737         }
1738         set_pname(error_pn);
1739         amfree(error_pn);
1740         amfree(sharename);
1741         error(_("could not open /dev/null: %s\n"),
1742               strerror(errno));
1743         /*NOTREACHED*/
1744     }
1745
1746 #if SAMBA_VERSION >= 2
1747     if (level == 0)
1748         tarkeys = "archive 0;recurse;du";
1749     else
1750         tarkeys = "archive 1;recurse;du";
1751 #else
1752     if (level == 0)
1753         tarkeys = "archive 0;recurse;dir";
1754     else
1755         tarkeys = "archive 1;recurse;dir";
1756 #endif
1757
1758     start_time = curclock();
1759
1760     if (pwtext_len > 0) {
1761         pw_fd_env = "PASSWD_FD";
1762     } else {
1763         pw_fd_env = "dummy_PASSWD_FD";
1764     }
1765     dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
1766               &nullfd, &nullfd, &pipefd, 
1767               pw_fd_env, &passwdfd,
1768               "smbclient",
1769               sharename,
1770               "-d", SAMBA_DEBUG_LEVEL,
1771               *user_and_password ? "-U" : skip_argument,
1772               *user_and_password ? user_and_password : skip_argument,
1773               "-E",
1774               domain ? "-W" : skip_argument,
1775               domain ? domain : skip_argument,
1776 #if SAMBA_VERSION >= 2
1777               subdir ? "-D" : skip_argument,
1778               subdir ? subdir : skip_argument,
1779 #endif
1780               "-c", tarkeys,
1781               NULL);
1782     if(domain) {
1783         memset(domain, '\0', strlen(domain));
1784         amfree(domain);
1785     }
1786     aclose(nullfd);
1787     if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
1788         int save_errno = errno;
1789
1790         memset(user_and_password, '\0', (size_t)lpass);
1791         amfree(user_and_password);
1792         aclose(passwdfd);
1793         set_pname(error_pn);
1794         amfree(error_pn);
1795         error(_("password write failed: %s"), strerror(save_errno));
1796         /*NOTREACHED*/
1797     }
1798     memset(user_and_password, '\0', (size_t)lpass);
1799     amfree(user_and_password);
1800     aclose(passwdfd);
1801     amfree(sharename);
1802     amfree(share);
1803     amfree(subdir);
1804     amfree(error_pn);
1805     dumpout = fdopen(pipefd,"r");
1806     if (!dumpout) {
1807         error(_("Can't fdopen: %s"), strerror(errno));
1808         /*NOTREACHED*/
1809     }
1810
1811     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1812         if (line[0] == '\0')
1813             continue;
1814         dbprintf("%s\n", line);
1815         size = handle_dumpline(line);
1816         if(size > -1) {
1817             amfree(line);
1818             while ((line = agets(dumpout)) != NULL) {
1819                 if (line[0] != '\0')
1820                     break;
1821                 amfree(line);
1822             }
1823             if(line != NULL) {
1824                 dbprintf("%s\n", line);
1825             }
1826             break;
1827         }
1828     }
1829     amfree(line);
1830
1831     dbprintf(".....\n");
1832     dbprintf(_("estimate time for %s level %d: %s\n"),
1833               qdisk,
1834               level,
1835               walltime_str(timessub(curclock(), start_time)));
1836     if(size == (off_t)-1) {
1837         *errmsg = vstrallocf(_("no size line match in %s output"),
1838                              SAMBA_CLIENT);
1839         dbprintf(_("%s for %s\n"),
1840                   *errmsg, qdisk);
1841         dbprintf(".....\n");
1842     } else if(size == (off_t)0 && level == 0) {
1843         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1844                   SAMBA_CLIENT, dle->disk);
1845         dbprintf(".....\n");
1846     }
1847     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1848               qdisk,
1849               level,
1850               (long long)size);
1851
1852     kill(-dumppid, SIGTERM);
1853
1854     dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1855     waitpid(dumppid, &wait_status, 0);
1856     if (WIFSIGNALED(wait_status)) {
1857         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1858                              SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
1859     } else if (WIFEXITED(wait_status)) {
1860         if (WEXITSTATUS(wait_status) != 0) {
1861             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1862                                  SAMBA_CLIENT, WEXITSTATUS(wait_status),
1863                                  dbfn());
1864         } else {
1865             /* Normal exit */
1866         }
1867     } else {
1868         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1869                              SAMBA_CLIENT, dbfn());
1870     }
1871     dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
1872
1873     afclose(dumpout);
1874     pipefd = -1;
1875
1876     amfree(error_pn);
1877     amfree(qdisk);
1878
1879     return size;
1880 }
1881 #endif
1882
1883 #ifdef GNUTAR
1884 off_t
1885 getsize_gnutar(
1886     dle_t      *dle,
1887     int         level,
1888     time_t      dumpsince,
1889     char      **errmsg)
1890 {
1891     int pipefd = -1, nullfd = -1;
1892     pid_t dumppid;
1893     off_t size = (off_t)-1;
1894     FILE *dumpout = NULL;
1895     char *incrname = NULL;
1896     char *basename = NULL;
1897     char *dirname = NULL;
1898     char *inputname = NULL;
1899     FILE *in = NULL;
1900     FILE *out = NULL;
1901     char *line = NULL;
1902     char *cmd = NULL;
1903     char dumptimestr[80];
1904     struct tm *gmtm;
1905     int nb_exclude = 0;
1906     int nb_include = 0;
1907     char **my_argv;
1908     int i;
1909     char *file_exclude = NULL;
1910     char *file_include = NULL;
1911     times_t start_time;
1912     int infd, outfd;
1913     ssize_t nb;
1914     char buf[32768];
1915     char *qdisk = quote_string(dle->disk);
1916     char *gnutar_list_dir;
1917     amwait_t wait_status;
1918     char tmppath[PATH_MAX];
1919
1920     if (level > 9)
1921         return -2; /* planner will not even consider this level */
1922     if (dle->estimate == ES_SERVER)
1923         return -1; /* planner will consider this level, */
1924                    /* but use a server-side estimate    */
1925
1926     if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
1927     if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
1928     if(dle->include_file) nb_include += dle->include_file->nb_element;
1929     if(dle->include_list) nb_include += dle->include_list->nb_element;
1930
1931     if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
1932     if(nb_include > 0) file_include = build_include(dle, 0);
1933
1934     my_argv = alloc(SIZEOF(char *) * 22);
1935     i = 0;
1936
1937     gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
1938     if (strlen(gnutar_list_dir) == 0)
1939         gnutar_list_dir = NULL;
1940     if (gnutar_list_dir) {
1941         char number[NUM_STR_SIZE];
1942         int baselevel;
1943         char *sdisk = sanitise_filename(dle->disk);
1944
1945         basename = vstralloc(gnutar_list_dir,
1946                              "/",
1947                              g_options->hostname,
1948                              sdisk,
1949                              NULL);
1950         amfree(sdisk);
1951
1952         g_snprintf(number, SIZEOF(number), "%d", level);
1953         incrname = vstralloc(basename, "_", number, ".new", NULL);
1954         unlink(incrname);
1955
1956         /*
1957          * Open the listed incremental file from the previous level.  Search
1958          * backward until one is found.  If none are found (which will also
1959          * be true for a level 0), arrange to read from /dev/null.
1960          */
1961         baselevel = level;
1962         infd = -1;
1963         while (infd == -1) {
1964             if (--baselevel >= 0) {
1965                 g_snprintf(number, SIZEOF(number), "%d", baselevel);
1966                 inputname = newvstralloc(inputname,
1967                                          basename, "_", number, NULL);
1968             } else {
1969                 inputname = newstralloc(inputname, "/dev/null");
1970             }
1971             if ((infd = open(inputname, O_RDONLY)) == -1) {
1972
1973                 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
1974                                      inputname, strerror(errno));
1975                 dbprintf("%s\n", *errmsg);
1976                 if (baselevel < 0) {
1977                     goto common_exit;
1978                 }
1979                 amfree(*errmsg);
1980             }
1981         }
1982
1983         /*
1984          * Copy the previous listed incremental file to the new one.
1985          */
1986         if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1987             *errmsg = vstrallocf(_("opening %s: %s"),
1988                                  incrname, strerror(errno));
1989             dbprintf("%s\n", *errmsg);
1990             goto common_exit;
1991         }
1992
1993         while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1994             if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
1995                 *errmsg = vstrallocf(_("writing to %s: %s"),
1996                                      incrname, strerror(errno));
1997                 dbprintf("%s\n", *errmsg);
1998                 goto common_exit;
1999             }
2000         }
2001
2002         if (nb < 0) {
2003             *errmsg = vstrallocf(_("reading from %s: %s"),
2004                                  inputname, strerror(errno));
2005             dbprintf("%s\n", *errmsg);
2006             goto common_exit;
2007         }
2008
2009         if (close(infd) != 0) {
2010             *errmsg = vstrallocf(_("closing %s: %s"),
2011                                  inputname, strerror(errno));
2012             dbprintf("%s\n", *errmsg);
2013             goto common_exit;
2014         }
2015         if (close(outfd) != 0) {
2016             *errmsg = vstrallocf(_("closing %s: %s"),
2017                                  incrname, strerror(errno));
2018             dbprintf("%s\n", *errmsg);
2019             goto common_exit;
2020         }
2021
2022         amfree(inputname);
2023         amfree(basename);
2024     }
2025
2026     gmtm = gmtime(&dumpsince);
2027     g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2028                 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2029                 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2030                 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2031
2032     dirname = amname_to_dirname(dle->device);
2033
2034     cmd = vstralloc(amlibexecdir, "/", "runtar", versionsuffix(), NULL);
2035     my_argv[i++] = "runtar";
2036     if (g_options->config)
2037         my_argv[i++] = g_options->config;
2038     else
2039         my_argv[i++] = "NOCONFIG";
2040
2041 #ifdef GNUTAR
2042     my_argv[i++] = GNUTAR;
2043 #else
2044     my_argv[i++] = "tar";
2045 #endif
2046     my_argv[i++] = "--create";
2047     my_argv[i++] = "--file";
2048     my_argv[i++] = "/dev/null";
2049     /* use --numeric-owner for estimates, to reduce the number of user/group
2050      * lookups required */
2051     my_argv[i++] = "--numeric-owner";
2052     my_argv[i++] = "--directory";
2053     canonicalize_pathname(dirname, tmppath);
2054     my_argv[i++] = tmppath;
2055     my_argv[i++] = "--one-file-system";
2056     if (gnutar_list_dir) {
2057             my_argv[i++] = "--listed-incremental";
2058             my_argv[i++] = incrname;
2059     } else {
2060         my_argv[i++] = "--incremental";
2061         my_argv[i++] = "--newer";
2062         my_argv[i++] = dumptimestr;
2063     }
2064 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2065     /* --atime-preserve causes gnutar to call
2066      * utime() after reading files in order to
2067      * adjust their atime.  However, utime()
2068      * updates the file's ctime, so incremental
2069      * dumps will think the file has changed. */
2070     my_argv[i++] = "--atime-preserve";
2071 #endif
2072     my_argv[i++] = "--sparse";
2073     my_argv[i++] = "--ignore-failed-read";
2074     my_argv[i++] = "--totals";
2075
2076     if(file_exclude) {
2077         my_argv[i++] = "--exclude-from";
2078         my_argv[i++] = file_exclude;
2079     }
2080
2081     if(file_include) {
2082         my_argv[i++] = "--files-from";
2083         my_argv[i++] = file_include;
2084     }
2085     else {
2086         my_argv[i++] = ".";
2087     }
2088     my_argv[i++] = NULL;
2089
2090     start_time = curclock();
2091
2092     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2093         *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2094                              strerror(errno));
2095         dbprintf("%s\n", *errmsg);
2096         goto common_exit;
2097     }
2098
2099     dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
2100                          &nullfd, &nullfd, &pipefd, my_argv);
2101
2102     dumpout = fdopen(pipefd,"r");
2103     if (!dumpout) {
2104         error(_("Can't fdopen: %s"), strerror(errno));
2105         /*NOTREACHED*/
2106     }
2107
2108     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2109         if (line[0] == '\0')
2110             continue;
2111         dbprintf("%s\n", line);
2112         size = handle_dumpline(line);
2113         if(size > (off_t)-1) {
2114             amfree(line);
2115             while ((line = agets(dumpout)) != NULL) {
2116                 if (line[0] != '\0') {
2117                     break;
2118                 }
2119                 amfree(line);
2120             }
2121             if (line != NULL) {
2122                 dbprintf("%s\n", line);
2123                 break;
2124             }
2125             break;
2126         }
2127     }
2128     amfree(line);
2129
2130     dbprintf(".....\n");
2131     dbprintf(_("estimate time for %s level %d: %s\n"),
2132               qdisk,
2133               level,
2134               walltime_str(timessub(curclock(), start_time)));
2135     if(size == (off_t)-1) {
2136         *errmsg = vstrallocf(_("no size line match in %s output"), my_argv[0]);
2137         dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2138         dbprintf(".....\n");
2139     } else if(size == (off_t)0 && level == 0) {
2140         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2141                   my_argv[0], dle->disk);
2142         dbprintf(".....\n");
2143     }
2144     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2145               qdisk,
2146               level,
2147               (long long)size);
2148
2149     kill(-dumppid, SIGTERM);
2150
2151     dbprintf(_("waiting for %s \"%s\" child\n"), my_argv[0], qdisk);
2152     waitpid(dumppid, &wait_status, 0);
2153     if (WIFSIGNALED(wait_status)) {
2154         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2155                              cmd, WTERMSIG(wait_status), dbfn());
2156     } else if (WIFEXITED(wait_status)) {
2157         if (WEXITSTATUS(wait_status) != 0) {
2158             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2159                                  cmd, WEXITSTATUS(wait_status), dbfn());
2160         } else {
2161             /* Normal exit */
2162         }
2163     } else {
2164         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2165                              cmd, dbfn());
2166     }
2167     dbprintf(_("after %s %s wait\n"), my_argv[0], qdisk);
2168
2169 common_exit:
2170
2171     if (incrname) {
2172         unlink(incrname);
2173     }
2174     amfree(incrname);
2175     amfree(basename);
2176     amfree(dirname);
2177     amfree(inputname);
2178     amfree(my_argv);
2179     amfree(qdisk);
2180     amfree(cmd);
2181     amfree(file_exclude);
2182     amfree(file_include);
2183
2184     aclose(nullfd);
2185     afclose(dumpout);
2186     afclose(in);
2187     afclose(out);
2188
2189     return size;
2190 }
2191 #endif
2192
2193 off_t
2194 getsize_application_api(
2195     disk_estimates_t         *est,
2196     int                       nb_level,
2197     int                      *levels,
2198     backup_support_option_t  *bsu)
2199 {
2200     dle_t *dle = est->dle;
2201     int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
2202     pid_t dumppid;
2203     off_t size = (off_t)-1;
2204     FILE *dumpout;
2205     FILE *dumperr;
2206     char *line = NULL;
2207     char *cmd = NULL;
2208     char *cmdline;
2209     int  i, j, k;
2210     char **argvchild;
2211     char *newoptstr = NULL;
2212     off_t size1, size2;
2213     times_t start_time;
2214     char *qdisk = quote_string(dle->disk);
2215     char *qamdevice = quote_string(dle->device);
2216     amwait_t wait_status;
2217     char levelstr[NUM_STR_SIZE];
2218     GSList   *scriptlist;
2219     script_t *script;
2220     char     *errmsg = NULL;
2221
2222     cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
2223
2224     i=0;
2225     k = application_property_argv_size(dle);
2226     for (scriptlist = dle->scriptlist; scriptlist != NULL;
2227          scriptlist = scriptlist->next) {
2228         script = (script_t *)scriptlist->data;
2229         if (script->result && script->result->proplist) {
2230             k += property_argv_size(script->result->proplist);
2231         }
2232     }
2233     argvchild = g_new0(char *, 16 + k + DUMP_LEVELS*2);
2234     argvchild[i++] = dle->program;
2235     argvchild[i++] = "estimate";
2236     if (bsu->message_line == 1) {
2237         argvchild[i++] = "--message";
2238         argvchild[i++] = "line";
2239     }
2240     if (g_options->config && bsu->config == 1) {
2241         argvchild[i++] = "--config";
2242         argvchild[i++] = g_options->config;
2243     }
2244     if (g_options->hostname && bsu->host == 1) {
2245         argvchild[i++] = "--host";
2246         argvchild[i++] = g_options->hostname;
2247     }
2248     argvchild[i++] = "--device";
2249     argvchild[i++] = dle->device;
2250     if (dle->disk && bsu->disk == 1) {
2251         argvchild[i++] = "--disk";
2252         argvchild[i++] = dle->disk;
2253     }
2254     for (j=0; j < nb_level; j++) {
2255         argvchild[i++] = "--level";
2256         g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
2257         argvchild[i++] = stralloc(levelstr);
2258     }
2259     if (dle->calcsize && bsu->calcsize) {
2260         argvchild[i++] = "--calcsize";
2261     }
2262
2263     i += application_property_add_to_argv(&argvchild[i], dle, bsu);
2264
2265     for (scriptlist = dle->scriptlist; scriptlist != NULL;
2266          scriptlist = scriptlist->next) {
2267         script = (script_t *)scriptlist->data;
2268         if (script->result && script->result->proplist) {
2269             i += property_add_to_argv(&argvchild[i],
2270                                       script->result->proplist);
2271         }
2272     }
2273
2274     argvchild[i] = NULL;
2275
2276     cmdline = stralloc(cmd);
2277     for(j = 1; j < i; j++)
2278         cmdline = vstrextend(&cmdline, " ", argvchild[j], NULL);
2279     dbprintf("running: \"%s\"\n", cmdline);
2280     amfree(cmdline);
2281
2282     if (pipe(pipeerrfd) < 0) {
2283         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2284                             strerror(errno));
2285         goto common_exit;
2286     }
2287
2288     if (pipe(pipeinfd) < 0) {
2289         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2290                             strerror(errno));
2291         goto common_exit;
2292     }
2293
2294     if (pipe(pipeoutfd) < 0) {
2295         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2296                             strerror(errno));
2297         goto common_exit;
2298     }
2299
2300     start_time = curclock();
2301
2302     switch(dumppid = fork()) {
2303     case -1:
2304       size = (off_t)-1;
2305       goto common_exit;
2306     default:
2307       break; /* parent */
2308     case 0:
2309       dup2(pipeinfd[0], 0);
2310       dup2(pipeoutfd[1], 1);
2311       dup2(pipeerrfd[1], 2);
2312       aclose(pipeinfd[1]);
2313       aclose(pipeoutfd[0]);
2314       aclose(pipeerrfd[0]);
2315       safe_fd(-1, 0);
2316
2317       execve(cmd, argvchild, safe_env());
2318       error(_("exec %s failed: %s"), cmd, strerror(errno));
2319       /*NOTREACHED*/
2320     }
2321     amfree(newoptstr);
2322
2323     aclose(pipeinfd[0]);
2324     aclose(pipeoutfd[1]);
2325     aclose(pipeerrfd[1]);
2326     aclose(pipeinfd[1]);
2327
2328     dumpout = fdopen(pipeoutfd[0],"r");
2329     if (!dumpout) {
2330         error(_("Can't fdopen: %s"), strerror(errno));
2331         /*NOTREACHED*/
2332     }
2333
2334     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2335         long long size1_ = (long long)0;
2336         long long size2_ = (long long)0;
2337         int  level = 0;
2338         if (line[0] == '\0')
2339             continue;
2340         dbprintf("%s\n", line);
2341         if (strncmp(line,"ERROR ", 6) == 0) {
2342             char *errmsg, *qerrmsg;
2343
2344             errmsg = stralloc(line+6);
2345             qerrmsg = quote_string(errmsg);
2346             dbprintf(_("errmsg is %s\n"), errmsg);
2347             g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2348             amfree(qerrmsg);
2349             continue;
2350         }
2351         i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
2352         if (i != 3) {
2353             i = sscanf(line, "%lld %lld", &size1_, &size2_);
2354             level = levels[0];
2355             if (i != 2) {
2356                 char *errmsg, *qerrmsg;
2357
2358                 errmsg = vstrallocf(_("bad line %s"), line);
2359                 qerrmsg = quote_string(errmsg);
2360                 dbprintf(_("errmsg is %s\n"), errmsg);
2361                 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2362                 amfree(qerrmsg);
2363                 continue;
2364             }
2365         }
2366         size1 = (off_t)size1_;
2367         size2 = (off_t)size2_;
2368         if (size1 <= 0 || size2 <=0)
2369             size = -1;
2370         else if (size1 * size2 > 0)
2371             size = size1 * size2;
2372         dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2373                  qamdevice,
2374                  level,
2375                  (long long)size);
2376         g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
2377     }
2378     amfree(line);
2379
2380     dumperr = fdopen(pipeerrfd[0],"r");
2381     if (!dumperr) {
2382         error(_("Can't fdopen: %s"), strerror(errno));
2383         /*NOTREACHED*/
2384     }
2385
2386     while ((line = agets(dumperr)) != NULL) {
2387             if (strlen(line) > 0) {
2388             char *err =  g_strdup_printf(_("Application '%s': %s"),
2389                                          dle->program, line);
2390             char *qerr = quote_string(err);
2391             for (j=0; j < nb_level; j++) {
2392                 fprintf(stdout, "%s %d ERROR %s\n",
2393                         est->qamname, levels[j], qerr);
2394             }
2395             dbprintf("ERROR %s", qerr);
2396             amfree(err);
2397             amfree(qerr);
2398         }
2399         amfree(line);
2400     }
2401
2402     dbprintf(".....\n");
2403     if (nb_level == 1) {
2404         dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
2405                  levels[0], walltime_str(timessub(curclock(), start_time)));
2406     } else {
2407         dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
2408                  walltime_str(timessub(curclock(), start_time)));
2409     }
2410
2411     kill(-dumppid, SIGTERM);
2412
2413     dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2414     waitpid(dumppid, &wait_status, 0);
2415     if (WIFSIGNALED(wait_status)) {
2416         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2417                             cmd, WTERMSIG(wait_status), dbfn());
2418     } else if (WIFEXITED(wait_status)) {
2419         if (WEXITSTATUS(wait_status) != 0) {
2420             errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2421                                 WEXITSTATUS(wait_status), dbfn());
2422         } else {
2423             /* Normal exit */
2424         }
2425     } else {
2426         errmsg = vstrallocf(_("%s got bad exit: see %s"),
2427                             cmd, dbfn());
2428     }
2429     dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2430
2431     afclose(dumpout);
2432     afclose(dumperr);
2433
2434 common_exit:
2435
2436     amfree(cmd);
2437     amfree(newoptstr);
2438     amfree(qdisk);
2439     amfree(qamdevice);
2440     if (errmsg) {
2441         char *qerrmsg = quote_string(errmsg);
2442         dbprintf(_("errmsg is %s\n"), errmsg);
2443         for (j=0; j < nb_level; j++) {
2444             g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
2445         }
2446         amfree(errmsg);
2447         amfree(qerrmsg);
2448     }
2449     return size;
2450 }
2451
2452
2453 /*
2454  * Returns the value of the first integer in a string.
2455  */
2456
2457 double
2458 first_num(
2459     char *      str)
2460 {
2461     char *start;
2462     int ch;
2463     double d;
2464
2465     ch = *str++;
2466     while(ch && !isdigit(ch)) ch = *str++;
2467     start = str-1;
2468     while(isdigit(ch) || (ch == '.')) ch = *str++;
2469     str[-1] = '\0';
2470     d = atof(start);
2471     str[-1] = (char)ch;
2472     return d;
2473 }
2474
2475
2476 /*
2477  * Checks the dump output line against the error and size regex tables.
2478  */
2479
2480 off_t
2481 handle_dumpline(
2482     char *      str)
2483 {
2484     regex_scale_t *rp;
2485     double size;
2486
2487     /* check for size match */
2488     /*@ignore@*/
2489     for(rp = re_size; rp->regex != NULL; rp++) {
2490         if(match(rp->regex, str)) {
2491             size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2492             if(size < 0.0)
2493                 size = 1.0;             /* found on NeXT -- sigh */
2494             return (off_t)size;
2495         }
2496     }
2497     /*@end@*/
2498     return (off_t)-1;
2499 }