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