Imported Upstream version 2.6.1
[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(_("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         amfree(device);
1312         amfree(qdevice);
1313         device = amname_to_dirname(amdevice);
1314         qdevice = quote_string(device);
1315         dumpkeys = vstralloc(level_str, "b", "f", NULL);
1316         dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1317                   cmd, name, dumpkeys, qdevice);
1318     }
1319     else
1320 #endif                                                  /* } */
1321 #ifdef DUMP                                             /* { */
1322     if (1) {
1323 # ifdef USE_RUNDUMP                                     /* { */
1324 #  ifdef AIX_BACKUP                                     /* { */
1325         name = stralloc(" (backup)");
1326 #  else                                                 /* } { */
1327         name = vstralloc(" (", DUMP, ")", NULL);
1328 #  endif                                                /* } */
1329 # else                                                  /* } { */
1330         name = stralloc("");
1331         cmd = newstralloc(cmd, DUMP);
1332         config = skip_argument;
1333         is_rundump = 0;
1334 # endif                                                 /* } */
1335
1336 # ifdef AIX_BACKUP                                      /* { */
1337         dumpkeys = vstralloc("-", level_str, "f", NULL);
1338         dbprintf(_("running \"%s%s %s - %s\"\n"),
1339                   cmd, name, dumpkeys, qdevice);
1340 # else                                                  /* } { */
1341 #  ifdef HAVE_DUMP_ESTIMATE
1342 #    define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1343 #  else
1344 #    define PARAM_DUMP_ESTIMATE ""
1345 #  endif
1346 #  ifdef HAVE_HONOR_NODUMP
1347 #    define PARAM_HONOR_NODUMP "h"
1348 #  else
1349 #    define PARAM_HONOR_NODUMP ""
1350 #  endif
1351         dumpkeys = vstralloc(level_str,
1352                              PARAM_DUMP_ESTIMATE,
1353                              PARAM_HONOR_NODUMP,
1354                              "s", "f", NULL);
1355
1356 #  ifdef HAVE_DUMP_ESTIMATE
1357         stdoutfd = pipefd[1];
1358 #  endif
1359
1360 #  ifdef HAVE_HONOR_NODUMP                              /* { */
1361         dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1362                   cmd, name, dumpkeys, qdevice);
1363 #  else                                                 /* } { */
1364         dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1365                   cmd, name, dumpkeys, qdevice);
1366 #  endif                                                /* } */
1367 # endif                                                 /* } */
1368     }
1369     else
1370 #endif                                                  /* } */
1371     {
1372         error(_("no dump program available"));
1373         /*NOTREACHED*/
1374     }
1375
1376     if (pipe(killctl) < 0) {
1377         dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1378         /* Message will be printed later... */
1379         killctl[0] = killctl[1] = -1;
1380     }
1381
1382     start_time = curclock();
1383     switch(dumppid = fork()) {
1384     case -1:
1385         *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1386                              strerror(errno));
1387         dbprintf("%s\n", *errmsg);
1388         amfree(dumpkeys);
1389         amfree(cmd);
1390         amfree(rundump_cmd);
1391         amfree(device);
1392         amfree(qdevice);
1393         amfree(qdisk);
1394         amfree(name);
1395         amfree(fstype);
1396         return -1;
1397     default:
1398         break; 
1399     case 0:     /* child process */
1400         if(SETPGRP == -1)
1401             SETPGRP_FAILED();
1402         else if (killctl[0] == -1 || killctl[1] == -1)
1403             dbprintf(_("Trying without killpgrp\n"));
1404         else {
1405             switch(fork()) {
1406             case -1:
1407                 dbprintf(_("fork failed, trying without killpgrp\n"));
1408                 break;
1409
1410             default:
1411             {
1412                 char *config;
1413                 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp",
1414                                                versionsuffix(), NULL);
1415                 dbprintf(_("running %s\n"), killpgrp_cmd);
1416                 dup2(killctl[0], 0);
1417                 dup2(nullfd, 1);
1418                 dup2(nullfd, 2);
1419                 close(pipefd[0]);
1420                 close(pipefd[1]);
1421                 close(killctl[1]);
1422                 close(nullfd);
1423                 if (g_options->config)
1424                     config = g_options->config;
1425                 else
1426                     config = "NOCONFIG";
1427                 safe_fd(-1, 0);
1428                 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1429                        safe_env());
1430                 dbprintf(_("cannot execute %s: %s\n"),
1431                           killpgrp_cmd, strerror(errno));
1432                 exit(-1);
1433             }
1434
1435             case 0:  /* child process */
1436                 break;
1437             }
1438         }
1439
1440         dup2(nullfd, 0);
1441         dup2(stdoutfd, 1);
1442         dup2(pipefd[1], 2);
1443         aclose(pipefd[0]);
1444         if (killctl[0] != -1)
1445             aclose(killctl[0]);
1446         if (killctl[1] != -1)
1447             aclose(killctl[1]);
1448         safe_fd(-1, 0);
1449
1450 #ifdef XFSDUMP
1451 #ifdef DUMP
1452         if (strcmp(fstype, "xfs") == 0)
1453 #else
1454         if (1)
1455 #endif
1456             if (is_rundump)
1457                 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1458                        level_str, "-", device, (char *)0, safe_env());
1459             else
1460                 execle(cmd, "xfsdump", "-F", "-J", "-l",
1461                        level_str, "-", device, (char *)0, safe_env());
1462         else
1463 #endif
1464 #ifdef VXDUMP
1465 #ifdef DUMP
1466         if (strcmp(fstype, "vxfs") == 0)
1467 #else
1468         if (1)
1469 #endif
1470             if (is_rundump)
1471                 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1472                        "-", device, (char *)0, safe_env());
1473             else
1474                 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1475                        device, (char *)0, safe_env());
1476         else
1477 #endif
1478 #ifdef VDUMP
1479 #ifdef DUMP
1480         if (strcmp(fstype, "advfs") == 0)
1481 #else
1482         if (1)
1483 #endif
1484             if (is_rundump)
1485                 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1486                        device, (char *)0, safe_env());
1487             else
1488                 execle(cmd, "vdump", dumpkeys, "60", "-",
1489                        device, (char *)0, safe_env());
1490         else
1491 #endif
1492 #ifdef DUMP
1493 # ifdef AIX_BACKUP
1494             if (is_rundump)
1495                 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1496                        device, (char *)0, safe_env());
1497             else
1498                 execle(cmd, "backup", dumpkeys, "-",
1499                        device, (char *)0, safe_env());
1500 # else
1501             if (is_rundump) {
1502                 execle(cmd, "rundump", config, "dump", dumpkeys, 
1503 #ifdef HAVE_HONOR_NODUMP
1504                        "0",
1505 #endif
1506                        "1048576", "-", device, (char *)0, safe_env());
1507             } else {
1508                 execle(cmd, "dump", dumpkeys, 
1509 #ifdef HAVE_HONOR_NODUMP
1510                        "0",
1511 #endif
1512                        "1048576", "-", device, (char *)0, safe_env());
1513             }
1514 # endif
1515 #endif
1516         {
1517             error(_("exec %s failed or no dump program available: %s"),
1518                   cmd, strerror(errno));
1519             /*NOTREACHED*/
1520         }
1521     }
1522
1523     amfree(dumpkeys);
1524     amfree(rundump_cmd);
1525
1526     aclose(pipefd[1]);
1527     if (killctl[0] != -1)
1528         aclose(killctl[0]);
1529     dumpout = fdopen(pipefd[0],"r");
1530     if (!dumpout) {
1531         error(_("Can't fdopen: %s"), strerror(errno));
1532         /*NOTREACHED*/
1533     }
1534
1535     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1536         if (line[0] == '\0')
1537             continue;
1538         dbprintf("%s\n", line);
1539         size = handle_dumpline(line);
1540         if(size > (off_t)-1) {
1541             amfree(line);
1542             while ((line = agets(dumpout)) != NULL) {
1543                 if (line[0] != '\0')
1544                     break;
1545                 amfree(line);
1546             }
1547             if (line != NULL) {
1548                 dbprintf("%s\n", line);
1549             }
1550             break;
1551         }
1552     }
1553     amfree(line);
1554
1555     dbprintf(".....\n");
1556     dbprintf(_("estimate time for %s level %d: %s\n"),
1557               qdisk,
1558               level,
1559               walltime_str(timessub(curclock(), start_time)));
1560     if(size == (off_t)-1) {
1561         *errmsg = vstrallocf(_("no size line match in %s%s output"),
1562                              cmd, name);
1563         dbprintf(_("%s for %s\n"),
1564                   *errmsg, qdisk);
1565
1566         dbprintf(".....\n");
1567         dbprintf(_("Run %s%s manually to check for errors\n"),
1568                     cmd, name);
1569     } else if(size == (off_t)0 && level == 0) {
1570         dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1571                   cmd, name, dle->disk);
1572         dbprintf(".....\n");
1573     } else {
1574             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1575               qdisk,
1576               level,
1577               (long long)size);
1578     }
1579
1580     if (killctl[1] != -1) {
1581         dbprintf(_("asking killpgrp to terminate\n"));
1582         aclose(killctl[1]);
1583         for(s = 5; s > 0; --s) {
1584             sleep(1);
1585             if (waitpid(dumppid, NULL, WNOHANG) != -1)
1586                 goto terminated;
1587         }
1588     }
1589     
1590     /*
1591      * First, try to kill the dump process nicely.  If it ignores us
1592      * for several seconds, hit it harder.
1593      */
1594     dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1595     if (kill(-dumppid, SIGTERM) == -1) {
1596         dbprintf(_("kill failed: %s\n"), strerror(errno));
1597     }
1598     /* Now check whether it dies */
1599     for(s = 5; s > 0; --s) {
1600         sleep(1);
1601         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1602             goto terminated;
1603     }
1604
1605     dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1606     if (kill(-dumppid, SIGKILL) == -1) {
1607         dbprintf(_("kill failed: %s\n"), strerror(errno));
1608     }
1609     for(s = 5; s > 0; --s) {
1610         sleep(1);
1611         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1612             goto terminated;
1613     }
1614
1615     dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1616     waitpid(dumppid, &wait_status, 0);
1617     if (WIFSIGNALED(wait_status)) {
1618         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1619                              cmd, WTERMSIG(wait_status), dbfn());
1620     } else if (WIFEXITED(wait_status)) {
1621         if (WEXITSTATUS(wait_status) != 0) {
1622             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1623                                  cmd, WEXITSTATUS(wait_status), dbfn());
1624         } else {
1625             /* Normal exit */
1626         }
1627     } else {
1628         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1629                              cmd, dbfn());
1630     }
1631     dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1632
1633  terminated:
1634
1635     aclose(nullfd);
1636     afclose(dumpout);
1637
1638     amfree(device);
1639     amfree(qdevice);
1640     amfree(qdisk);
1641     amfree(fstype);
1642
1643     amfree(cmd);
1644     amfree(name);
1645
1646     return size;
1647 }
1648
1649 #ifdef SAMBA_CLIENT
1650 off_t
1651 getsize_smbtar(
1652     dle_t      *dle,
1653     int         level,
1654     char      **errmsg)
1655 {
1656     int pipefd = -1, nullfd = -1, passwdfd = -1;
1657     pid_t dumppid;
1658     off_t size;
1659     FILE *dumpout;
1660     char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1661     char *share = NULL, *subdir = NULL;
1662     size_t lpass;
1663     char *pwtext;
1664     size_t pwtext_len;
1665     char *line;
1666     char *pw_fd_env;
1667     times_t start_time;
1668     char *error_pn = NULL;
1669     char *qdisk = quote_string(dle->disk);
1670     amwait_t wait_status;
1671
1672     error_pn = stralloc2(get_pname(), "-smbclient");
1673
1674     if (level > 1)
1675         return -2; /* planner will not even consider this level */
1676     if (dle->estimate == ES_SERVER)
1677         return -1; /* planner will consider this level, */
1678                    /* but use a server-side estimate    */
1679
1680     parsesharename(dle->device, &share, &subdir);
1681     if (!share) {
1682         amfree(share);
1683         amfree(subdir);
1684         set_pname(error_pn);
1685         amfree(error_pn);
1686         error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1687         /*NOTREACHED*/
1688     }
1689     if ((subdir) && (SAMBA_VERSION < 2)) {
1690         amfree(share);
1691         amfree(subdir);
1692         set_pname(error_pn);
1693         amfree(error_pn);
1694         error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1695         /*NOTREACHED*/
1696     }
1697     if ((user_and_password = findpass(share, &domain)) == NULL) {
1698
1699         if(domain) {
1700             memset(domain, '\0', strlen(domain));
1701             amfree(domain);
1702         }
1703         set_pname(error_pn);
1704         amfree(error_pn);
1705         error(_("cannot find password for %s"), dle->disk);
1706         /*NOTREACHED*/
1707     }
1708     lpass = strlen(user_and_password);
1709     if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1710         memset(user_and_password, '\0', (size_t)lpass);
1711         amfree(user_and_password);
1712         if(domain) {
1713             memset(domain, '\0', strlen(domain));
1714             amfree(domain);
1715         }
1716         set_pname(error_pn);
1717         amfree(error_pn);
1718         error(_("password field not \'user%%pass\' for %s"), dle->disk);
1719         /*NOTREACHED*/
1720     }
1721     *pwtext++ = '\0';
1722     pwtext_len = strlen(pwtext);
1723     if ((sharename = makesharename(share, 0)) == NULL) {
1724         memset(user_and_password, '\0', (size_t)lpass);
1725         amfree(user_and_password);
1726         if(domain) {
1727             memset(domain, '\0', strlen(domain));
1728             amfree(domain);
1729         }
1730         set_pname(error_pn);
1731         amfree(error_pn);
1732         error(_("cannot make share name of %s"), share);
1733         /*NOTREACHED*/
1734     }
1735     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1736         memset(user_and_password, '\0', (size_t)lpass);
1737         amfree(user_and_password);
1738         if(domain) {
1739             memset(domain, '\0', strlen(domain));
1740             amfree(domain);
1741         }
1742         set_pname(error_pn);
1743         amfree(error_pn);
1744         amfree(sharename);
1745         error(_("could not open /dev/null: %s\n"),
1746               strerror(errno));
1747         /*NOTREACHED*/
1748     }
1749
1750 #if SAMBA_VERSION >= 2
1751     if (level == 0)
1752         tarkeys = "archive 0;recurse;du";
1753     else
1754         tarkeys = "archive 1;recurse;du";
1755 #else
1756     if (level == 0)
1757         tarkeys = "archive 0;recurse;dir";
1758     else
1759         tarkeys = "archive 1;recurse;dir";
1760 #endif
1761
1762     start_time = curclock();
1763
1764     if (pwtext_len > 0) {
1765         pw_fd_env = "PASSWD_FD";
1766     } else {
1767         pw_fd_env = "dummy_PASSWD_FD";
1768     }
1769     dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
1770               &nullfd, &nullfd, &pipefd, 
1771               pw_fd_env, &passwdfd,
1772               "smbclient",
1773               sharename,
1774               "-d", SAMBA_DEBUG_LEVEL,
1775               *user_and_password ? "-U" : skip_argument,
1776               *user_and_password ? user_and_password : skip_argument,
1777               "-E",
1778               domain ? "-W" : skip_argument,
1779               domain ? domain : skip_argument,
1780 #if SAMBA_VERSION >= 2
1781               subdir ? "-D" : skip_argument,
1782               subdir ? subdir : skip_argument,
1783 #endif
1784               "-c", tarkeys,
1785               NULL);
1786     if(domain) {
1787         memset(domain, '\0', strlen(domain));
1788         amfree(domain);
1789     }
1790     aclose(nullfd);
1791     if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
1792         int save_errno = errno;
1793
1794         memset(user_and_password, '\0', (size_t)lpass);
1795         amfree(user_and_password);
1796         aclose(passwdfd);
1797         set_pname(error_pn);
1798         amfree(error_pn);
1799         error(_("password write failed: %s"), strerror(save_errno));
1800         /*NOTREACHED*/
1801     }
1802     memset(user_and_password, '\0', (size_t)lpass);
1803     amfree(user_and_password);
1804     aclose(passwdfd);
1805     amfree(sharename);
1806     amfree(share);
1807     amfree(subdir);
1808     amfree(error_pn);
1809     dumpout = fdopen(pipefd,"r");
1810     if (!dumpout) {
1811         error(_("Can't fdopen: %s"), strerror(errno));
1812         /*NOTREACHED*/
1813     }
1814
1815     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1816         if (line[0] == '\0')
1817             continue;
1818         dbprintf("%s\n", line);
1819         size = handle_dumpline(line);
1820         if(size > -1) {
1821             amfree(line);
1822             while ((line = agets(dumpout)) != NULL) {
1823                 if (line[0] != '\0')
1824                     break;
1825                 amfree(line);
1826             }
1827             if(line != NULL) {
1828                 dbprintf("%s\n", line);
1829             }
1830             break;
1831         }
1832     }
1833     amfree(line);
1834
1835     dbprintf(".....\n");
1836     dbprintf(_("estimate time for %s level %d: %s\n"),
1837               qdisk,
1838               level,
1839               walltime_str(timessub(curclock(), start_time)));
1840     if(size == (off_t)-1) {
1841         *errmsg = vstrallocf(_("no size line match in %s output"),
1842                              SAMBA_CLIENT);
1843         dbprintf(_("%s for %s\n"),
1844                   *errmsg, qdisk);
1845         dbprintf(".....\n");
1846     } else if(size == (off_t)0 && level == 0) {
1847         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1848                   SAMBA_CLIENT, dle->disk);
1849         dbprintf(".....\n");
1850     }
1851     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1852               qdisk,
1853               level,
1854               (long long)size);
1855
1856     kill(-dumppid, SIGTERM);
1857
1858     dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1859     waitpid(dumppid, &wait_status, 0);
1860     if (WIFSIGNALED(wait_status)) {
1861         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1862                              SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
1863     } else if (WIFEXITED(wait_status)) {
1864         if (WEXITSTATUS(wait_status) != 0) {
1865             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1866                                  SAMBA_CLIENT, WEXITSTATUS(wait_status),
1867                                  dbfn());
1868         } else {
1869             /* Normal exit */
1870         }
1871     } else {
1872         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1873                              SAMBA_CLIENT, dbfn());
1874     }
1875     dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
1876
1877     afclose(dumpout);
1878     pipefd = -1;
1879
1880     amfree(error_pn);
1881     amfree(qdisk);
1882
1883     return size;
1884 }
1885 #endif
1886
1887 #ifdef GNUTAR
1888 off_t
1889 getsize_gnutar(
1890     dle_t      *dle,
1891     int         level,
1892     time_t      dumpsince,
1893     char      **errmsg)
1894 {
1895     int pipefd = -1, nullfd = -1;
1896     pid_t dumppid;
1897     off_t size = (off_t)-1;
1898     FILE *dumpout = NULL;
1899     char *incrname = NULL;
1900     char *basename = NULL;
1901     char *dirname = NULL;
1902     char *inputname = NULL;
1903     FILE *in = NULL;
1904     FILE *out = NULL;
1905     char *line = NULL;
1906     char *cmd = NULL;
1907     char dumptimestr[80];
1908     struct tm *gmtm;
1909     int nb_exclude = 0;
1910     int nb_include = 0;
1911     char **my_argv;
1912     int i;
1913     char *file_exclude = NULL;
1914     char *file_include = NULL;
1915     times_t start_time;
1916     int infd, outfd;
1917     ssize_t nb;
1918     char buf[32768];
1919     char *qdisk = quote_string(dle->disk);
1920     char *gnutar_list_dir;
1921     amwait_t wait_status;
1922     char tmppath[PATH_MAX];
1923
1924     if (level > 9)
1925         return -2; /* planner will not even consider this level */
1926     if (dle->estimate == ES_SERVER)
1927         return -1; /* planner will consider this level, */
1928                    /* but use a server-side estimate    */
1929
1930     if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
1931     if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
1932     if(dle->include_file) nb_include += dle->include_file->nb_element;
1933     if(dle->include_list) nb_include += dle->include_list->nb_element;
1934
1935     if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
1936     if(nb_include > 0) file_include = build_include(dle, 0);
1937
1938     my_argv = alloc(SIZEOF(char *) * 22);
1939     i = 0;
1940
1941     gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
1942     if (strlen(gnutar_list_dir) == 0)
1943         gnutar_list_dir = NULL;
1944     if (gnutar_list_dir) {
1945         char number[NUM_STR_SIZE];
1946         int baselevel;
1947         char *sdisk = sanitise_filename(dle->disk);
1948
1949         basename = vstralloc(gnutar_list_dir,
1950                              "/",
1951                              g_options->hostname,
1952                              sdisk,
1953                              NULL);
1954         amfree(sdisk);
1955
1956         g_snprintf(number, SIZEOF(number), "%d", level);
1957         incrname = vstralloc(basename, "_", number, ".new", NULL);
1958         unlink(incrname);
1959
1960         /*
1961          * Open the listed incremental file from the previous level.  Search
1962          * backward until one is found.  If none are found (which will also
1963          * be true for a level 0), arrange to read from /dev/null.
1964          */
1965         baselevel = level;
1966         infd = -1;
1967         while (infd == -1) {
1968             if (--baselevel >= 0) {
1969                 g_snprintf(number, SIZEOF(number), "%d", baselevel);
1970                 inputname = newvstralloc(inputname,
1971                                          basename, "_", number, NULL);
1972             } else {
1973                 inputname = newstralloc(inputname, "/dev/null");
1974             }
1975             if ((infd = open(inputname, O_RDONLY)) == -1) {
1976
1977                 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
1978                                      inputname, strerror(errno));
1979                 dbprintf("%s\n", *errmsg);
1980                 if (baselevel < 0) {
1981                     goto common_exit;
1982                 }
1983                 amfree(*errmsg);
1984             }
1985         }
1986
1987         /*
1988          * Copy the previous listed incremental file to the new one.
1989          */
1990         if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1991             *errmsg = vstrallocf(_("opening %s: %s"),
1992                                  incrname, strerror(errno));
1993             dbprintf("%s\n", *errmsg);
1994             goto common_exit;
1995         }
1996
1997         while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1998             if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
1999                 *errmsg = vstrallocf(_("writing to %s: %s"),
2000                                      incrname, strerror(errno));
2001                 dbprintf("%s\n", *errmsg);
2002                 goto common_exit;
2003             }
2004         }
2005
2006         if (nb < 0) {
2007             *errmsg = vstrallocf(_("reading from %s: %s"),
2008                                  inputname, strerror(errno));
2009             dbprintf("%s\n", *errmsg);
2010             goto common_exit;
2011         }
2012
2013         if (close(infd) != 0) {
2014             *errmsg = vstrallocf(_("closing %s: %s"),
2015                                  inputname, strerror(errno));
2016             dbprintf("%s\n", *errmsg);
2017             goto common_exit;
2018         }
2019         if (close(outfd) != 0) {
2020             *errmsg = vstrallocf(_("closing %s: %s"),
2021                                  incrname, strerror(errno));
2022             dbprintf("%s\n", *errmsg);
2023             goto common_exit;
2024         }
2025
2026         amfree(inputname);
2027         amfree(basename);
2028     }
2029
2030     gmtm = gmtime(&dumpsince);
2031     g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2032                 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2033                 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2034                 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2035
2036     dirname = amname_to_dirname(dle->device);
2037
2038     cmd = vstralloc(amlibexecdir, "/", "runtar", versionsuffix(), NULL);
2039     my_argv[i++] = "runtar";
2040     if (g_options->config)
2041         my_argv[i++] = g_options->config;
2042     else
2043         my_argv[i++] = "NOCONFIG";
2044
2045 #ifdef GNUTAR
2046     my_argv[i++] = GNUTAR;
2047 #else
2048     my_argv[i++] = "tar";
2049 #endif
2050     my_argv[i++] = "--create";
2051     my_argv[i++] = "--file";
2052     my_argv[i++] = "/dev/null";
2053     /* use --numeric-owner for estimates, to reduce the number of user/group
2054      * lookups required */
2055     my_argv[i++] = "--numeric-owner";
2056     my_argv[i++] = "--directory";
2057     canonicalize_pathname(dirname, tmppath);
2058     my_argv[i++] = tmppath;
2059     my_argv[i++] = "--one-file-system";
2060     if (gnutar_list_dir) {
2061             my_argv[i++] = "--listed-incremental";
2062             my_argv[i++] = incrname;
2063     } else {
2064         my_argv[i++] = "--incremental";
2065         my_argv[i++] = "--newer";
2066         my_argv[i++] = dumptimestr;
2067     }
2068 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2069     /* --atime-preserve causes gnutar to call
2070      * utime() after reading files in order to
2071      * adjust their atime.  However, utime()
2072      * updates the file's ctime, so incremental
2073      * dumps will think the file has changed. */
2074     my_argv[i++] = "--atime-preserve";
2075 #endif
2076     my_argv[i++] = "--sparse";
2077     my_argv[i++] = "--ignore-failed-read";
2078     my_argv[i++] = "--totals";
2079
2080     if(file_exclude) {
2081         my_argv[i++] = "--exclude-from";
2082         my_argv[i++] = file_exclude;
2083     }
2084
2085     if(file_include) {
2086         my_argv[i++] = "--files-from";
2087         my_argv[i++] = file_include;
2088     }
2089     else {
2090         my_argv[i++] = ".";
2091     }
2092     my_argv[i++] = NULL;
2093
2094     start_time = curclock();
2095
2096     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2097         *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2098                              strerror(errno));
2099         dbprintf("%s\n", *errmsg);
2100         goto common_exit;
2101     }
2102
2103     dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
2104                          &nullfd, &nullfd, &pipefd, my_argv);
2105
2106     dumpout = fdopen(pipefd,"r");
2107     if (!dumpout) {
2108         error(_("Can't fdopen: %s"), strerror(errno));
2109         /*NOTREACHED*/
2110     }
2111
2112     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2113         if (line[0] == '\0')
2114             continue;
2115         dbprintf("%s\n", line);
2116         size = handle_dumpline(line);
2117         if(size > (off_t)-1) {
2118             amfree(line);
2119             while ((line = agets(dumpout)) != NULL) {
2120                 if (line[0] != '\0') {
2121                     break;
2122                 }
2123                 amfree(line);
2124             }
2125             if (line != NULL) {
2126                 dbprintf("%s\n", line);
2127                 break;
2128             }
2129             break;
2130         }
2131     }
2132     amfree(line);
2133
2134     dbprintf(".....\n");
2135     dbprintf(_("estimate time for %s level %d: %s\n"),
2136               qdisk,
2137               level,
2138               walltime_str(timessub(curclock(), start_time)));
2139     if(size == (off_t)-1) {
2140         *errmsg = vstrallocf(_("no size line match in %s output"), my_argv[0]);
2141         dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2142         dbprintf(".....\n");
2143     } else if(size == (off_t)0 && level == 0) {
2144         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2145                   my_argv[0], dle->disk);
2146         dbprintf(".....\n");
2147     }
2148     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2149               qdisk,
2150               level,
2151               (long long)size);
2152
2153     kill(-dumppid, SIGTERM);
2154
2155     dbprintf(_("waiting for %s \"%s\" child\n"), my_argv[0], qdisk);
2156     waitpid(dumppid, &wait_status, 0);
2157     if (WIFSIGNALED(wait_status)) {
2158         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2159                              cmd, WTERMSIG(wait_status), dbfn());
2160     } else if (WIFEXITED(wait_status)) {
2161         if (WEXITSTATUS(wait_status) != 0) {
2162             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2163                                  cmd, WEXITSTATUS(wait_status), dbfn());
2164         } else {
2165             /* Normal exit */
2166         }
2167     } else {
2168         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2169                              cmd, dbfn());
2170     }
2171     dbprintf(_("after %s %s wait\n"), my_argv[0], qdisk);
2172
2173 common_exit:
2174
2175     if (incrname) {
2176         unlink(incrname);
2177     }
2178     amfree(incrname);
2179     amfree(basename);
2180     amfree(dirname);
2181     amfree(inputname);
2182     amfree(my_argv);
2183     amfree(qdisk);
2184     amfree(cmd);
2185     amfree(file_exclude);
2186     amfree(file_include);
2187
2188     aclose(nullfd);
2189     afclose(dumpout);
2190     afclose(in);
2191     afclose(out);
2192
2193     return size;
2194 }
2195 #endif
2196
2197 off_t
2198 getsize_application_api(
2199     disk_estimates_t         *est,
2200     int                       nb_level,
2201     int                      *levels,
2202     backup_support_option_t  *bsu)
2203 {
2204     dle_t *dle = est->dle;
2205     int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
2206     pid_t dumppid;
2207     off_t size = (off_t)-1;
2208     FILE *dumpout;
2209     FILE *dumperr;
2210     char *line = NULL;
2211     char *cmd = NULL;
2212     char *cmdline;
2213     int  i, j, k;
2214     char **argvchild;
2215     char *newoptstr = NULL;
2216     off_t size1, size2;
2217     times_t start_time;
2218     char *qdisk = quote_string(dle->disk);
2219     char *qamdevice = quote_string(dle->device);
2220     amwait_t wait_status;
2221     char levelstr[NUM_STR_SIZE];
2222     GSList   *scriptlist;
2223     script_t *script;
2224     char     *errmsg = NULL;
2225
2226     cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
2227
2228     i=0;
2229     k = application_property_argv_size(dle);
2230     for (scriptlist = dle->scriptlist; scriptlist != NULL;
2231          scriptlist = scriptlist->next) {
2232         script = (script_t *)scriptlist->data;
2233         if (script->result && script->result->proplist) {
2234             k += property_argv_size(script->result->proplist);
2235         }
2236     }
2237     argvchild = g_new0(char *, 16 + k + DUMP_LEVELS*2);
2238     argvchild[i++] = dle->program;
2239     argvchild[i++] = "estimate";
2240     if (bsu->message_line == 1) {
2241         argvchild[i++] = "--message";
2242         argvchild[i++] = "line";
2243     }
2244     if (g_options->config && bsu->config == 1) {
2245         argvchild[i++] = "--config";
2246         argvchild[i++] = g_options->config;
2247     }
2248     if (g_options->hostname && bsu->host == 1) {
2249         argvchild[i++] = "--host";
2250         argvchild[i++] = g_options->hostname;
2251     }
2252     argvchild[i++] = "--device";
2253     argvchild[i++] = dle->device;
2254     if (dle->disk && bsu->disk == 1) {
2255         argvchild[i++] = "--disk";
2256         argvchild[i++] = dle->disk;
2257     }
2258     for (j=0; j < nb_level; j++) {
2259         argvchild[i++] = "--level";
2260         g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
2261         argvchild[i++] = stralloc(levelstr);
2262     }
2263     if (dle->calcsize && bsu->calcsize) {
2264         argvchild[i++] = "--calcsize";
2265     }
2266
2267     i += application_property_add_to_argv(&argvchild[i], dle, bsu);
2268
2269     for (scriptlist = dle->scriptlist; scriptlist != NULL;
2270          scriptlist = scriptlist->next) {
2271         script = (script_t *)scriptlist->data;
2272         if (script->result && script->result->proplist) {
2273             i += property_add_to_argv(&argvchild[i],
2274                                       script->result->proplist);
2275         }
2276     }
2277
2278     argvchild[i] = NULL;
2279
2280     cmdline = stralloc(cmd);
2281     for(j = 1; j < i; j++)
2282         cmdline = vstrextend(&cmdline, " ", argvchild[j], NULL);
2283     dbprintf("running: \"%s\"\n", cmdline);
2284     amfree(cmdline);
2285
2286     if (pipe(pipeerrfd) < 0) {
2287         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2288                             strerror(errno));
2289         goto common_exit;
2290     }
2291
2292     if (pipe(pipeinfd) < 0) {
2293         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2294                             strerror(errno));
2295         goto common_exit;
2296     }
2297
2298     if (pipe(pipeoutfd) < 0) {
2299         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2300                             strerror(errno));
2301         goto common_exit;
2302     }
2303
2304     start_time = curclock();
2305
2306     switch(dumppid = fork()) {
2307     case -1:
2308       size = (off_t)-1;
2309       goto common_exit;
2310     default:
2311       break; /* parent */
2312     case 0:
2313       dup2(pipeinfd[0], 0);
2314       dup2(pipeoutfd[1], 1);
2315       dup2(pipeerrfd[1], 2);
2316       aclose(pipeinfd[1]);
2317       aclose(pipeoutfd[0]);
2318       aclose(pipeerrfd[0]);
2319       safe_fd(-1, 0);
2320
2321       execve(cmd, argvchild, safe_env());
2322       error(_("exec %s failed: %s"), cmd, strerror(errno));
2323       /*NOTREACHED*/
2324     }
2325     amfree(newoptstr);
2326
2327     aclose(pipeinfd[0]);
2328     aclose(pipeoutfd[1]);
2329     aclose(pipeerrfd[1]);
2330     aclose(pipeinfd[1]);
2331
2332     dumpout = fdopen(pipeoutfd[0],"r");
2333     if (!dumpout) {
2334         error(_("Can't fdopen: %s"), strerror(errno));
2335         /*NOTREACHED*/
2336     }
2337
2338     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2339         long long size1_ = (long long)0;
2340         long long size2_ = (long long)0;
2341         int  level = 0;
2342         if (line[0] == '\0')
2343             continue;
2344         dbprintf("%s\n", line);
2345         if (strncmp(line,"ERROR ", 6) == 0) {
2346             char *errmsg, *qerrmsg;
2347
2348             errmsg = stralloc(line+6);
2349             qerrmsg = quote_string(errmsg);
2350             dbprintf(_("errmsg is %s\n"), errmsg);
2351             g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2352             amfree(qerrmsg);
2353             continue;
2354         }
2355         i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
2356         if (i != 3) {
2357             i = sscanf(line, "%lld %lld", &size1_, &size2_);
2358             level = levels[0];
2359             if (i != 2) {
2360                 char *errmsg, *qerrmsg;
2361
2362                 errmsg = vstrallocf(_("bad line %s"), line);
2363                 qerrmsg = quote_string(errmsg);
2364                 dbprintf(_("errmsg is %s\n"), errmsg);
2365                 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2366                 amfree(qerrmsg);
2367                 continue;
2368             }
2369         }
2370         size1 = (off_t)size1_;
2371         size2 = (off_t)size2_;
2372         if (size1 <= 0 || size2 <=0)
2373             size = -1;
2374         else if (size1 * size2 > 0)
2375             size = size1 * size2;
2376         dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2377                  qamdevice,
2378                  level,
2379                  (long long)size);
2380         g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
2381     }
2382     amfree(line);
2383
2384     dumperr = fdopen(pipeerrfd[0],"r");
2385     if (!dumperr) {
2386         error(_("Can't fdopen: %s"), strerror(errno));
2387         /*NOTREACHED*/
2388     }
2389
2390     while ((line = agets(dumperr)) != NULL) {
2391             if (strlen(line) > 0) {
2392             char *err =  g_strdup_printf(_("Application '%s': %s"),
2393                                          dle->program, line);
2394             char *qerr = quote_string(err);
2395             for (j=0; j < nb_level; j++) {
2396                 fprintf(stdout, "%s %d ERROR %s\n",
2397                         est->qamname, levels[j], qerr);
2398             }
2399             dbprintf("ERROR %s", qerr);
2400             amfree(err);
2401             amfree(qerr);
2402         }
2403         amfree(line);
2404     }
2405
2406     dbprintf(".....\n");
2407     if (nb_level == 1) {
2408         dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
2409                  levels[0], walltime_str(timessub(curclock(), start_time)));
2410     } else {
2411         dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
2412                  walltime_str(timessub(curclock(), start_time)));
2413     }
2414
2415     kill(-dumppid, SIGTERM);
2416
2417     dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2418     waitpid(dumppid, &wait_status, 0);
2419     if (WIFSIGNALED(wait_status)) {
2420         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2421                             cmd, WTERMSIG(wait_status), dbfn());
2422     } else if (WIFEXITED(wait_status)) {
2423         if (WEXITSTATUS(wait_status) != 0) {
2424             errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2425                                 WEXITSTATUS(wait_status), dbfn());
2426         } else {
2427             /* Normal exit */
2428         }
2429     } else {
2430         errmsg = vstrallocf(_("%s got bad exit: see %s"),
2431                             cmd, dbfn());
2432     }
2433     dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2434
2435     afclose(dumpout);
2436     afclose(dumperr);
2437
2438 common_exit:
2439
2440     amfree(cmd);
2441     amfree(newoptstr);
2442     amfree(qdisk);
2443     amfree(qamdevice);
2444     if (errmsg) {
2445         char *qerrmsg = quote_string(errmsg);
2446         dbprintf(_("errmsg is %s\n"), errmsg);
2447         for (j=0; j < nb_level; j++) {
2448             g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
2449         }
2450         amfree(errmsg);
2451         amfree(qerrmsg);
2452     }
2453     return size;
2454 }
2455
2456
2457 /*
2458  * Returns the value of the first integer in a string.
2459  */
2460
2461 double
2462 first_num(
2463     char *      str)
2464 {
2465     char *start;
2466     int ch;
2467     double d;
2468
2469     ch = *str++;
2470     while(ch && !isdigit(ch)) ch = *str++;
2471     start = str-1;
2472     while(isdigit(ch) || (ch == '.')) ch = *str++;
2473     str[-1] = '\0';
2474     d = atof(start);
2475     str[-1] = (char)ch;
2476     return d;
2477 }
2478
2479
2480 /*
2481  * Checks the dump output line against the error and size regex tables.
2482  */
2483
2484 off_t
2485 handle_dumpline(
2486     char *      str)
2487 {
2488     regex_scale_t *rp;
2489     double size;
2490
2491     /* check for size match */
2492     /*@ignore@*/
2493     for(rp = re_size; rp->regex != NULL; rp++) {
2494         if(match(rp->regex, str)) {
2495             size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2496             if(size < 0.0)
2497                 size = 1.0;             /* found on NeXT -- sigh */
2498             return (off_t)size;
2499         }
2500     }
2501     /*@end@*/
2502     return (off_t)-1;
2503 }