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