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