Imported Upstream version 2.6.0p2
[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 G_GNUC_UNUSED,
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     g_snprintf(level_str, SIZEOF(level_str), "%d", level);
1116
1117     device = amname_to_devname(amdevice);
1118     qdevice = quote_string(device);
1119     fstype = amname_to_fstype(amdevice);
1120
1121     dbprintf(_("calculating for device %s with %s\n"),
1122               qdevice, fstype);
1123
1124     cmd = vstralloc(amlibexecdir, "/rundump", versionsuffix(), NULL);
1125     rundump_cmd = stralloc(cmd);
1126     if (g_options->config)
1127         config = g_options->config;
1128     else
1129         config = "NOCONFIG";
1130     if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1131         *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
1132                              strerror(errno));
1133         dbprintf("%s\n", *errmsg);
1134         amfree(cmd);
1135         amfree(rundump_cmd);
1136         amfree(fstype);
1137         amfree(device);
1138         amfree(qdevice);
1139         amfree(qdisk);
1140         return(-1);
1141     }
1142     pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1143     if (pipe(pipefd) < 0) {
1144         *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
1145                              strerror(errno));
1146         dbprintf("%s\n", *errmsg);
1147         amfree(cmd);
1148         amfree(rundump_cmd);
1149         amfree(fstype);
1150         amfree(device);
1151         amfree(qdevice);
1152         amfree(qdisk);
1153         return(-1);
1154     }
1155
1156 #ifdef XFSDUMP                                          /* { */
1157 #ifdef DUMP                                             /* { */
1158     if (strcmp(fstype, "xfs") == 0)
1159 #else                                                   /* } { */
1160     if (1)
1161 #endif                                                  /* } */
1162     {
1163         name = stralloc(" (xfsdump)");
1164         dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
1165                   cmd, name, level_str, qdevice);
1166     }
1167     else
1168 #endif                                                  /* } */
1169 #ifdef VXDUMP                                           /* { */
1170 #ifdef DUMP                                             /* { */
1171     if (strcmp(fstype, "vxfs") == 0)
1172 #else                                                   /* } { */
1173     if (1)
1174 #endif                                                  /* } */
1175     {
1176 #ifdef USE_RUNDUMP
1177         name = stralloc(" (vxdump)");
1178 #else
1179         name = stralloc("");
1180         cmd = newstralloc(cmd, VXDUMP);
1181         config = skip_argument;
1182         is_rundump = 0;
1183 #endif
1184         dumpkeys = vstralloc(level_str, "s", "f", NULL);
1185         dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1186                   cmd, name, dumpkeys, qdevice);
1187     }
1188     else
1189 #endif                                                  /* } */
1190 #ifdef VDUMP                                            /* { */
1191 #ifdef DUMP                                             /* { */
1192     if (strcmp(fstype, "advfs") == 0)
1193 #else                                                   /* } { */
1194     if (1)
1195 #endif                                                  /* } */
1196     {
1197         name = stralloc(" (vdump)");
1198         amfree(device);
1199         amfree(qdevice);
1200         device = amname_to_dirname(amdevice);
1201         qdevice = quote_string(device);
1202         dumpkeys = vstralloc(level_str, "b", "f", NULL);
1203         dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1204                   cmd, name, dumpkeys, qdevice);
1205     }
1206     else
1207 #endif                                                  /* } */
1208 #ifdef DUMP                                             /* { */
1209     if (1) {
1210 # ifdef USE_RUNDUMP                                     /* { */
1211 #  ifdef AIX_BACKUP                                     /* { */
1212         name = stralloc(" (backup)");
1213 #  else                                                 /* } { */
1214         name = vstralloc(" (", DUMP, ")", NULL);
1215 #  endif                                                /* } */
1216 # else                                                  /* } { */
1217         name = stralloc("");
1218         cmd = newstralloc(cmd, DUMP);
1219         config = skip_argument;
1220         is_rundump = 0;
1221 # endif                                                 /* } */
1222
1223 # ifdef AIX_BACKUP                                      /* { */
1224         dumpkeys = vstralloc("-", level_str, "f", NULL);
1225         dbprintf(_("running \"%s%s %s - %s\"\n"),
1226                   cmd, name, dumpkeys, qdevice);
1227 # else                                                  /* } { */
1228 #  ifdef HAVE_DUMP_ESTIMATE
1229 #    define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1230 #  else
1231 #    define PARAM_DUMP_ESTIMATE ""
1232 #  endif
1233 #  ifdef HAVE_HONOR_NODUMP
1234 #    define PARAM_HONOR_NODUMP "h"
1235 #  else
1236 #    define PARAM_HONOR_NODUMP ""
1237 #  endif
1238         dumpkeys = vstralloc(level_str,
1239                              PARAM_DUMP_ESTIMATE,
1240                              PARAM_HONOR_NODUMP,
1241                              "s", "f", NULL);
1242
1243 #  ifdef HAVE_DUMP_ESTIMATE
1244         stdoutfd = pipefd[1];
1245 #  endif
1246
1247 #  ifdef HAVE_HONOR_NODUMP                              /* { */
1248         dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1249                   cmd, name, dumpkeys, qdevice);
1250 #  else                                                 /* } { */
1251         dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1252                   cmd, name, dumpkeys, qdevice);
1253 #  endif                                                /* } */
1254 # endif                                                 /* } */
1255     }
1256     else
1257 #endif                                                  /* } */
1258     {
1259         error(_("no dump program available"));
1260         /*NOTREACHED*/
1261     }
1262
1263     if (pipe(killctl) < 0) {
1264         dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1265         /* Message will be printed later... */
1266         killctl[0] = killctl[1] = -1;
1267     }
1268
1269     start_time = curclock();
1270     switch(dumppid = fork()) {
1271     case -1:
1272         *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1273                              strerror(errno));
1274         dbprintf("%s\n", *errmsg);
1275         amfree(dumpkeys);
1276         amfree(cmd);
1277         amfree(rundump_cmd);
1278         amfree(device);
1279         amfree(qdevice);
1280         amfree(qdisk);
1281         amfree(name);
1282         amfree(fstype);
1283         return -1;
1284     default:
1285         break; 
1286     case 0:     /* child process */
1287         if(SETPGRP == -1)
1288             SETPGRP_FAILED();
1289         else if (killctl[0] == -1 || killctl[1] == -1)
1290             dbprintf(_("Trying without killpgrp\n"));
1291         else {
1292             switch(fork()) {
1293             case -1:
1294                 dbprintf(_("fork failed, trying without killpgrp\n"));
1295                 break;
1296
1297             default:
1298             {
1299                 char *config;
1300                 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp",
1301                                                versionsuffix(), NULL);
1302                 dbprintf(_("running %s\n"), killpgrp_cmd);
1303                 dup2(killctl[0], 0);
1304                 dup2(nullfd, 1);
1305                 dup2(nullfd, 2);
1306                 close(pipefd[0]);
1307                 close(pipefd[1]);
1308                 close(killctl[1]);
1309                 close(nullfd);
1310                 if (g_options->config)
1311                     config = g_options->config;
1312                 else
1313                     config = "NOCONFIG";
1314                 safe_fd(-1, 0);
1315                 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1316                        safe_env());
1317                 dbprintf(_("cannot execute %s: %s\n"),
1318                           killpgrp_cmd, strerror(errno));
1319                 exit(-1);
1320             }
1321
1322             case 0:  /* child process */
1323                 break;
1324             }
1325         }
1326
1327         dup2(nullfd, 0);
1328         dup2(stdoutfd, 1);
1329         dup2(pipefd[1], 2);
1330         aclose(pipefd[0]);
1331         if (killctl[0] != -1)
1332             aclose(killctl[0]);
1333         if (killctl[1] != -1)
1334             aclose(killctl[1]);
1335         safe_fd(-1, 0);
1336
1337 #ifdef XFSDUMP
1338 #ifdef DUMP
1339         if (strcmp(fstype, "xfs") == 0)
1340 #else
1341         if (1)
1342 #endif
1343             if (is_rundump)
1344                 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1345                        level_str, "-", device, (char *)0, safe_env());
1346             else
1347                 execle(cmd, "xfsdump", "-F", "-J", "-l",
1348                        level_str, "-", device, (char *)0, safe_env());
1349         else
1350 #endif
1351 #ifdef VXDUMP
1352 #ifdef DUMP
1353         if (strcmp(fstype, "vxfs") == 0)
1354 #else
1355         if (1)
1356 #endif
1357             if (is_rundump)
1358                 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1359                        "-", device, (char *)0, safe_env());
1360             else
1361                 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1362                        device, (char *)0, safe_env());
1363         else
1364 #endif
1365 #ifdef VDUMP
1366 #ifdef DUMP
1367         if (strcmp(fstype, "advfs") == 0)
1368 #else
1369         if (1)
1370 #endif
1371             if (is_rundump)
1372                 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1373                        device, (char *)0, safe_env());
1374             else
1375                 execle(cmd, "vdump", dumpkeys, "60", "-",
1376                        device, (char *)0, safe_env());
1377         else
1378 #endif
1379 #ifdef DUMP
1380 # ifdef AIX_BACKUP
1381             if (is_rundump)
1382                 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1383                        device, (char *)0, safe_env());
1384             else
1385                 execle(cmd, "backup", dumpkeys, "-",
1386                        device, (char *)0, safe_env());
1387 # else
1388             if (is_rundump) {
1389                 execle(cmd, "rundump", config, "dump", dumpkeys, 
1390 #ifdef HAVE_HONOR_NODUMP
1391                        "0",
1392 #endif
1393                        "1048576", "-", device, (char *)0, safe_env());
1394             } else {
1395                 execle(cmd, "dump", dumpkeys, 
1396 #ifdef HAVE_HONOR_NODUMP
1397                        "0",
1398 #endif
1399                        "1048576", "-", device, (char *)0, safe_env());
1400             }
1401 # endif
1402 #endif
1403         {
1404             error(_("exec %s failed or no dump program available: %s"),
1405                   cmd, strerror(errno));
1406             /*NOTREACHED*/
1407         }
1408     }
1409
1410     amfree(dumpkeys);
1411     amfree(rundump_cmd);
1412
1413     aclose(pipefd[1]);
1414     if (killctl[0] != -1)
1415         aclose(killctl[0]);
1416     dumpout = fdopen(pipefd[0],"r");
1417     if (!dumpout) {
1418         error(_("Can't fdopen: %s"), strerror(errno));
1419         /*NOTREACHED*/
1420     }
1421
1422     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1423         if (line[0] == '\0')
1424             continue;
1425         dbprintf("%s\n", line);
1426         size = handle_dumpline(line);
1427         if(size > (off_t)-1) {
1428             amfree(line);
1429             while ((line = agets(dumpout)) != NULL) {
1430                 if (line[0] != '\0')
1431                     break;
1432                 amfree(line);
1433             }
1434             if (line != NULL) {
1435                 dbprintf("%s\n", line);
1436             }
1437             break;
1438         }
1439     }
1440     amfree(line);
1441
1442     dbprintf(".....\n");
1443     dbprintf(_("estimate time for %s level %d: %s\n"),
1444               qdisk,
1445               level,
1446               walltime_str(timessub(curclock(), start_time)));
1447     if(size == (off_t)-1) {
1448         *errmsg = vstrallocf(_("no size line match in %s%s output"),
1449                              cmd, name);
1450         dbprintf(_("%s for %s\n"),
1451                   *errmsg, qdisk);
1452
1453         dbprintf(".....\n");
1454         dbprintf(_("Run %s%s manually to check for errors\n"),
1455                     cmd, name);
1456     } else if(size == (off_t)0 && level == 0) {
1457         dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1458                   cmd, name, disk);
1459         dbprintf(".....\n");
1460     } else {
1461             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1462               qdisk,
1463               level,
1464               (long long)size);
1465     }
1466
1467     if (killctl[1] != -1) {
1468         dbprintf(_("asking killpgrp to terminate\n"));
1469         aclose(killctl[1]);
1470         for(s = 5; s > 0; --s) {
1471             sleep(1);
1472             if (waitpid(dumppid, NULL, WNOHANG) != -1)
1473                 goto terminated;
1474         }
1475     }
1476     
1477     /*
1478      * First, try to kill the dump process nicely.  If it ignores us
1479      * for several seconds, hit it harder.
1480      */
1481     dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1482     if (kill(-dumppid, SIGTERM) == -1) {
1483         dbprintf(_("kill failed: %s\n"), strerror(errno));
1484     }
1485     /* Now check whether it dies */
1486     for(s = 5; s > 0; --s) {
1487         sleep(1);
1488         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1489             goto terminated;
1490     }
1491
1492     dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1493     if (kill(-dumppid, SIGKILL) == -1) {
1494         dbprintf(_("kill failed: %s\n"), strerror(errno));
1495     }
1496     for(s = 5; s > 0; --s) {
1497         sleep(1);
1498         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1499             goto terminated;
1500     }
1501
1502     dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1503     waitpid(dumppid, &wait_status, 0);
1504     if (WIFSIGNALED(wait_status)) {
1505         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1506                              cmd, WTERMSIG(wait_status), dbfn());
1507     } else if (WIFEXITED(wait_status)) {
1508         if (WEXITSTATUS(wait_status) != 0) {
1509             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1510                                  cmd, WEXITSTATUS(wait_status), dbfn());
1511         } else {
1512             /* Normal exit */
1513         }
1514     } else {
1515         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1516                              cmd, dbfn());
1517     }
1518     dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1519
1520  terminated:
1521
1522     aclose(nullfd);
1523     afclose(dumpout);
1524
1525     amfree(device);
1526     amfree(qdevice);
1527     amfree(qdisk);
1528     amfree(fstype);
1529
1530     amfree(cmd);
1531     amfree(name);
1532
1533     return size;
1534 }
1535
1536 #ifdef SAMBA_CLIENT
1537 off_t
1538 getsize_smbtar(
1539     char       *disk,
1540     char       *amdevice,
1541     int         level,
1542     option_t   *options,
1543     char      **errmsg)
1544 {
1545     int pipefd = -1, nullfd = -1, passwdfd = -1;
1546     pid_t dumppid;
1547     off_t size;
1548     FILE *dumpout;
1549     char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1550     char *share = NULL, *subdir = NULL;
1551     size_t lpass;
1552     char *pwtext;
1553     size_t pwtext_len;
1554     char *line;
1555     char *pw_fd_env;
1556     times_t start_time;
1557     char *error_pn = NULL;
1558     char *qdisk = quote_string(disk);
1559     amwait_t wait_status;
1560
1561     (void)options;      /* Quiet unused parameter warning */
1562
1563     error_pn = stralloc2(get_pname(), "-smbclient");
1564
1565     parsesharename(amdevice, &share, &subdir);
1566     if (!share) {
1567         amfree(share);
1568         amfree(subdir);
1569         set_pname(error_pn);
1570         amfree(error_pn);
1571         error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1572         /*NOTREACHED*/
1573     }
1574     if ((subdir) && (SAMBA_VERSION < 2)) {
1575         amfree(share);
1576         amfree(subdir);
1577         set_pname(error_pn);
1578         amfree(error_pn);
1579         error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1580         /*NOTREACHED*/
1581     }
1582     if ((user_and_password = findpass(share, &domain)) == NULL) {
1583
1584         if(domain) {
1585             memset(domain, '\0', strlen(domain));
1586             amfree(domain);
1587         }
1588         set_pname(error_pn);
1589         amfree(error_pn);
1590         error(_("cannot find password for %s"), disk);
1591         /*NOTREACHED*/
1592     }
1593     lpass = strlen(user_and_password);
1594     if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1595         memset(user_and_password, '\0', (size_t)lpass);
1596         amfree(user_and_password);
1597         if(domain) {
1598             memset(domain, '\0', strlen(domain));
1599             amfree(domain);
1600         }
1601         set_pname(error_pn);
1602         amfree(error_pn);
1603         error(_("password field not \'user%%pass\' for %s"), disk);
1604         /*NOTREACHED*/
1605     }
1606     *pwtext++ = '\0';
1607     pwtext_len = strlen(pwtext);
1608     if ((sharename = makesharename(share, 0)) == NULL) {
1609         memset(user_and_password, '\0', (size_t)lpass);
1610         amfree(user_and_password);
1611         if(domain) {
1612             memset(domain, '\0', strlen(domain));
1613             amfree(domain);
1614         }
1615         set_pname(error_pn);
1616         amfree(error_pn);
1617         error(_("cannot make share name of %s"), share);
1618         /*NOTREACHED*/
1619     }
1620     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1621         memset(user_and_password, '\0', (size_t)lpass);
1622         amfree(user_and_password);
1623         if(domain) {
1624             memset(domain, '\0', strlen(domain));
1625             amfree(domain);
1626         }
1627         set_pname(error_pn);
1628         amfree(error_pn);
1629         amfree(sharename);
1630         error(_("could not open /dev/null: %s\n"),
1631               strerror(errno));
1632         /*NOTREACHED*/
1633     }
1634
1635 #if SAMBA_VERSION >= 2
1636     if (level == 0)
1637         tarkeys = "archive 0;recurse;du";
1638     else
1639         tarkeys = "archive 1;recurse;du";
1640 #else
1641     if (level == 0)
1642         tarkeys = "archive 0;recurse;dir";
1643     else
1644         tarkeys = "archive 1;recurse;dir";
1645 #endif
1646
1647     start_time = curclock();
1648
1649     if (pwtext_len > 0) {
1650         pw_fd_env = "PASSWD_FD";
1651     } else {
1652         pw_fd_env = "dummy_PASSWD_FD";
1653     }
1654     dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
1655               &nullfd, &nullfd, &pipefd, 
1656               pw_fd_env, &passwdfd,
1657               "smbclient",
1658               sharename,
1659               "-d", SAMBA_DEBUG_LEVEL,
1660               *user_and_password ? "-U" : skip_argument,
1661               *user_and_password ? user_and_password : skip_argument,
1662               "-E",
1663               domain ? "-W" : skip_argument,
1664               domain ? domain : skip_argument,
1665 #if SAMBA_VERSION >= 2
1666               subdir ? "-D" : skip_argument,
1667               subdir ? subdir : skip_argument,
1668 #endif
1669               "-c", tarkeys,
1670               NULL);
1671     if(domain) {
1672         memset(domain, '\0', strlen(domain));
1673         amfree(domain);
1674     }
1675     aclose(nullfd);
1676     if(pwtext_len > 0 && fullwrite(passwdfd, pwtext, (size_t)pwtext_len) < 0) {
1677         int save_errno = errno;
1678
1679         memset(user_and_password, '\0', (size_t)lpass);
1680         amfree(user_and_password);
1681         aclose(passwdfd);
1682         set_pname(error_pn);
1683         amfree(error_pn);
1684         error(_("password write failed: %s"), strerror(save_errno));
1685         /*NOTREACHED*/
1686     }
1687     memset(user_and_password, '\0', (size_t)lpass);
1688     amfree(user_and_password);
1689     aclose(passwdfd);
1690     amfree(sharename);
1691     amfree(share);
1692     amfree(subdir);
1693     amfree(error_pn);
1694     dumpout = fdopen(pipefd,"r");
1695     if (!dumpout) {
1696         error(_("Can't fdopen: %s"), strerror(errno));
1697         /*NOTREACHED*/
1698     }
1699
1700     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1701         if (line[0] == '\0')
1702             continue;
1703         dbprintf("%s\n", line);
1704         size = handle_dumpline(line);
1705         if(size > -1) {
1706             amfree(line);
1707             while ((line = agets(dumpout)) != NULL) {
1708                 if (line[0] != '\0')
1709                     break;
1710                 amfree(line);
1711             }
1712             if(line != NULL) {
1713                 dbprintf("%s\n", line);
1714             }
1715             break;
1716         }
1717     }
1718     amfree(line);
1719
1720     dbprintf(".....\n");
1721     dbprintf(_("estimate time for %s level %d: %s\n"),
1722               qdisk,
1723               level,
1724               walltime_str(timessub(curclock(), start_time)));
1725     if(size == (off_t)-1) {
1726         *errmsg = vstrallocf(_("no size line match in %s output"),
1727                              SAMBA_CLIENT);
1728         dbprintf(_("%s for %s\n"),
1729                   *errmsg, qdisk);
1730         dbprintf(".....\n");
1731     } else if(size == (off_t)0 && level == 0) {
1732         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1733                   SAMBA_CLIENT, disk);
1734         dbprintf(".....\n");
1735     }
1736     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1737               qdisk,
1738               level,
1739               (long long)size);
1740
1741     kill(-dumppid, SIGTERM);
1742
1743     dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1744     waitpid(dumppid, &wait_status, 0);
1745     if (WIFSIGNALED(wait_status)) {
1746         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1747                              SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
1748     } else if (WIFEXITED(wait_status)) {
1749         if (WEXITSTATUS(wait_status) != 0) {
1750             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1751                                  SAMBA_CLIENT, WEXITSTATUS(wait_status),
1752                                  dbfn());
1753         } else {
1754             /* Normal exit */
1755         }
1756     } else {
1757         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1758                              SAMBA_CLIENT, dbfn());
1759     }
1760     dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
1761
1762     afclose(dumpout);
1763     pipefd = -1;
1764
1765     amfree(error_pn);
1766     amfree(qdisk);
1767
1768     return size;
1769 }
1770 #endif
1771
1772 #ifdef GNUTAR
1773 off_t
1774 getsize_gnutar(
1775     char       *disk,
1776     char       *amdevice,
1777     int         level,
1778     option_t   *options,
1779     time_t      dumpsince,
1780     char      **errmsg)
1781 {
1782     int pipefd = -1, nullfd = -1;
1783     pid_t dumppid;
1784     off_t size = (off_t)-1;
1785     FILE *dumpout = NULL;
1786     char *incrname = NULL;
1787     char *basename = NULL;
1788     char *dirname = NULL;
1789     char *inputname = NULL;
1790     FILE *in = NULL;
1791     FILE *out = NULL;
1792     char *line = NULL;
1793     char *cmd = NULL;
1794     char dumptimestr[80];
1795     struct tm *gmtm;
1796     int nb_exclude = 0;
1797     int nb_include = 0;
1798     char **my_argv;
1799     int i;
1800     char *file_exclude = NULL;
1801     char *file_include = NULL;
1802     times_t start_time;
1803     int infd, outfd;
1804     ssize_t nb;
1805     char buf[32768];
1806     char *qdisk = quote_string(disk);
1807     char *gnutar_list_dir;
1808     amwait_t wait_status;
1809     char tmppath[PATH_MAX];
1810
1811     if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
1812     if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
1813     if(options->include_file) nb_include += options->include_file->nb_element;
1814     if(options->include_list) nb_include += options->include_list->nb_element;
1815
1816     if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
1817     if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
1818
1819     my_argv = alloc(SIZEOF(char *) * 22);
1820     i = 0;
1821
1822     gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
1823     if (strlen(gnutar_list_dir) == 0)
1824         gnutar_list_dir = NULL;
1825     if (gnutar_list_dir) {
1826         char number[NUM_STR_SIZE];
1827         int baselevel;
1828         char *sdisk = sanitise_filename(disk);
1829
1830         basename = vstralloc(gnutar_list_dir,
1831                              "/",
1832                              g_options->hostname,
1833                              sdisk,
1834                              NULL);
1835         amfree(sdisk);
1836
1837         g_snprintf(number, SIZEOF(number), "%d", level);
1838         incrname = vstralloc(basename, "_", number, ".new", NULL);
1839         unlink(incrname);
1840
1841         /*
1842          * Open the listed incremental file from the previous level.  Search
1843          * backward until one is found.  If none are found (which will also
1844          * be true for a level 0), arrange to read from /dev/null.
1845          */
1846         baselevel = level;
1847         infd = -1;
1848         while (infd == -1) {
1849             if (--baselevel >= 0) {
1850                 g_snprintf(number, SIZEOF(number), "%d", baselevel);
1851                 inputname = newvstralloc(inputname,
1852                                          basename, "_", number, NULL);
1853             } else {
1854                 inputname = newstralloc(inputname, "/dev/null");
1855             }
1856             if ((infd = open(inputname, O_RDONLY)) == -1) {
1857
1858                 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
1859                                      inputname, strerror(errno));
1860                 dbprintf("%s\n", *errmsg);
1861                 if (baselevel < 0) {
1862                     goto common_exit;
1863                 }
1864                 amfree(*errmsg);
1865             }
1866         }
1867
1868         /*
1869          * Copy the previous listed incremental file to the new one.
1870          */
1871         if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1872             *errmsg = vstrallocf(_("opening %s: %s"),
1873                                  incrname, strerror(errno));
1874             dbprintf("%s\n", *errmsg);
1875             goto common_exit;
1876         }
1877
1878         while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1879             if (fullwrite(outfd, &buf, (size_t)nb) < nb) {
1880                 *errmsg = vstrallocf(_("writing to %s: %s"),
1881                                      incrname, strerror(errno));
1882                 dbprintf("%s\n", *errmsg);
1883                 goto common_exit;
1884             }
1885         }
1886
1887         if (nb < 0) {
1888             *errmsg = vstrallocf(_("reading from %s: %s"),
1889                                  inputname, strerror(errno));
1890             dbprintf("%s\n", *errmsg);
1891             goto common_exit;
1892         }
1893
1894         if (close(infd) != 0) {
1895             *errmsg = vstrallocf(_("closing %s: %s"),
1896                                  inputname, strerror(errno));
1897             dbprintf("%s\n", *errmsg);
1898             goto common_exit;
1899         }
1900         if (close(outfd) != 0) {
1901             *errmsg = vstrallocf(_("closing %s: %s"),
1902                                  incrname, strerror(errno));
1903             dbprintf("%s\n", *errmsg);
1904             goto common_exit;
1905         }
1906
1907         amfree(inputname);
1908         amfree(basename);
1909     }
1910
1911     gmtm = gmtime(&dumpsince);
1912     g_snprintf(dumptimestr, SIZEOF(dumptimestr),
1913                 "%04d-%02d-%02d %2d:%02d:%02d GMT",
1914                 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
1915                 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
1916
1917     dirname = amname_to_dirname(amdevice);
1918
1919     cmd = vstralloc(amlibexecdir, "/", "runtar", versionsuffix(), NULL);
1920     my_argv[i++] = "runtar";
1921     if (g_options->config)
1922         my_argv[i++] = g_options->config;
1923     else
1924         my_argv[i++] = "NOCONFIG";
1925
1926 #ifdef GNUTAR
1927     my_argv[i++] = GNUTAR;
1928 #else
1929     my_argv[i++] = "tar";
1930 #endif
1931     my_argv[i++] = "--create";
1932     my_argv[i++] = "--file";
1933     my_argv[i++] = "/dev/null";
1934     my_argv[i++] = "--directory";
1935     canonicalize_pathname(dirname, tmppath);
1936     my_argv[i++] = tmppath;
1937     my_argv[i++] = "--one-file-system";
1938     if (gnutar_list_dir) {
1939             my_argv[i++] = "--listed-incremental";
1940             my_argv[i++] = incrname;
1941     } else {
1942         my_argv[i++] = "--incremental";
1943         my_argv[i++] = "--newer";
1944         my_argv[i++] = dumptimestr;
1945     }
1946 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
1947     /* --atime-preserve causes gnutar to call
1948      * utime() after reading files in order to
1949      * adjust their atime.  However, utime()
1950      * updates the file's ctime, so incremental
1951      * dumps will think the file has changed. */
1952     my_argv[i++] = "--atime-preserve";
1953 #endif
1954     my_argv[i++] = "--sparse";
1955     my_argv[i++] = "--ignore-failed-read";
1956     my_argv[i++] = "--totals";
1957
1958     if(file_exclude) {
1959         my_argv[i++] = "--exclude-from";
1960         my_argv[i++] = file_exclude;
1961     }
1962
1963     if(file_include) {
1964         my_argv[i++] = "--files-from";
1965         my_argv[i++] = file_include;
1966     }
1967     else {
1968         my_argv[i++] = ".";
1969     }
1970     my_argv[i++] = NULL;
1971
1972     start_time = curclock();
1973
1974     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1975         *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1976                              strerror(errno));
1977         dbprintf("%s\n", *errmsg);
1978         goto common_exit;
1979     }
1980
1981     dumppid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
1982
1983     dumpout = fdopen(pipefd,"r");
1984     if (!dumpout) {
1985         error(_("Can't fdopen: %s"), strerror(errno));
1986         /*NOTREACHED*/
1987     }
1988
1989     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1990         if (line[0] == '\0')
1991             continue;
1992         dbprintf("%s\n", line);
1993         size = handle_dumpline(line);
1994         if(size > (off_t)-1) {
1995             amfree(line);
1996             while ((line = agets(dumpout)) != NULL) {
1997                 if (line[0] != '\0') {
1998                     break;
1999                 }
2000                 amfree(line);
2001             }
2002             if (line != NULL) {
2003                 dbprintf("%s\n", line);
2004                 break;
2005             }
2006             break;
2007         }
2008     }
2009     amfree(line);
2010
2011     dbprintf(".....\n");
2012     dbprintf(_("estimate time for %s level %d: %s\n"),
2013               qdisk,
2014               level,
2015               walltime_str(timessub(curclock(), start_time)));
2016     if(size == (off_t)-1) {
2017         *errmsg = vstrallocf(_("no size line match in %s output"), my_argv[0]);
2018         dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2019         dbprintf(".....\n");
2020     } else if(size == (off_t)0 && level == 0) {
2021         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2022                   my_argv[0], disk);
2023         dbprintf(".....\n");
2024     }
2025     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2026               qdisk,
2027               level,
2028               (long long)size);
2029
2030     kill(-dumppid, SIGTERM);
2031
2032     dbprintf(_("waiting for %s \"%s\" child\n"), my_argv[0], qdisk);
2033     waitpid(dumppid, &wait_status, 0);
2034     if (WIFSIGNALED(wait_status)) {
2035         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2036                              cmd, WTERMSIG(wait_status), dbfn());
2037     } else if (WIFEXITED(wait_status)) {
2038         if (WEXITSTATUS(wait_status) != 0) {
2039             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2040                                  cmd, WEXITSTATUS(wait_status), dbfn());
2041         } else {
2042             /* Normal exit */
2043         }
2044     } else {
2045         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2046                              cmd, dbfn());
2047     }
2048     dbprintf(_("after %s %s wait\n"), my_argv[0], qdisk);
2049
2050 common_exit:
2051
2052     if (incrname) {
2053         unlink(incrname);
2054     }
2055     amfree(incrname);
2056     amfree(basename);
2057     amfree(dirname);
2058     amfree(inputname);
2059     amfree(my_argv);
2060     amfree(qdisk);
2061     amfree(cmd);
2062     amfree(file_exclude);
2063     amfree(file_include);
2064
2065     aclose(nullfd);
2066     afclose(dumpout);
2067     afclose(in);
2068     afclose(out);
2069
2070     return size;
2071 }
2072 #endif
2073
2074 off_t
2075 getsize_backup_api(
2076     char        *program,
2077     char        *disk,
2078     char        *amdevice,
2079     int          level,
2080     option_t    *options,
2081     time_t       dumpsince,
2082     char        **errmsg)
2083 {
2084     int pipeinfd[2], pipeoutfd[2], nullfd;
2085     pid_t dumppid;
2086     off_t size = (off_t)-1;
2087     FILE *dumpout, *toolin;
2088     char *line = NULL;
2089     char *cmd = NULL;
2090     char *cmdline;
2091     char dumptimestr[80];
2092     struct tm *gmtm;
2093     int  i, j;
2094     char *argvchild[10];
2095     char *newoptstr = NULL;
2096     off_t size1, size2;
2097     times_t start_time;
2098     char *qdisk = quote_string(disk);
2099     char *qamdevice = quote_string(amdevice);
2100     amwait_t wait_status;
2101     char levelstr[NUM_STR_SIZE];
2102     backup_support_option_t *bsu;
2103
2104     (void)options;
2105     gmtm = gmtime(&dumpsince);
2106     g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2107                 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2108                 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2109                 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2110
2111     cmd = vstralloc(DUMPER_DIR, "/", program, NULL);
2112
2113     bsu = backup_support_option(program, g_options, disk, amdevice);
2114
2115     i=0;
2116     argvchild[i++] = program;
2117     argvchild[i++] = "estimate";
2118     if (bsu->message_line == 1) {
2119         argvchild[i++] = "--message";
2120         argvchild[i++] = "line";
2121     }
2122     if (g_options->config && bsu->config == 1) {
2123         argvchild[i++] = "--config";
2124         argvchild[i++] = g_options->config;
2125     }
2126     if (g_options->hostname && bsu->host == 1) {
2127         argvchild[i++] = "--host";
2128         argvchild[i++] = g_options->hostname;
2129     }
2130     argvchild[i++] = "--device";
2131     argvchild[i++] = amdevice;
2132     if (disk && bsu->disk == 1) {
2133         argvchild[i++] = "--disk";
2134         argvchild[i++] = disk;
2135     }
2136     if (level <= bsu->max_level) {
2137         argvchild[i++] = "--level";
2138         g_snprintf(levelstr,SIZEOF(levelstr),"%d",level);
2139         argvchild[i++] = levelstr;
2140     }
2141
2142     argvchild[i] = NULL;
2143
2144     cmdline = stralloc(cmd);
2145     for(j = 1; j < i; j++)
2146         cmdline = vstrextend(&cmdline, " ", argvchild[i], NULL);
2147     dbprintf("running: \"%s\"\n", cmdline);
2148     amfree(cmdline);
2149
2150     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2151         *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2152                              strerror(errno));
2153         dbprintf("%s\n", *errmsg);
2154         goto common_exit;
2155     }
2156
2157     if (pipe(pipeinfd) < 0) {
2158         *errmsg = vstrallocf(_("getsize_backup_api could create data pipes: %s"),
2159                              strerror(errno));
2160         dbprintf("%s\n", *errmsg);
2161         goto common_exit;
2162     }
2163
2164     if (pipe(pipeoutfd) < 0) {
2165         *errmsg = vstrallocf(_("getsize_backup_api could create data pipes: %s"),
2166                              strerror(errno));
2167         dbprintf("%s\n", *errmsg);
2168         goto common_exit;
2169     }
2170
2171     start_time = curclock();
2172
2173     switch(dumppid = fork()) {
2174     case -1:
2175       size = (off_t)-1;
2176       goto common_exit;
2177     default:
2178       break; /* parent */
2179     case 0:
2180       dup2(pipeinfd[0], 0);
2181       dup2(pipeoutfd[1], 1);
2182       dup2(nullfd, 2);
2183       aclose(pipeinfd[1]);
2184       aclose(pipeoutfd[0]);
2185       safe_fd(-1, 0);
2186
2187       execve(cmd, argvchild, safe_env());
2188       error(_("exec %s failed: %s"), cmd, strerror(errno));
2189       /*NOTREACHED*/
2190     }
2191     amfree(newoptstr);
2192
2193     aclose(pipeinfd[0]);
2194     aclose(pipeoutfd[1]);
2195
2196     toolin = fdopen(pipeinfd[1],"w");
2197     if (!toolin) {
2198         error("Can't fdopen: %s", strerror(errno));
2199         /*NOTREACHED*/
2200     }
2201
2202     output_tool_property(toolin, options);
2203     fflush(toolin);
2204     fclose(toolin);
2205
2206     dumpout = fdopen(pipeoutfd[0],"r");
2207     if (!dumpout) {
2208         error(_("Can't fdopen: %s"), strerror(errno));
2209         /*NOTREACHED*/
2210     }
2211
2212     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2213         long long size1_ = (long long)0;
2214         long long size2_ = (long long)0;
2215         if (line[0] == '\0')
2216             continue;
2217         dbprintf("%s\n", line);
2218         i = sscanf(line, "%lld %lld", &size1_, &size2_);
2219         size1 = (off_t)size1_;
2220         size2 = (off_t)size2_;
2221         if(i == 2) {
2222             size = size1 * size2;
2223         }
2224         if(size > -1) {
2225             amfree(line);
2226             while ((line = agets(dumpout)) != NULL) {
2227                 if (line[0] != '\0')
2228                     break;
2229                 amfree(line);
2230             }
2231             if(line != NULL) {
2232                 dbprintf(_("%s\n"), line);
2233             }
2234             break;
2235         }
2236     }
2237     amfree(line);
2238
2239     dbprintf(".....\n");
2240     dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice, level,
2241               walltime_str(timessub(curclock(), start_time)));
2242     if(size == (off_t)-1) {
2243         *errmsg = vstrallocf(_("no size line match in %s output"), cmd);
2244         dbprintf(_("%s for %s\n"), cmd, qdisk);
2245         dbprintf(".....\n");
2246     } else if(size == (off_t)0 && level == 0) {
2247         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2248                   cmd, qdisk);
2249         dbprintf(".....\n");
2250     }
2251     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2252               qamdevice,
2253               level,
2254               (long long)size);
2255
2256     kill(-dumppid, SIGTERM);
2257
2258     dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2259     waitpid(dumppid, &wait_status, 0);
2260     if (WIFSIGNALED(wait_status)) {
2261         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2262                              cmd, WTERMSIG(wait_status), dbfn());
2263     } else if (WIFEXITED(wait_status)) {
2264         if (WEXITSTATUS(wait_status) != 0) {
2265             *errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2266                                  WEXITSTATUS(wait_status), dbfn());
2267         } else {
2268             /* Normal exit */
2269         }
2270     } else {
2271         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2272                              cmd, dbfn());
2273     }
2274     dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2275
2276     aclose(nullfd);
2277     afclose(dumpout);
2278
2279 common_exit:
2280
2281     amfree(cmd);
2282     amfree(newoptstr);
2283     amfree(qdisk);
2284     amfree(qamdevice);
2285     return size;
2286 }
2287
2288
2289 /*
2290  * Returns the value of the first integer in a string.
2291  */
2292
2293 double
2294 first_num(
2295     char *      str)
2296 {
2297     char *start;
2298     int ch;
2299     double d;
2300
2301     ch = *str++;
2302     while(ch && !isdigit(ch)) ch = *str++;
2303     start = str-1;
2304     while(isdigit(ch) || (ch == '.')) ch = *str++;
2305     str[-1] = '\0';
2306     d = atof(start);
2307     str[-1] = (char)ch;
2308     return d;
2309 }
2310
2311
2312 /*
2313  * Checks the dump output line against the error and size regex tables.
2314  */
2315
2316 off_t
2317 handle_dumpline(
2318     char *      str)
2319 {
2320     regex_scale_t *rp;
2321     double size;
2322
2323     /* check for size match */
2324     /*@ignore@*/
2325     for(rp = re_size; rp->regex != NULL; rp++) {
2326         if(match(rp->regex, str)) {
2327             size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2328             if(size < 0.0)
2329                 size = 1.0;             /* found on NeXT -- sigh */
2330             return (off_t)size;
2331         }
2332     }
2333     /*@end@*/
2334     return (off_t)-1;
2335 }