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