Merge branch 'upstream'
[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 #if defined(USE_DBMALLOC)
173     malloc_size_1 = malloc_inuse(&malloc_hist_1);
174 #endif
175
176     erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
177     dbopen(DBG_SUBDIR_CLIENT);
178     startclock();
179     dbprintf(_("version %s\n"), version());
180
181     our_features = am_init_feature_set();
182     our_feature_string = am_feature_to_string(our_features);
183
184     config_init(CONFIG_INIT_CLIENT, NULL);
185
186     check_running_as(RUNNING_AS_CLIENT_LOGIN);
187
188     /* handle all service requests */
189
190     for(; (line = agets(stdin)) != NULL; free(line)) {
191         if (line[0] == '\0')
192             continue;
193         if(strncmp_const(line, "OPTIONS ") == 0) {
194             g_options = parse_g_options(line+8, 1);
195             if(!g_options->hostname) {
196                 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
197                 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
198                 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
199             }
200
201             g_printf("OPTIONS ");
202             if(am_has_feature(g_options->features, fe_rep_options_features)) {
203                 g_printf("features=%s;", our_feature_string);
204             }
205             if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
206                 g_printf("maxdumps=%d;", g_options->maxdumps);
207             }
208             if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
209                 g_printf("hostname=%s;", g_options->hostname);
210             }
211             g_printf("\n");
212             fflush(stdout);
213
214             if (g_options->config) {
215                 /* overlay this configuration on the existing (nameless) configuration */
216                 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
217                             g_options->config);
218
219                 dbrename(config_name, DBG_SUBDIR_CLIENT);
220             }
221
222             continue;
223         }
224
225         if (amandates_read == 0) {
226             amandates_file = getconf_str(CNF_AMANDATES);
227             if(!start_amandates(amandates_file, 0))
228                 error("error [opening %s: %s]", amandates_file,
229                       strerror(errno));
230             amandates_read = 1;
231         }
232
233         s = line;
234         ch = *s++;
235
236         skip_whitespace(s, ch);                 /* find the program name */
237         if(ch == '\0') {
238             err_extra = stralloc(_("no program name"));
239             goto err;                           /* no program name */
240         }
241         prog = s - 1;
242         skip_non_whitespace(s, ch);
243         s[-1] = '\0';
244
245         program_is_backup_api=0;
246         if(strncmp_const(prog, "CALCSIZE") == 0) {
247             skip_whitespace(s, ch);             /* find the program name */
248             if(ch == '\0') {
249                 err_extra = stralloc(_("no program name"));
250                 goto err;
251             }
252             calcprog = s - 1;
253             skip_non_whitespace(s, ch);
254             s[-1] = '\0';
255             if (strcmp(calcprog,"BACKUP") == 0) {
256                 program_is_backup_api=1;
257                 skip_whitespace(s, ch);         /* find dumper name */
258                 if (ch == '\0') {
259                     goto err;                   /* no program */
260                 }
261                 calcprog = s - 1;
262                 skip_non_whitespace(s, ch);
263                 s[-1] = '\0';
264             }
265         }
266         else {
267             calcprog = NULL;
268             if (strcmp(prog,"BACKUP") == 0) {
269                 program_is_backup_api=1;
270                 skip_whitespace(s, ch);         /* find dumper name */
271                 if (ch == '\0') {
272                     goto err;                   /* no program */
273                 }
274                 prog = s - 1;
275                 skip_non_whitespace(s, ch);
276                 s[-1] = '\0';
277             }
278         }
279
280         skip_whitespace(s, ch);                 /* find the disk name */
281         if(ch == '\0') {
282             err_extra = stralloc(_("no disk name"));
283             goto err;                           /* no disk name */
284         }
285
286         if (qdisk != NULL)
287             amfree(qdisk);
288         if (disk != NULL)
289             amfree(disk);
290
291         fp = s - 1;
292         skip_quoted_string(s, ch);
293         s[-1] = '\0';                           /* terminate the disk name */
294         qdisk = stralloc(fp);
295         disk = unquote_string(qdisk);
296
297         skip_whitespace(s, ch);                 /* find the device or level */
298         if (ch == '\0') {
299             err_extra = stralloc(_("bad level"));
300             goto err;
301         }
302         if(!isdigit((int)s[-1])) {
303             fp = s - 1;
304             skip_quoted_string(s, ch);
305             s[-1] = '\0';
306             qamdevice = stralloc(fp);
307             amdevice = unquote_string(qamdevice);
308             skip_whitespace(s, ch);             /* find level number */
309         }
310         else {
311             amdevice = stralloc(disk);
312             qamdevice = stralloc(qdisk);
313         }
314
315                                                 /* find the level number */
316         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
317             err_extra = stralloc(_("bad level"));
318             goto err;                           /* bad level */
319         }
320         if (level < 0 || level >= DUMP_LEVELS) {
321             err_extra = stralloc(_("bad level"));
322             goto err;
323         }
324         skip_integer(s, ch);
325
326         skip_whitespace(s, ch);                 /* find the dump date */
327         if(ch == '\0') {
328             err_extra = stralloc(_("no dumpdate"));
329             goto err;                           /* no dumpdate */
330         }
331         dumpdate = s - 1;
332         skip_non_whitespace(s, ch);
333         s[-1] = '\0';
334         (void)dumpdate;                         /* XXX: Set but not used */
335
336         spindle = 0;                            /* default spindle */
337
338         skip_whitespace(s, ch);                 /* find the spindle */
339         if(ch != '\0') {
340             if(sscanf(s - 1, "%d", &spindle) != 1) {
341                 err_extra = stralloc(_("bad spindle"));
342                 goto err;                       /* bad spindle */
343             }
344             skip_integer(s, ch);
345
346             skip_whitespace(s, ch);             /* find the parameters */
347             if(ch != '\0') {
348                 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
349                     options = parse_options(s + 8,
350                                             disk,
351                                             amdevice,
352                                             g_options->features,
353                                             0);
354                 }
355                 else {
356                     options = alloc(SIZEOF(option_t));
357                     init_options(options);
358                     while (ch != '\0') {
359                         if(strncmp_const(s-1, "exclude-file=") == 0) {
360                             qlist = unquote_string(s+12);
361                             options->exclude_file =
362                                 append_sl(options->exclude_file, qlist);
363                             amfree(qlist);
364                         } else if(strncmp_const(s-1, "exclude-list=") == 0) {
365                             qlist = unquote_string(s+12);
366                             options->exclude_list =
367                                 append_sl(options->exclude_list, qlist);
368                             amfree(qlist);
369                         } else if(strncmp_const(s-1, "include-file=") == 0) {
370                             qlist = unquote_string(s+12);
371                             options->include_file =
372                                 append_sl(options->include_file, qlist);
373                             amfree(qlist);
374                         } else if(strncmp_const(s-1, "include-list=") == 0) {
375                             qlist = unquote_string(s+12);
376                             options->include_list =
377                                 append_sl(options->include_list, qlist);
378                             amfree(qlist);
379                         } else {
380                             err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
381                             goto err;           /* should have gotten to end */
382                         }
383                         skip_quoted_string(s, ch);
384                         skip_whitespace(s, ch); /* find the inclusion list */
385                         amfree(qlist);
386                     }
387                 }
388             }
389             else {
390                 options = alloc(SIZEOF(option_t));
391                 init_options(options);
392             }
393         }
394         else {
395             options = alloc(SIZEOF(option_t));
396             init_options(options);
397         }
398
399         /*@ignore@*/
400         add_diskest(disk, amdevice, level, spindle, program_is_backup_api, prog, calcprog, options);
401         /*@end@*/
402         amfree(disk);
403         amfree(qdisk);
404         amfree(amdevice);
405         amfree(qamdevice);
406     }
407     if (g_options == NULL) {
408         printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
409         error(_("Missing OPTIONS line in sendsize input\n"));
410         /*NOTREACHED*/
411     }
412     amfree(line);
413
414     finish_amandates();
415     free_amandates();
416
417     dumpsrunning = 0;
418     need_wait = 0;
419     done = 0;
420     while(! done) {
421         done = 1;
422         /*
423          * See if we need to wait for a child before we can do anything
424          * else in this pass.
425          */
426         if(need_wait) {
427             pid_t child_pid;
428             amwait_t child_status;
429
430             need_wait = 0;
431             dbprintf(_("waiting for any estimate child: %d running\n"),
432                       dumpsrunning);
433             child_pid = wait(&child_status);
434             if(child_pid == -1) {
435                 error(_("wait failed: %s"), strerror(errno));
436                 /*NOTREACHED*/
437             }
438
439             if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
440                 char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
441                 char *child_status_str = str_exit_status(child_name, child_status);
442                 dbprintf("%s\n", child_status_str);
443                 amfree(child_status_str);
444                 amfree(child_name);
445             }
446
447             /*
448              * Find the child and mark it done.
449              */
450             for(est = est_list; est != NULL; est = est->next) {
451                 if(est->child == child_pid) {
452                     break;
453                 }
454             }
455             if(est == NULL) {
456                 dbprintf(_("unexpected child %ld\n"), (long)child_pid);
457             } else {
458                 est->done = 1;
459                 est->child = 0;
460                 dumpsrunning--;
461             }
462         }
463         /*
464          * If we are already running the maximum number of children
465          * go back and wait until one of them finishes.
466          */
467         if(dumpsrunning >= g_options->maxdumps) {
468             done = 0;
469             need_wait = 1;
470             continue;                           /* have to wait first */
471         }
472         /*
473          * Find a new child to start.
474          */
475         for(est = est_list; est != NULL; est = est->next) {
476             if(est->done == 0) {
477                 done = 0;                       /* more to do */
478             }
479             if(est->child != 0 || est->done) {
480                 continue;                       /* child is running or done */
481             }
482             /*
483              * Make sure there is no spindle conflict.
484              */
485             if(est->spindle != -1) {
486                 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
487                     if(est1->child == 0 || est == est1 || est1->done) {
488                         /*
489                          * Ignore anything not yet started, ourself,
490                          * and anything completed.
491                          */
492                         continue;
493                     }
494                     if(est1->spindle == est->spindle) {
495                         break;                  /* oops -- they match */
496                     }
497                 }
498                 if(est1 != NULL) {
499                     continue;                   /* spindle conflict */
500                 }
501             }
502             break;                              /* start this estimate */
503         }
504         if(est == NULL) {
505             if(dumpsrunning > 0) {
506                 need_wait = 1;                  /* nothing to do but wait */
507             }
508         } else {
509             done = 0;
510             if((est->child = fork()) == 0) {
511                 calc_estimates(est);            /* child does the estimate */
512                 exit(0);
513             } else if(est->child == -1) {
514                 error(_("calc_estimates fork failed: %s"), strerror(errno));
515                 /*NOTREACHED*/
516             }
517             dumpsrunning++;                     /* parent */
518         }
519     }
520
521     est_prev = NULL;
522     for(est = est_list; est != NULL; est = est->next) {
523         free_estimates(est);
524         amfree(est_prev);
525         est_prev = est;
526     }
527     amfree(est_prev);
528     amfree(our_feature_string);
529     am_release_feature_set(our_features);
530     our_features = NULL;
531     free_g_options(g_options);
532
533     dbclose();
534     return 0;
535  err:
536     g_printf(_("FORMAT ERROR IN REQUEST PACKET\n"));
537     if (err_extra) {
538         dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
539         amfree(err_extra);
540     } else {
541         dbprintf(_("REQ packet is bogus\n"));
542     }
543     dbclose();
544     return 1;
545 }
546
547
548 void
549 add_diskest(
550     char *      disk,
551     char *      amdevice,
552     int         level,
553     int         spindle,
554     int         program_is_backup_api,
555     char *      prog,
556     char *      calcprog,
557     option_t *  options)
558 {
559     disk_estimates_t *newp, *curp;
560     amandates_t *amdp;
561     int dumplev, estlev;
562     time_t dumpdate;
563
564     if (level < 0)
565         level = 0;
566     if (level >= DUMP_LEVELS)
567         level = DUMP_LEVELS - 1;
568
569     for(curp = est_list; curp != NULL; curp = curp->next) {
570         if(strcmp(curp->amname, disk) == 0) {
571             /* already have disk info, just note the level request */
572             curp->est[level].needestimate = 1;
573             if(options) {
574                 free_sl(options->exclude_file);
575                 free_sl(options->exclude_list);
576                 free_sl(options->include_file);
577                 free_sl(options->include_list);
578                 amfree(options->auth);
579                 amfree(options->str);
580                 amfree(options);
581             }
582             return;
583         }
584     }
585
586     newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
587     memset(newp, 0, SIZEOF(*newp));
588     newp->next = est_list;
589     est_list = newp;
590     newp->amname = stralloc(disk);
591     newp->qamname = quote_string(disk);
592     newp->amdevice = stralloc(amdevice);
593     newp->qamdevice = quote_string(amdevice);
594     newp->dirname = amname_to_dirname(newp->amdevice);
595     newp->qdirname = quote_string(newp->dirname);
596     newp->program = stralloc(prog);
597     if(calcprog != NULL)
598         newp->calcprog = stralloc(calcprog);
599     else
600         newp->calcprog = NULL;
601     newp->program_is_backup_api = program_is_backup_api;
602     newp->spindle = spindle;
603     newp->est[level].needestimate = 1;
604     newp->options = options;
605
606     /* fill in dump-since dates */
607
608     amdp = amandates_lookup(newp->amname);
609
610     newp->est[0].dumpsince = EPOCH;
611     for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
612         dumpdate = amdp->dates[dumplev];
613         for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
614             if(dumpdate > newp->est[estlev].dumpsince)
615                 newp->est[estlev].dumpsince = dumpdate;
616         }
617     }
618 }
619
620
621 void
622 free_estimates(
623     disk_estimates_t *  est)
624 {
625     amfree(est->amname);
626     amfree(est->qamname);
627     amfree(est->amdevice);
628     amfree(est->qamdevice);
629     amfree(est->dirname);
630     amfree(est->qdirname);
631     amfree(est->program);
632     if(est->options) {
633         free_sl(est->options->exclude_file);
634         free_sl(est->options->exclude_list);
635         free_sl(est->options->include_file);
636         free_sl(est->options->include_list);
637         amfree(est->options->str);
638         amfree(est->options->auth);
639         amfree(est->options);
640     }
641 }
642
643 /*
644  * ------------------------------------------------------------------------
645  *
646  */
647
648 void
649 calc_estimates(
650     disk_estimates_t *  est)
651 {
652     dbprintf(_("calculating for amname %s, dirname %s, spindle %d\n"),
653               est->qamname, est->qdirname, est->spindle);
654         
655     if(est->program_is_backup_api ==  1)
656         backup_api_calc_estimate(est);
657     else
658 #ifndef USE_GENERIC_CALCSIZE
659     if(strcmp(est->program, "DUMP") == 0)
660         dump_calc_estimates(est);
661     else
662 #endif
663 #ifdef SAMBA_CLIENT
664       if (strcmp(est->program, "GNUTAR") == 0 &&
665           est->amdevice[0] == '/' && est->amdevice[1] == '/')
666         smbtar_calc_estimates(est);
667       else
668 #endif
669 #ifdef GNUTAR
670         if (strcmp(est->program, "GNUTAR") == 0)
671           gnutar_calc_estimates(est);
672         else
673 #endif
674 #ifdef SAMBA_CLIENT
675           if (est->amdevice[0] == '/' && est->amdevice[1] == '/')
676             dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
677                       est->qamname, est->qdirname);
678           else
679 #endif
680             generic_calc_estimates(est);
681
682     dbprintf(_("done with amname %s dirname %s spindle %d\n"),
683               est->qamname, est->qdirname, est->spindle);
684 }
685
686 /*
687  * ------------------------------------------------------------------------
688  *
689  */
690
691 /* local functions */
692 off_t getsize_dump(char *disk, char *amdevice, int level, option_t *options,
693                    char **errmsg);
694 off_t getsize_smbtar(char *disk, char *amdevice, int level, option_t *options,
695                      char **errmsg);
696 off_t getsize_gnutar(char *disk, char *amdevice, int level,
697                        option_t *options, time_t dumpsince, char **errmsg);
698 off_t getsize_backup_api(char *program, char *disk, char *amdevice, int level,
699                         option_t *options, time_t dumpsince, char **errmsg);
700 off_t handle_dumpline(char *str);
701 double first_num(char *str);
702
703 void
704 backup_api_calc_estimate(
705     disk_estimates_t *  est)
706 {
707     int    level;
708     off_t  size;
709     char  *errmsg = NULL, *qerrmsg;
710
711     for(level = 0; level < DUMP_LEVELS; level++) {
712         if (est->est[level].needestimate) {
713             dbprintf(_("getting size via application API for %s %s level %d\n"),
714                       est->qamname, est->qamdevice, level);
715             size = getsize_backup_api(est->program, est->amname, est->amdevice,
716                                       level, est->options,
717                                       est->est[level].dumpsince, &errmsg);
718
719             amflock(1, "size");
720
721             g_printf(_("%s %d SIZE %lld\n"), est->qamname, level,
722                    (long long)size);
723             if (errmsg && errmsg[0] != '\0') {
724                 if(am_has_feature(g_options->features,
725                                   fe_rep_sendsize_quoted_error)) {
726                     qerrmsg = quote_string(errmsg);
727                     dbprintf(_("errmsg is %s\n"), errmsg);
728                     g_printf(_("%s %d ERROR %s\n"),
729                            est->qamname, level, qerrmsg);
730                     amfree(qerrmsg);
731                 }
732             }
733             amfree(errmsg);
734             fflush(stdout);
735
736             amfunlock(1, "size");
737         }
738     }
739 }
740
741
742 void
743 generic_calc_estimates(
744     disk_estimates_t *  est)
745 {
746     int pipefd = -1, nullfd = -1;
747     char *cmd;
748     char *cmdline;
749     char *my_argv[DUMP_LEVELS*2+22];
750     char number[NUM_STR_SIZE];
751     int i, level, my_argc;
752     pid_t calcpid;
753     int nb_exclude = 0;
754     int nb_include = 0;
755     char *file_exclude = NULL;
756     char *file_include = NULL;
757     times_t start_time;
758     FILE *dumpout = NULL;
759     off_t size = (off_t)1;
760     char *line = NULL;
761     char *match_expr;
762     amwait_t wait_status;
763     char *errmsg = NULL, *qerrmsg;
764     char tmppath[PATH_MAX];
765
766     cmd = vstralloc(amlibexecdir, "/", "calcsize", versionsuffix(), NULL);
767
768     my_argc = 0;
769
770     my_argv[my_argc++] = stralloc("calcsize");
771     if (g_options->config)
772         my_argv[my_argc++] = stralloc(g_options->config);
773     else
774         my_argv[my_argc++] = stralloc("NOCONFIG");
775
776     my_argv[my_argc++] = stralloc(est->calcprog);
777
778     my_argv[my_argc++] = stralloc(est->amname);
779     canonicalize_pathname(est->dirname, tmppath);
780     my_argv[my_argc++] = stralloc(tmppath);
781
782
783     if(est->options->exclude_file)
784         nb_exclude += est->options->exclude_file->nb_element;
785     if(est->options->exclude_list)
786         nb_exclude += est->options->exclude_list->nb_element;
787     if(est->options->include_file)
788         nb_include += est->options->include_file->nb_element;
789     if(est->options->include_list)
790         nb_include += est->options->include_list->nb_element;
791
792     if(nb_exclude > 0)
793         file_exclude = build_exclude(est->amname,
794                 est->amdevice, est->options, 0);
795     if(nb_include > 0)
796         file_include = build_include(est->amname,
797                 est->amdevice, est->options, 0);
798
799     if(file_exclude) {
800         my_argv[my_argc++] = stralloc("-X");
801         my_argv[my_argc++] = file_exclude;
802     }
803
804     if(file_include) {
805         my_argv[my_argc++] = stralloc("-I");
806         my_argv[my_argc++] = file_include;
807     }
808     start_time = curclock();
809
810     cmdline = stralloc(my_argv[0]);
811     for(i = 1; i < my_argc; i++)
812         cmdline = vstrextend(&cmdline, " ", my_argv[i], NULL);
813     dbprintf(_("running: \"%s\"\n"), cmdline);
814     amfree(cmdline);
815
816     for(level = 0; level < DUMP_LEVELS; level++) {
817         if(est->est[level].needestimate) {
818             g_snprintf(number, SIZEOF(number), "%d", level);
819             my_argv[my_argc++] = stralloc(number); 
820             dbprintf(" %s", number);
821             g_snprintf(number, SIZEOF(number),
822                         "%ld", (long)est->est[level].dumpsince);
823             my_argv[my_argc++] = stralloc(number); 
824             dbprintf(" %s", number);
825         }
826     }
827     my_argv[my_argc] = NULL;
828     dbprintf("\n");
829
830     fflush(stderr); fflush(stdout);
831
832     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
833         errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
834                             strerror(errno));
835         dbprintf("%s\n", errmsg);
836         goto common_exit;
837     }
838
839     calcpid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
840     amfree(cmd);
841
842     dumpout = fdopen(pipefd,"r");
843     if (!dumpout) {
844         error(_("Can't fdopen: %s"), strerror(errno));
845         /*NOTREACHED*/
846     }
847     match_expr = vstralloc(est->qamname," %d SIZE %lld", NULL);
848     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
849         long long size_ = (long long)0;
850         if (line[0] == '\0')
851             continue;
852         if(sscanf(line, match_expr, &level, &size_) == 2) {
853             g_printf("%s\n", line); /* write to amandad */
854             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
855                       est->qamname,
856                       level,
857                       size_);
858         }
859         size = (off_t)size_;
860     }
861     amfree(match_expr);
862
863     dbprintf(_("waiting for %s %s child (pid=%d)\n"),
864               my_argv[0], est->qamdevice, (int)calcpid);
865     waitpid(calcpid, &wait_status, 0);
866     if (WIFSIGNALED(wait_status)) {
867         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
868                             "calcsize", WTERMSIG(wait_status),
869                             dbfn());
870     } else if (WIFEXITED(wait_status)) {
871         if (WEXITSTATUS(wait_status) != 0) {
872             errmsg = vstrallocf(_("%s exited with status %d: see %s"),
873                                 "calcsize", WEXITSTATUS(wait_status),
874                                 dbfn());
875         } else {
876             /* Normal exit */
877         }
878     } else {
879         errmsg = vstrallocf(_("%s got bad exit: see %s"),
880                              "calcsize", dbfn());
881     }
882     dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
883               my_argv[0], est->qamdevice,
884               (int)calcpid, WEXITSTATUS(wait_status));
885
886     dbprintf(_(".....\n"));
887     dbprintf(_("estimate time for %s: %s\n"),
888               est->qamname,
889               walltime_str(timessub(curclock(), start_time)));
890
891 common_exit:
892     if (errmsg && errmsg[0] != '\0') {
893         if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
894             qerrmsg = quote_string(errmsg);
895             dbprintf(_("errmsg is %s\n"), errmsg);
896             g_printf("%s %d ERROR %s\n",
897                     est->qamname, 0, qerrmsg);
898             amfree(qerrmsg);
899         }
900     }
901     amfree(errmsg);
902     for(i = 0; i < my_argc; i++) {
903         amfree(my_argv[i]);
904     }
905     amfree(cmd);
906 }
907
908
909 void
910 dump_calc_estimates(
911     disk_estimates_t *  est)
912 {
913     int level;
914     off_t size;
915     char *errmsg=NULL, *qerrmsg;
916
917     for(level = 0; level < DUMP_LEVELS; level++) {
918         if(est->est[level].needestimate) {
919             dbprintf(_("getting size via dump for %s level %d\n"),
920                       est->qamname, level);
921             size = getsize_dump(est->amname, est->amdevice,
922                                 level, est->options, &errmsg);
923
924             amflock(1, "size");
925
926             g_printf(_("%s %d SIZE %lld\n"),
927                    est->qamname, level, (long long)size);
928             if (errmsg && errmsg[0] != '\0') {
929                 if(am_has_feature(g_options->features,
930                                   fe_rep_sendsize_quoted_error)) {
931                     qerrmsg = quote_string(errmsg);
932                     dbprintf(_("errmsg is %s\n"), errmsg);
933                     g_printf("%s %d ERROR %s\n",
934                            est->qamname, level, qerrmsg);
935                     amfree(qerrmsg);
936                 }
937             }
938             amfree(errmsg);
939             fflush(stdout);
940
941             amfunlock(1, "size");
942         }
943     }
944 }
945
946 #ifdef SAMBA_CLIENT
947 void
948 smbtar_calc_estimates(
949     disk_estimates_t *  est)
950 {
951     int level;
952     off_t size;
953     char  *errmsg = NULL, *qerrmsg;
954
955     for(level = 0; level < DUMP_LEVELS; level++) {
956         if(est->est[level].needestimate) {
957             dbprintf(_("getting size via smbclient for %s level %d\n"),
958                       est->qamname, level);
959             size = getsize_smbtar(est->amname, est->amdevice, level,
960                                   est->options, &errmsg);
961
962             amflock(1, "size");
963
964             g_printf(_("%s %d SIZE %lld\n"),
965                    est->qamname, level, (long long)size);
966             if (errmsg && errmsg[0] != '\0') {
967                 if(am_has_feature(g_options->features,
968                                   fe_rep_sendsize_quoted_error)) {
969                     qerrmsg = quote_string(errmsg);
970                     dbprintf(_("errmsg is %s\n"), errmsg);
971                     g_printf("%s %d ERROR %s\n",
972                            est->qamname, level, qerrmsg);
973                     amfree(qerrmsg);
974                 }
975             }
976             amfree(errmsg);
977             fflush(stdout);
978
979             amfunlock(1, "size");
980         }
981     }
982 }
983 #endif
984
985 #ifdef GNUTAR
986 void
987 gnutar_calc_estimates(
988     disk_estimates_t *  est)
989 {
990     int level;
991     off_t size;
992     char *errmsg = NULL, *qerrmsg;
993
994     for(level = 0; level < DUMP_LEVELS; level++) {
995         if (est->est[level].needestimate) {
996             dbprintf(_("getting size via gnutar for %s level %d\n"),
997                       est->qamname, level);
998             size = getsize_gnutar(est->amname, est->amdevice, level,
999                                   est->options, est->est[level].dumpsince,
1000                                   &errmsg);
1001
1002             amflock(1, "size");
1003
1004             g_printf(_("%s %d SIZE %lld\n"),
1005                    est->qamname, level, (long long)size);
1006             if (errmsg && errmsg[0] != '\0') {
1007                 if(am_has_feature(g_options->features,
1008                                   fe_rep_sendsize_quoted_error)) {
1009                     qerrmsg = quote_string(errmsg);
1010                     dbprintf(_("errmsg is %s\n"), errmsg);
1011                     g_printf(_("%s %d ERROR %s\n"),
1012                            est->qamname, level, qerrmsg);
1013                     amfree(qerrmsg);
1014                 }
1015             }
1016             amfree(errmsg);
1017             fflush(stdout);
1018
1019             amfunlock(1, "size");
1020         }
1021     }
1022 }
1023 #endif
1024
1025 typedef struct regex_s {
1026     char *regex;
1027     int scale;
1028 } regex_scale_t;
1029
1030 /*@ignore@*/
1031 regex_scale_t re_size[] = {
1032 #ifdef DUMP
1033     {"  DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1034     {"  DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1035     {"  DUMP: [Ee]stimated [0-9][0-9]* bytes", 1},              /* Ultrix 4.4 */
1036     {" UFSDUMP: estimated [0-9][0-9]* blocks", 512},            /* NEC EWS-UX */
1037     {"dump: Estimate: [0-9][0-9]* tape blocks", 1024},              /* OSF/1 */
1038     {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1039     {"backup: estimated [0-9][0-9]* 1k blocks", 1024},                /* AIX */
1040     {"backup: estimated [0-9][0-9]* tape blocks", 1024},              /* AIX */
1041     {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024},  /* AIX */
1042     {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024},  /* AIX */
1043     {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1044                                                               /* DU 4.0 dump  */
1045     {"dump: Dumping [0-9][0-9]* bytes, ", 1},                 /* DU 4.0 vdump */
1046     {"DUMP: estimated [0-9][0-9]* KB output", 1024},                  /* HPUX */
1047     {"DUMP: estimated [0-9][0-9]* KB\\.", 1024},                    /* NetApp */
1048     {"  UFSDUMP: estimated [0-9][0-9]* blocks", 512},                /* Sinix */
1049
1050 #ifdef HAVE_DUMP_ESTIMATE
1051     {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1052                                                            /* DU 3.2g dump -E */
1053     {"^[0-9][0-9]* blocks$", 1024},                        /* DU 4.0 dump  -E */
1054     {"^[0-9][0-9]*$", 1},                               /* Solaris ufsdump -S */
1055 #endif
1056 #endif
1057
1058 #ifdef VDUMP
1059     {"vdump: Dumping [0-9][0-9]* bytes, ", 1},                 /* OSF/1 vdump */
1060 #endif
1061     
1062 #ifdef VXDUMP
1063     {"vxdump: estimated [0-9][0-9]* blocks", 512},           /* HPUX's vxdump */
1064     {"  VXDUMP: estimated [0-9][0-9]* blocks", 512},                 /* Sinix */
1065 #endif
1066
1067 #ifdef XFSDUMP
1068     {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1},  /* Irix 6.2 xfs */
1069 #endif
1070
1071 #ifdef GNUTAR
1072     {"Total bytes written: [0-9][0-9]*", 1},                /* Gnutar client */
1073 #endif
1074
1075 #ifdef SAMBA_CLIENT
1076 #if SAMBA_VERSION >= 2
1077 #define SAMBA_DEBUG_LEVEL "0"
1078     {"Total number of bytes: [0-9][0-9]*", 1},                   /* Samba du */
1079 #else
1080 #define SAMBA_DEBUG_LEVEL "3"
1081     {"Total bytes listed: [0-9][0-9]*", 1},                     /* Samba dir */
1082 #endif
1083 #endif
1084
1085     { NULL, 0 }
1086 };
1087 /*@end@*/
1088
1089 off_t
1090 getsize_dump(
1091     char       *disk,
1092     char       *amdevice,
1093     int         level,
1094     option_t   *options G_GNUC_UNUSED,
1095     char      **errmsg)
1096 {
1097     int pipefd[2], nullfd, stdoutfd, killctl[2];
1098     pid_t dumppid;
1099     off_t size;
1100     FILE *dumpout;
1101     char *dumpkeys = NULL;
1102     char *device = NULL;
1103     char *fstype = NULL;
1104     char *cmd = NULL;
1105     char *name = NULL;
1106     char *line = NULL;
1107     char *rundump_cmd = NULL;
1108     char level_str[NUM_STR_SIZE];
1109     int s;
1110     times_t start_time;
1111     char *qdisk = quote_string(disk);
1112     char *qdevice;
1113     char *config;
1114     amwait_t wait_status;
1115 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1116     int is_rundump = 1;
1117 #endif
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 }