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