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