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