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