2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: sendsize.c,v 1.97.2.13.4.6.2.23.2.5 2005/09/20 21:31:52 jrjackson Exp $
29 * send estimated backup sizes using dump
33 #include "pipespawn.h"
34 #include "amfeatures.h"
35 #include "amandates.h"
40 #include "client_util.h"
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))); \
54 #if defined(SETPGRP_VOID)
55 # define SETPGRP setpgrp()
56 # define SETPGRP_FAILED() do { \
57 dbprintf(("setpgrp() failed: %s\n", strerror(errno))); \
61 # define SETPGRP setpgrp(0, getpid())
62 # define SETPGRP_FAILED() do { \
63 dbprintf(("setpgrp(0,%ld) failed: %s\n", \
64 (long)getpid(), strerror(errno))); \
70 typedef struct level_estimates_s {
76 typedef struct disk_estimates_s {
77 struct disk_estimates_s *next;
87 level_estimate_t est[DUMP_LEVELS];
90 disk_estimates_t *est_list;
92 static am_feature_t *our_features = NULL;
93 static char *our_feature_string = NULL;
94 static g_option_t *g_options = NULL;
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 *));
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;
121 char *err_extra = NULL;
122 unsigned long malloc_hist_1, malloc_size_1;
123 unsigned long malloc_hist_2, malloc_size_2;
134 set_pname("sendsize");
136 malloc_size_1 = malloc_inuse(&malloc_hist_1);
138 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
141 dbprintf(("%s: version %s\n", get_pname(), version()));
143 our_features = am_init_feature_set();
144 our_feature_string = am_feature_to_string(our_features);
146 set_debug_prefix_pid(getpid());
148 /* handle all service requests */
152 for(; (line = agets(stdin)) != NULL; free(line)) {
153 #define sc "OPTIONS "
154 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
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';
164 if(am_has_feature(g_options->features, fe_rep_options_features)) {
165 printf("features=%s;", our_feature_string);
167 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
168 printf("maxdumps=%d;", g_options->maxdumps);
170 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
171 printf("hostname=%s;", g_options->hostname);
181 skip_whitespace(s, ch); /* find the program name */
183 err_extra = "no program name";
184 goto err; /* no program name */
187 skip_non_whitespace(s, ch);
190 if(strncmp(prog, "CALCSIZE", 8) == 0) {
191 skip_whitespace(s, ch); /* find the program name */
193 err_extra = "no program name";
197 skip_non_whitespace(s, ch);
204 skip_whitespace(s, ch); /* find the disk name */
206 err_extra = "no disk name";
207 goto err; /* no disk name */
210 skip_non_whitespace(s, ch);
213 skip_whitespace(s, ch); /* find the device or level */
215 err_extra = "bad level";
218 if(!isdigit((int)s[-1])) {
220 skip_non_whitespace(s, ch);
222 amdevice = stralloc(fp);
223 skip_whitespace(s, ch); /* find level number */
226 amdevice = stralloc(disk);
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 */
236 skip_whitespace(s, ch); /* find the dump date */
238 err_extra = "no dumpdate";
239 goto err; /* no dumpdate */
242 skip_non_whitespace(s, ch);
245 spindle = 0; /* default spindle */
247 skip_whitespace(s, ch); /* find the spindle */
249 if(sscanf(s - 1, "%d", &spindle) != 1) {
250 err_extra = "bad spindle";
251 goto err; /* bad spindle */
255 skip_whitespace(s, ch); /* find the exclusion list */
257 if(strncmp(s-1, "OPTIONS |;",10) == 0) {
258 options = parse_options(s + 8,
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);
271 if(strncmp(s-1, "exclude-list=", 13) == 0) {
272 options->exclude_list =
273 append_sl(options->exclude_list, s+12);
276 skip_non_whitespace(s, ch);
278 err_extra = "extra text at end";
279 goto err; /* should have gotten to end */
284 options = alloc(sizeof(option_t));
285 init_options(options);
289 add_diskest(disk, amdevice, level, spindle, prog, calcprog, options);
303 * See if we need to wait for a child before we can do anything
308 amwait_t child_status;
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));
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)));
323 exit_code = WEXITSTATUS(child_status);
325 dbprintf(("%s: child %ld terminated normally\n",
326 debug_prefix_time(NULL), (long) child_pid));
328 dbprintf(("%s: child %ld terminated with code %d\n",
329 debug_prefix_time(NULL),
330 (long) child_pid, exit_code));
334 * Find the child and mark it done.
336 for(est = est_list; est != NULL; est = est->next) {
337 if(est->child == child_pid) {
342 dbprintf(("%s: unexpected child %ld\n",
343 debug_prefix_time(NULL), (long)child_pid));
351 * If we are already running the maximum number of children
352 * go back and wait until one of them finishes.
354 if(dumpsrunning >= g_options->maxdumps) {
357 continue; /* have to wait first */
360 * Find a new child to start.
362 for(est = est_list; est != NULL; est = est->next) {
364 done = 0; /* more to do */
366 if(est->child != 0 || est->done) {
367 continue; /* child is running or done */
370 * Make sure there is no spindle conflict.
372 if(est->spindle != -1) {
373 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
374 if(est1->child == 0 || est == est1 || est1->done) {
376 * Ignore anything not yet started, ourself,
377 * and anything completed.
381 if(est1->spindle == est->spindle) {
382 break; /* oops -- they match */
386 continue; /* spindle conflict */
389 break; /* start this estimate */
392 if(dumpsrunning > 0) {
393 need_wait = 1; /* nothing to do but wait */
397 if((est->child = fork()) == 0) {
398 set_debug_prefix_pid(getpid());
399 calc_estimates(est); /* child does the estimate */
401 } else if(est->child == -1) {
402 error("calc_estimates fork failed: %s", strerror(errno));
404 dumpsrunning++; /* parent */
409 for(est = est_list; est != NULL; est = est->next) {
415 amfree(our_feature_string);
416 am_release_feature_set(our_features);
418 am_release_feature_set(g_options->features);
419 g_options->features = NULL;
420 amfree(g_options->str);
421 amfree(g_options->hostname);
424 malloc_size_2 = malloc_inuse(&malloc_hist_2);
426 if(malloc_size_1 != malloc_size_2) {
427 #if defined(USE_DBMALLOC)
428 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
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 : ""));
445 void add_diskest(disk, amdevice, level, spindle, prog, calcprog, options)
446 char *disk, *amdevice, *prog, *calcprog;
450 disk_estimates_t *newp, *curp;
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;
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);
471 newp = (disk_estimates_t *) alloc(sizeof(disk_estimates_t));
472 memset(newp, 0, sizeof(*newp));
473 newp->next = est_list;
475 newp->amname = stralloc(disk);
476 newp->amdevice = stralloc(amdevice);
477 newp->dirname = amname_to_dirname(newp->amdevice);
478 newp->program = stralloc(prog);
480 newp->calcprog = stralloc(calcprog);
482 newp->calcprog = NULL;
483 newp->spindle = spindle;
484 newp->est[level].needestimate = 1;
485 newp->options = options;
487 /* fill in dump-since dates */
489 amdp = amandates_lookup(newp->amname);
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;
502 void free_estimates(est)
503 disk_estimates_t *est;
506 amfree(est->amdevice);
507 amfree(est->dirname);
508 amfree(est->program);
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);
520 * ------------------------------------------------------------------------
524 void calc_estimates(est)
525 disk_estimates_t *est;
527 dbprintf(("%s: calculating for amname '%s', dirname '%s', spindle %d\n",
528 debug_prefix_time(NULL),
529 est->amname, est->dirname, est->spindle));
531 #ifndef USE_GENERIC_CALCSIZE
532 if(strcmp(est->program, "DUMP") == 0)
533 dump_calc_estimates(est);
537 if (strcmp(est->program, "GNUTAR") == 0 &&
538 est->amdevice[0] == '/' && est->amdevice[1] == '/')
539 smbtar_calc_estimates(est);
543 if (strcmp(est->program, "GNUTAR") == 0)
544 gnutar_calc_estimates(est);
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));
554 generic_calc_estimates(est);
556 dbprintf(("%s: done with amname '%s', dirname '%s', spindle %d\n",
557 debug_prefix_time(NULL),
558 est->amname, est->dirname, est->spindle));
561 void generic_calc_estimates(est)
562 disk_estimates_t *est;
564 int pipefd = -1, nullfd = -1;
566 char *my_argv[DUMP_LEVELS*2+20];
567 char number[NUM_STR_SIZE];
568 int i, level, my_argc, calcpid;
571 char *file_exclude = NULL;
572 char *file_include = NULL;
574 FILE *dumpout = NULL;
579 cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
582 my_argv[my_argc++] = stralloc("calcsize");
583 my_argv[my_argc++] = stralloc(est->calcprog);
585 my_argv[my_argc++] = stralloc(est->amname);
586 my_argv[my_argc++] = stralloc(est->dirname);
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;
599 file_exclude = build_exclude(est->amname, est->amdevice,est->options,0);
601 file_include = build_include(est->amname, est->amdevice,est->options,0);
604 my_argv[my_argc++] = stralloc("-X");
605 my_argv[my_argc++] = file_exclude;
609 my_argv[my_argc++] = stralloc("-I");
610 my_argv[my_argc++] = file_include;
613 start_time = curclock();
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]));
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));
630 my_argv[my_argc] = NULL;
633 fflush(stderr); fflush(stdout);
635 nullfd = open("/dev/null", O_RDWR);
636 calcpid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
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",
653 dbprintf(("%s: waiting for %s \"%s\" child\n",
654 debug_prefix_time(NULL), my_argv[0], est->amdevice));
656 dbprintf(("%s: after %s \"%s\" wait\n",
657 debug_prefix_time(NULL), my_argv[0], est->amdevice));
659 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
660 dbprintf(("%s: estimate time for %s: %s\n",
663 walltime_str(timessub(curclock(), start_time))));
665 for(i = 0; i < my_argc; i++) {
673 * ------------------------------------------------------------------------
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));
686 void dump_calc_estimates(est)
687 disk_estimates_t *est;
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);
700 fseek(stdout, (off_t)0, SEEK_SET);
702 printf("%s %d SIZE %ld\n", est->amname, level, size);
705 amfunlock(1, "size");
711 void smbtar_calc_estimates(est)
712 disk_estimates_t *est;
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);
725 fseek(stdout, (off_t)0, SEEK_SET);
727 printf("%s %d SIZE %ld\n", est->amname, level, size);
730 amfunlock(1, "size");
737 void gnutar_calc_estimates(est)
738 disk_estimates_t *est;
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);
752 fseek(stdout, (off_t)0, SEEK_SET);
754 printf("%s %d SIZE %ld\n", est->amname, level, size);
757 amfunlock(1, "size");
763 typedef struct regex_s {
768 regex_t re_size[] = {
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},
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 */
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 */
796 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
800 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
801 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
805 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
808 #ifdef USE_QUICK_AND_DIRTY_ESTIMATES
809 {"amqde estimate: [0-9][0-9]* kb", 1024}, /* amqde */
813 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
817 #if SAMBA_VERSION >= 2
818 #define SAMBA_DEBUG_LEVEL "0"
819 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
821 #define SAMBA_DEBUG_LEVEL "3"
822 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
830 long getsize_dump(disk, amdevice, level, options)
831 char *disk, *amdevice;
835 int pipefd[2], nullfd, stdoutfd, killctl[2];
839 char *dumpkeys = NULL;
845 char *rundump_cmd = NULL;
846 char level_str[NUM_STR_SIZE];
850 ap_snprintf(level_str, sizeof(level_str), "%d", level);
852 device = amname_to_devname(amdevice);
853 fstype = amname_to_fstype(amdevice);
855 dbprintf(("%s: calculating for device '%s' with '%s'\n",
856 debug_prefix_time(NULL), device, fstype));
858 cmd = vstralloc(libexecdir, "/rundump", versionsuffix(), NULL);
859 rundump_cmd = stralloc(cmd);
861 stdoutfd = nullfd = open("/dev/null", O_RDWR);
862 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
865 #ifdef XFSDUMP /* { */
867 if (strcmp(fstype, "xfs") == 0)
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));
878 #ifdef VXDUMP /* { */
880 if (strcmp(fstype, "vxfs") == 0)
886 name = stralloc(" (vxdump)");
889 cmd = newstralloc(cmd, VXDUMP);
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));
899 if (strcmp(fstype, "advfs") == 0)
904 name = stralloc(" (vdump)");
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));
915 # ifdef USE_RUNDUMP /* { */
916 # ifdef AIX_BACKUP /* { */
917 name = stralloc(" (backup)");
919 name = vstralloc(" (", DUMP, ")", NULL);
923 cmd = newstralloc(cmd, DUMP);
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));
931 dumpkeys = vstralloc(level_str,
932 # ifdef HAVE_DUMP_ESTIMATE /* { */
935 # ifdef HAVE_HONOR_NODUMP /* { */
940 # ifdef HAVE_DUMP_ESTIMATE
941 stdoutfd = pipefd[1];
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));
948 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
949 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
956 error("no dump program available");
961 start_time = curclock();
962 switch(dumppid = fork()) {
964 dbprintf(("%s: cannot fork for killpgrp: %s\n",
965 debug_prefix(NULL), strerror(errno)));
974 case 0: /* child process */
977 else if (killctl[0] == -1 || killctl[1] == -1)
978 dbprintf(("%s: pipe for killpgrp failed, trying without killpgrp\n",
979 debug_prefix(NULL)));
983 dbprintf(("%s: fork failed, trying without killpgrp\n",
984 debug_prefix(NULL)));
989 char *killpgrp_cmd = vstralloc(libexecdir, "/killpgrp",
990 versionsuffix(), NULL);
991 dbprintf(("%s: running %s\n",
992 debug_prefix_time(NULL), killpgrp_cmd));
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)));
1006 case 0: /* child process */
1015 if (killctl[0] != -1)
1017 if (killctl[1] != -1)
1022 if (strcmp(fstype, "xfs") == 0)
1026 execle(cmd, "xfsdump", "-F", "-J", "-l", level_str, "-", device,
1027 (char *)0, safe_env());
1032 if (strcmp(fstype, "vxfs") == 0)
1036 execle(cmd, "vxdump", dumpkeys, "1048576", "-", device, (char *)0,
1042 if (strcmp(fstype, "advfs") == 0)
1046 execle(cmd, "vdump", dumpkeys, "60", "-", device, (char *)0,
1052 execle(cmd, "backup", dumpkeys, "-", device, (char *)0, safe_env());
1054 execle(cmd, "dump", dumpkeys,
1055 #ifdef HAVE_HONOR_NODUMP
1058 "1048576", "-", device, (char *)0, safe_env());
1062 error("exec %s failed or no dump program available: %s",
1063 cmd, strerror(errno));
1068 amfree(rundump_cmd);
1071 if (killctl[0] != -1)
1073 dumpout = fdopen(pipefd[0],"r");
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);
1080 if((line = agets(dumpout)) != NULL) {
1081 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1088 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1089 dbprintf(("%s: estimate time for %s level %d: %s\n",
1093 walltime_str(timessub(curclock(), start_time))));
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)));
1103 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1109 if (killctl[1] != -1) {
1110 dbprintf(("%s: asking killpgrp to terminate\n",
1111 debug_prefix_time(NULL)));
1113 for(s = 5; s > 0; --s) {
1115 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1121 * First, try to kill the dump process nicely. If it ignores us
1122 * for several seconds, hit it harder.
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)));
1130 /* Now check whether it dies */
1131 for(s = 5; s > 0; --s) {
1133 if (waitpid(dumppid, NULL, WNOHANG) != -1)
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)));
1143 for(s = 5; s > 0; --s) {
1145 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1149 dbprintf(("%s: waiting for %s%s \"%s\" child\n",
1150 debug_prefix_time(NULL), cmd, name, disk));
1152 dbprintf(("%s: after %s%s \"%s\" wait\n",
1153 debug_prefix_time(NULL), cmd, name, disk));
1170 long getsize_smbtar(disk, amdevice, level, optionns)
1171 char *disk, *amdevice;
1175 int pipefd = -1, nullfd = -1, passwdfd = -1;
1179 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1180 char *share = NULL, *subdir = NULL;
1187 char *error_pn = NULL;
1189 error_pn = stralloc2(get_pname(), "-smbclient");
1191 parsesharename(amdevice, &share, &subdir);
1195 set_pname(error_pn);
1197 error("cannot parse disk entry '%s' for share/subdir", disk);
1199 if ((subdir) && (SAMBA_VERSION < 2)) {
1202 set_pname(error_pn);
1204 error("subdirectory specified for share '%s' but samba not v2 or better", disk);
1206 if ((user_and_password = findpass(share, &domain)) == NULL) {
1209 memset(domain, '\0', strlen(domain));
1212 set_pname(error_pn);
1214 error("cannot find password for %s", disk);
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);
1221 memset(domain, '\0', strlen(domain));
1224 set_pname(error_pn);
1226 error("password field not \'user%%pass\' for %s", disk);
1229 pwtext_len = strlen(pwtext);
1230 if ((sharename = makesharename(share, 0)) == NULL) {
1231 memset(user_and_password, '\0', lpass);
1232 amfree(user_and_password);
1234 memset(domain, '\0', strlen(domain));
1237 set_pname(error_pn);
1239 error("cannot make share name of %s", share);
1241 nullfd = open("/dev/null", O_RDWR);
1243 #if SAMBA_VERSION >= 2
1245 tarkeys = "archive 0;recurse;du";
1247 tarkeys = "archive 1;recurse;du";
1250 tarkeys = "archive 0;recurse;dir";
1252 tarkeys = "archive 1;recurse;dir";
1255 start_time = curclock();
1257 if (pwtext_len > 0) {
1258 pw_fd_env = "PASSWD_FD";
1260 pw_fd_env = "dummy_PASSWD_FD";
1262 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
1263 &nullfd, &nullfd, &pipefd,
1264 pw_fd_env, &passwdfd,
1267 "-d", SAMBA_DEBUG_LEVEL,
1268 *user_and_password ? "-U" : skip_argument,
1269 *user_and_password ? user_and_password : skip_argument,
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,
1280 memset(domain, '\0', strlen(domain));
1284 if(pwtext_len > 0 && fullwrite(passwdfd, pwtext, pwtext_len) < 0) {
1285 int save_errno = errno;
1287 memset(user_and_password, '\0', lpass);
1288 amfree(user_and_password);
1290 set_pname(error_pn);
1292 error("password write failed: %s", strerror(save_errno));
1294 memset(user_and_password, '\0', lpass);
1295 amfree(user_and_password);
1301 dumpout = fdopen(pipefd,"r");
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);
1308 if((line = agets(dumpout)) != NULL) {
1309 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1316 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1317 dbprintf(("%s: estimate time for %s level %d: %s\n",
1321 walltime_str(timessub(curclock(), start_time))));
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)));
1331 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1337 kill(-dumppid, SIGTERM);
1339 dbprintf(("%s: waiting for %s \"%s\" child\n",
1340 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1342 dbprintf(("%s: after %s \"%s\" wait\n",
1343 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1355 long getsize_gnutar(disk, amdevice, level, options, dumpsince)
1356 char *disk, *amdevice;
1361 int pipefd = -1, nullfd = -1, dumppid;
1363 FILE *dumpout = NULL;
1364 char *incrname = NULL;
1365 char *basename = NULL;
1366 char *dirname = NULL;
1367 char *inputname = NULL;
1372 char dumptimestr[80];
1378 char *file_exclude = NULL;
1379 char *file_include = NULL;
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;
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);
1390 my_argv = alloc(sizeof(char *) * 21);
1393 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
1395 char number[NUM_STR_SIZE];
1400 basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
1402 g_options->hostname,
1406 * The loop starts at the first character of the host name,
1409 s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
1410 while((ch = *s++) != '\0') {
1411 if(ch == '/' || isspace(ch)) s[-1] = '_';
1414 ap_snprintf(number, sizeof(number), "%d", level);
1415 incrname = vstralloc(basename, "_", number, ".new", NULL);
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.
1424 while (in == NULL) {
1425 if (--baselevel >= 0) {
1426 ap_snprintf(number, sizeof(number), "%d", baselevel);
1427 inputname = newvstralloc(inputname,
1428 basename, "_", number, NULL);
1430 inputname = newstralloc(inputname, "/dev/null");
1432 if ((in = fopen(inputname, "r")) == NULL) {
1433 int save_errno = errno;
1435 dbprintf(("%s: gnutar: error opening %s: %s\n",
1436 debug_prefix(NULL), inputname, strerror(save_errno)));
1437 if (baselevel < 0) {
1444 * Copy the previous listed incremental file to the new one.
1446 if ((out = fopen(incrname, "w")) == NULL) {
1447 dbprintf(("%s: opening %s: %s\n",
1448 debug_prefix(NULL), incrname, strerror(errno)));
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)));
1462 dbprintf(("%s: reading from %s: %s\n",
1463 debug_prefix(NULL), inputname, strerror(errno)));
1466 if (fclose(in) == EOF) {
1467 dbprintf(("%s: closing %s: %s\n",
1468 debug_prefix(NULL), inputname, strerror(errno)));
1473 if (fclose(out) == EOF) {
1474 dbprintf(("%s: closing %s: %s\n",
1475 debug_prefix(NULL), incrname, strerror(errno)));
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);
1492 dirname = amname_to_dirname(amdevice);
1496 #ifdef USE_QUICK_AND_DIRTY_ESTIMATES
1497 ap_snprintf(dumptimestr, sizeof(dumptimestr), "%ld", dumpsince);
1499 cmd = vstralloc(libexecdir, "/", "amqde", versionsuffix(), NULL);
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;
1508 /* [XXX] need to also consider implementation of --files-from */
1509 my_argv[i++] = dirname;
1510 my_argv[i++] = NULL;
1513 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
1515 my_argv[i++] = GNUTAR;
1517 my_argv[i++] = "tar";
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;
1529 my_argv[i++] = "--incremental";
1530 my_argv[i++] = "--newer";
1531 my_argv[i++] = dumptimestr;
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";
1541 my_argv[i++] = "--sparse";
1542 my_argv[i++] = "--ignore-failed-read";
1543 my_argv[i++] = "--totals";
1546 my_argv[i++] = "--exclude-from";
1547 my_argv[i++] = file_exclude;
1551 my_argv[i++] = "--files-from";
1552 my_argv[i++] = file_include;
1557 #endif /* USE_QUICK_AND_DIRTY_ESTIMATES */
1558 my_argv[i++] = NULL;
1560 start_time = curclock();
1562 nullfd = open("/dev/null", O_RDWR);
1563 dumppid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
1565 amfree(file_exclude);
1566 amfree(file_include);
1568 dumpout = fdopen(pipefd,"r");
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);
1575 if((line = agets(dumpout)) != NULL) {
1576 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1583 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1584 dbprintf(("%s: estimate time for %s level %d: %s\n",
1588 walltime_str(timessub(curclock(), start_time))));
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)));
1598 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1604 kill(-dumppid, SIGTERM);
1606 dbprintf(("%s: waiting for %s \"%s\" child\n",
1607 debug_prefix_time(NULL), my_argv[0], disk));
1609 dbprintf(("%s: after %s \"%s\" wait\n",
1610 debug_prefix_time(NULL), my_argv[0], disk));
1633 double first_num(str)
1636 * Returns the value of the first integer in a string.
1644 while(ch && !isdigit(ch)) ch = *str++;
1646 while(isdigit(ch) || (ch == '.')) ch = *str++;
1654 long handle_dumpline(str)
1657 * Checks the dump output line against the error and size regex tables.
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 */