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.152 2006/03/09 16:51:41 martinea 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;
83 int program_is_wrapper;
88 level_estimate_t est[DUMP_LEVELS];
91 disk_estimates_t *est_list;
93 static am_feature_t *our_features = NULL;
94 static char *our_feature_string = NULL;
95 static g_option_t *g_options = NULL;
98 int main P((int argc, char **argv));
99 void add_diskest P((char *disk, char *amdevice, int level, int spindle,
100 int program_is_wrapper, char *prog, char *calcprog,
102 void calc_estimates P((disk_estimates_t *est));
103 void free_estimates P((disk_estimates_t *est));
104 void dump_calc_estimates P((disk_estimates_t *));
105 void smbtar_calc_estimates P((disk_estimates_t *));
106 void gnutar_calc_estimates P((disk_estimates_t *));
107 void wrapper_calc_estimates P((disk_estimates_t *));
108 void generic_calc_estimates P((disk_estimates_t *));
116 char *prog, *calcprog, *disk, *amdevice, *dumpdate;
117 option_t *options = NULL;
118 int program_is_wrapper;
119 disk_estimates_t *est;
120 disk_estimates_t *est1;
121 disk_estimates_t *est_prev;
125 char *err_extra = NULL;
126 unsigned long malloc_hist_1, malloc_size_1;
127 unsigned long malloc_hist_2, malloc_size_2;
138 set_pname("sendsize");
140 /* Don't die when child closes pipe */
141 signal(SIGPIPE, SIG_IGN);
143 malloc_size_1 = malloc_inuse(&malloc_hist_1);
145 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
148 dbprintf(("%s: version %s\n", get_pname(), version()));
150 our_features = am_init_feature_set();
151 our_feature_string = am_feature_to_string(our_features);
153 set_debug_prefix_pid(getpid());
155 /* handle all service requests */
159 for(; (line = agets(stdin)) != NULL; free(line)) {
160 #define sc "OPTIONS "
161 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
163 g_options = parse_g_options(line+8, 1);
164 if(!g_options->hostname) {
165 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
166 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
167 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
171 if(am_has_feature(g_options->features, fe_rep_options_features)) {
172 printf("features=%s;", our_feature_string);
174 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
175 printf("maxdumps=%d;", g_options->maxdumps);
177 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
178 printf("hostname=%s;", g_options->hostname);
188 skip_whitespace(s, ch); /* find the program name */
190 err_extra = "no program name";
191 goto err; /* no program name */
194 skip_non_whitespace(s, ch);
197 program_is_wrapper=0;
198 if(strcmp(prog,"DUMPER")==0) {
199 program_is_wrapper=1;
200 skip_whitespace(s, ch); /* find dumper name */
202 goto err; /* no program */
205 skip_non_whitespace(s, ch);
209 if(strncmp(prog, "CALCSIZE", 8) == 0) {
210 skip_whitespace(s, ch); /* find the program name */
212 err_extra = "no program name";
216 skip_non_whitespace(s, ch);
223 skip_whitespace(s, ch); /* find the disk name */
225 err_extra = "no disk name";
226 goto err; /* no disk name */
229 skip_non_whitespace(s, ch);
232 skip_whitespace(s, ch); /* find the device or level */
234 err_extra = "bad level";
237 if(!isdigit((int)s[-1])) {
239 skip_non_whitespace(s, ch);
241 amdevice = stralloc(fp);
242 skip_whitespace(s, ch); /* find level number */
245 amdevice = stralloc(disk);
248 /* find the level number */
249 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
250 err_extra = "bad level";
251 goto err; /* bad level */
255 skip_whitespace(s, ch); /* find the dump date */
257 err_extra = "no dumpdate";
258 goto err; /* no dumpdate */
261 skip_non_whitespace(s, ch);
264 spindle = 0; /* default spindle */
266 skip_whitespace(s, ch); /* find the spindle */
268 if(sscanf(s - 1, "%d", &spindle) != 1) {
269 err_extra = "bad spindle";
270 goto err; /* bad spindle */
274 skip_whitespace(s, ch); /* find the exclusion list */
276 if(strncmp(s-1, "OPTIONS |;",10) == 0) {
277 options = parse_options(s + 8,
284 options = alloc(sizeof(option_t));
285 init_options(options);
286 if(strncmp(s-1, "exclude-file=", 13) == 0) {
287 options->exclude_file =
288 append_sl(options->exclude_file, s+12);
290 if(strncmp(s-1, "exclude-list=", 13) == 0) {
291 options->exclude_list =
292 append_sl(options->exclude_list, s+12);
295 skip_non_whitespace(s, ch);
297 err_extra = "extra text at end";
298 goto err; /* should have gotten to end */
303 options = alloc(sizeof(option_t));
304 init_options(options);
308 add_diskest(disk, amdevice, level, spindle, program_is_wrapper, prog, calcprog, options);
322 * See if we need to wait for a child before we can do anything
327 amwait_t child_status;
331 dbprintf(("%s: waiting for any estimate child: %d running\n",
332 debug_prefix_time(NULL), dumpsrunning));
333 child_pid = wait(&child_status);
334 if(child_pid == -1) {
335 error("wait failed: %s", strerror(errno));
337 if(WIFSIGNALED(child_status)) {
338 dbprintf(("%s: child %ld terminated with signal %d\n",
339 debug_prefix_time(NULL),
340 (long) child_pid, WTERMSIG(child_status)));
342 exit_code = WEXITSTATUS(child_status);
344 dbprintf(("%s: child %ld terminated normally\n",
345 debug_prefix_time(NULL), (long) child_pid));
347 dbprintf(("%s: child %ld terminated with code %d\n",
348 debug_prefix_time(NULL),
349 (long) child_pid, exit_code));
353 * Find the child and mark it done.
355 for(est = est_list; est != NULL; est = est->next) {
356 if(est->child == child_pid) {
361 dbprintf(("%s: unexpected child %ld\n",
362 debug_prefix_time(NULL), (long)child_pid));
370 * If we are already running the maximum number of children
371 * go back and wait until one of them finishes.
373 if(dumpsrunning >= g_options->maxdumps) {
376 continue; /* have to wait first */
379 * Find a new child to start.
381 for(est = est_list; est != NULL; est = est->next) {
383 done = 0; /* more to do */
385 if(est->child != 0 || est->done) {
386 continue; /* child is running or done */
389 * Make sure there is no spindle conflict.
391 if(est->spindle != -1) {
392 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
393 if(est1->child == 0 || est == est1 || est1->done) {
395 * Ignore anything not yet started, ourself,
396 * and anything completed.
400 if(est1->spindle == est->spindle) {
401 break; /* oops -- they match */
405 continue; /* spindle conflict */
408 break; /* start this estimate */
411 if(dumpsrunning > 0) {
412 need_wait = 1; /* nothing to do but wait */
416 if((est->child = fork()) == 0) {
417 set_debug_prefix_pid(getpid());
418 calc_estimates(est); /* child does the estimate */
420 } else if(est->child == -1) {
421 error("calc_estimates fork failed: %s", strerror(errno));
423 dumpsrunning++; /* parent */
428 for(est = est_list; est != NULL; est = est->next) {
434 amfree(our_feature_string);
435 am_release_feature_set(our_features);
437 am_release_feature_set(g_options->features);
438 g_options->features = NULL;
439 amfree(g_options->hostname);
440 amfree(g_options->str);
443 malloc_size_2 = malloc_inuse(&malloc_hist_2);
445 if(malloc_size_1 != malloc_size_2) {
446 #if defined(USE_DBMALLOC)
447 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
454 printf("FORMAT ERROR IN REQUEST PACKET\n");
455 dbprintf(("%s: REQ packet is bogus%s%s\n",
456 debug_prefix_time(NULL),
457 err_extra ? ": " : "",
458 err_extra ? err_extra : ""));
464 void add_diskest(disk, amdevice, level, spindle, program_is_wrapper, prog, calcprog, options)
465 char *disk, *amdevice, *prog, *calcprog;
466 int level, spindle, program_is_wrapper;
469 disk_estimates_t *newp, *curp;
474 for(curp = est_list; curp != NULL; curp = curp->next) {
475 if(strcmp(curp->amname, disk) == 0) {
476 /* already have disk info, just note the level request */
477 curp->est[level].needestimate = 1;
479 free_sl(options->exclude_file);
480 free_sl(options->exclude_list);
481 free_sl(options->include_file);
482 free_sl(options->include_list);
483 amfree(options->auth);
484 amfree(options->str);
491 newp = (disk_estimates_t *) alloc(sizeof(disk_estimates_t));
492 memset(newp, 0, sizeof(*newp));
493 newp->next = est_list;
495 newp->amname = stralloc(disk);
496 newp->amdevice = stralloc(amdevice);
497 newp->dirname = amname_to_dirname(newp->amdevice);
498 newp->program = stralloc(prog);
500 newp->calcprog = stralloc(calcprog);
502 newp->calcprog = NULL;
503 newp->program_is_wrapper = program_is_wrapper;
504 newp->spindle = spindle;
505 newp->est[level].needestimate = 1;
506 newp->options = options;
508 /* fill in dump-since dates */
510 amdp = amandates_lookup(newp->amname);
512 newp->est[0].dumpsince = EPOCH;
513 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
514 dumpdate = amdp->dates[dumplev];
515 for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
516 if(dumpdate > newp->est[estlev].dumpsince)
517 newp->est[estlev].dumpsince = dumpdate;
523 void free_estimates(est)
524 disk_estimates_t *est;
527 amfree(est->amdevice);
528 amfree(est->dirname);
529 amfree(est->program);
531 free_sl(est->options->exclude_file);
532 free_sl(est->options->exclude_list);
533 free_sl(est->options->include_file);
534 free_sl(est->options->include_list);
535 amfree(est->options->str);
536 amfree(est->options->auth);
537 amfree(est->options);
542 * ------------------------------------------------------------------------
546 void calc_estimates(est)
547 disk_estimates_t *est;
549 dbprintf(("%s: calculating for amname '%s', dirname '%s', spindle %d\n",
550 debug_prefix_time(NULL),
551 est->amname, est->dirname, est->spindle));
553 if(est->program_is_wrapper == 1)
554 wrapper_calc_estimates(est);
556 #ifndef USE_GENERIC_CALCSIZE
557 if(strcmp(est->program, "DUMP") == 0)
558 dump_calc_estimates(est);
562 if (strcmp(est->program, "GNUTAR") == 0 &&
563 est->amdevice[0] == '/' && est->amdevice[1] == '/')
564 smbtar_calc_estimates(est);
568 if (strcmp(est->program, "GNUTAR") == 0)
569 gnutar_calc_estimates(est);
573 if (est->amdevice[0] == '/' && est->amdevice[1] == '/')
574 dbprintf(("%s: Can't use CALCSIZE for samba estimate: %s %s\n",
575 debug_prefix_time(NULL),
576 est->amname, est->dirname));
579 generic_calc_estimates(est);
581 dbprintf(("%s: done with amname '%s', dirname '%s', spindle %d\n",
582 debug_prefix_time(NULL),
583 est->amname, est->dirname, est->spindle));
587 * ------------------------------------------------------------------------
591 /* local functions */
592 long getsize_dump P((char *disk, char *amdevice, int level, option_t *options));
593 long getsize_smbtar P((char *disk, char *amdevice, int level, option_t *options));
594 long getsize_gnutar P((char *disk, char *amdevice, int level,
595 option_t *options, time_t dumpsince));
596 long getsize_wrapper P((char *program, char *disk, char *amdevice, int level,
597 option_t *options, time_t dumpsince));
598 long handle_dumpline P((char *str));
599 double first_num P((char *str));
601 void wrapper_calc_estimates(est)
602 disk_estimates_t *est;
607 for(level = 0; level < DUMP_LEVELS; level++) {
608 if (est->est[level].needestimate) {
609 dbprintf(("%s: getting size via wrapper for %s level %d\n",
610 debug_prefix_time(NULL), est->amname, level));
611 size = getsize_wrapper(est->program, est->amname, est->amdevice, level, est->options,
612 est->est[level].dumpsince);
616 fseek(stdout, (off_t)0, SEEK_SET);
618 printf("%s %d SIZE %ld\n", est->amname, level, size);
621 amfunlock(1, "size");
627 void generic_calc_estimates(est)
628 disk_estimates_t *est;
630 int pipefd = -1, nullfd = -1;
632 char *my_argv[DUMP_LEVELS*2+20];
633 char number[NUM_STR_SIZE];
634 int i, level, my_argc, calcpid;
637 char *file_exclude = NULL;
638 char *file_include = NULL;
640 FILE *dumpout = NULL;
645 cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
648 my_argv[my_argc++] = stralloc("calcsize");
649 my_argv[my_argc++] = stralloc(est->calcprog);
651 my_argv[my_argc++] = stralloc(est->amname);
652 my_argv[my_argc++] = stralloc(est->dirname);
655 if(est->options->exclude_file)
656 nb_exclude += est->options->exclude_file->nb_element;
657 if(est->options->exclude_list)
658 nb_exclude += est->options->exclude_list->nb_element;
659 if(est->options->include_file)
660 nb_include += est->options->include_file->nb_element;
661 if(est->options->include_list)
662 nb_include += est->options->include_list->nb_element;
665 file_exclude = build_exclude(est->amname, est->amdevice,est->options,0);
667 file_include = build_include(est->amname, est->amdevice,est->options,0);
670 my_argv[my_argc++] = stralloc("-X");
671 my_argv[my_argc++] = file_exclude;
675 my_argv[my_argc++] = stralloc("-I");
676 my_argv[my_argc++] = file_include;
678 start_time = curclock();
680 dbprintf(("%s: running cmd: %s", debug_prefix_time(NULL), my_argv[0]));
681 for(i=0; i<my_argc; ++i)
682 dbprintf((" %s", my_argv[i]));
684 for(level = 0; level < DUMP_LEVELS; level++) {
685 if(est->est[level].needestimate) {
686 snprintf(number, sizeof(number), "%d", level);
687 my_argv[my_argc++] = stralloc(number);
688 dbprintf((" %s", number));
689 snprintf(number, sizeof(number),
690 "%ld", (long)est->est[level].dumpsince);
691 my_argv[my_argc++] = stralloc(number);
692 dbprintf((" %s", number));
695 my_argv[my_argc] = NULL;
698 fflush(stderr); fflush(stdout);
700 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
701 dbprintf(("Cannot access /dev/null : %s\n", strerror(errno)));
705 calcpid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
708 dumpout = fdopen(pipefd,"r");
709 match_expr = vstralloc(est->amname," %d SIZE %ld", NULL);
710 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
711 if(sscanf(line, match_expr, &level, &size) == 2) {
712 printf("%s\n", line); /* write to amandad */
713 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
722 dbprintf(("%s: waiting for %s \"%s\" child\n",
723 debug_prefix_time(NULL), my_argv[0], est->amdevice));
725 dbprintf(("%s: after %s \"%s\" wait\n",
726 debug_prefix_time(NULL), my_argv[0], est->amdevice));
728 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
729 dbprintf(("%s: estimate time for %s: %s\n",
732 walltime_str(timessub(curclock(), start_time))));
735 for(i = 0; i < my_argc; i++) {
742 void dump_calc_estimates(est)
743 disk_estimates_t *est;
748 for(level = 0; level < DUMP_LEVELS; level++) {
749 if(est->est[level].needestimate) {
750 dbprintf(("%s: getting size via dump for %s level %d\n",
751 debug_prefix_time(NULL), est->amname, level));
752 size = getsize_dump(est->amname, est->amdevice,level, est->options);
756 fseek(stdout, (off_t)0, SEEK_SET);
758 printf("%s %d SIZE %ld\n", est->amname, level, size);
761 amfunlock(1, "size");
767 void smbtar_calc_estimates(est)
768 disk_estimates_t *est;
773 for(level = 0; level < DUMP_LEVELS; level++) {
774 if(est->est[level].needestimate) {
775 dbprintf(("%s: getting size via smbclient for %s level %d\n",
776 debug_prefix_time(NULL), est->amname, level));
777 size = getsize_smbtar(est->amname, est->amdevice, level, est->options);
781 fseek(stdout, (off_t)0, SEEK_SET);
783 printf("%s %d SIZE %ld\n", est->amname, level, size);
786 amfunlock(1, "size");
793 void gnutar_calc_estimates(est)
794 disk_estimates_t *est;
799 for(level = 0; level < DUMP_LEVELS; level++) {
800 if (est->est[level].needestimate) {
801 dbprintf(("%s: getting size via gnutar for %s level %d\n",
802 debug_prefix_time(NULL), est->amname, level));
803 size = getsize_gnutar(est->amname, est->amdevice, level,
804 est->options, est->est[level].dumpsince);
808 fseek(stdout, (off_t)0, SEEK_SET);
810 printf("%s %d SIZE %ld\n", est->amname, level, size);
813 amfunlock(1, "size");
819 typedef struct regex_s {
824 regex_t re_size[] = {
826 {" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
827 {" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
828 {" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
829 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
830 {"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
831 {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
832 {"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
833 {"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
834 {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
835 {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
836 {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
838 {"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
839 {"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
840 {"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
841 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
843 #ifdef HAVE_DUMP_ESTIMATE
844 {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
845 /* DU 3.2g dump -E */
846 {"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
847 {"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
852 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
856 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
857 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
861 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
865 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
869 #if SAMBA_VERSION >= 2
870 #define SAMBA_DEBUG_LEVEL "0"
871 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
873 #define SAMBA_DEBUG_LEVEL "3"
874 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
882 long getsize_dump(disk, amdevice, level, options)
883 char *disk, *amdevice;
887 int pipefd[2], nullfd, stdoutfd, killctl[2];
891 char *dumpkeys = NULL;
897 char *rundump_cmd = NULL;
898 char level_str[NUM_STR_SIZE];
902 snprintf(level_str, sizeof(level_str), "%d", level);
904 device = amname_to_devname(amdevice);
905 fstype = amname_to_fstype(amdevice);
907 dbprintf(("%s: calculating for device '%s' with '%s'\n",
908 debug_prefix_time(NULL), device, fstype));
910 cmd = vstralloc(libexecdir, "/rundump", versionsuffix(), NULL);
911 rundump_cmd = stralloc(cmd);
913 if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
914 dbprintf(("getsize_dump could not open /dev/null: %s\n",
922 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
925 #ifdef XFSDUMP /* { */
927 if (strcmp(fstype, "xfs") == 0)
932 name = stralloc(" (xfsdump)");
933 dbprintf(("%s: running \"%s%s -F -J -l %s - %s\"\n",
934 debug_prefix_time(NULL), cmd, name, level_str, device));
938 #ifdef VXDUMP /* { */
940 if (strcmp(fstype, "vxfs") == 0)
946 name = stralloc(" (vxdump)");
949 cmd = newstralloc(cmd, VXDUMP);
951 dumpkeys = vstralloc(level_str, "s", "f", NULL);
952 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
953 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
959 if (strcmp(fstype, "advfs") == 0)
964 name = stralloc(" (vdump)");
966 device = amname_to_dirname(amdevice);
967 dumpkeys = vstralloc(level_str, "b", "f", NULL);
968 dbprintf(("%s: running \"%s%s %s 60 - %s\"\n",
969 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
975 # ifdef USE_RUNDUMP /* { */
976 # ifdef AIX_BACKUP /* { */
977 name = stralloc(" (backup)");
979 name = vstralloc(" (", DUMP, ")", NULL);
983 cmd = newstralloc(cmd, DUMP);
986 # ifdef AIX_BACKUP /* { */
987 dumpkeys = vstralloc("-", level_str, "f", NULL);
988 dbprintf(("%s: running \"%s%s %s - %s\"\n",
989 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
991 dumpkeys = vstralloc(level_str,
992 # ifdef HAVE_DUMP_ESTIMATE /* { */
995 # ifdef HAVE_HONOR_NODUMP /* { */
1000 # ifdef HAVE_DUMP_ESTIMATE
1001 stdoutfd = pipefd[1];
1004 # ifdef HAVE_HONOR_NODUMP /* { */
1005 dbprintf(("%s: running \"%s%s %s 0 1048576 - %s\"\n",
1006 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
1008 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
1009 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
1016 error("no dump program available");
1021 start_time = curclock();
1022 switch(dumppid = fork()) {
1024 dbprintf(("%s: cannot fork for killpgrp: %s\n",
1025 debug_prefix(NULL), strerror(errno)));
1028 amfree(rundump_cmd);
1035 case 0: /* child process */
1038 else if (killctl[0] == -1 || killctl[1] == -1)
1039 dbprintf(("%s: pipe for killpgrp failed, trying without killpgrp\n",
1040 debug_prefix(NULL)));
1044 dbprintf(("%s: fork failed, trying without killpgrp\n",
1045 debug_prefix(NULL)));
1050 char *killpgrp_cmd = vstralloc(libexecdir, "/killpgrp",
1051 versionsuffix(), NULL);
1052 dbprintf(("%s: running %s\n",
1053 debug_prefix_time(NULL), killpgrp_cmd));
1054 dup2(killctl[0], 0);
1061 execle(killpgrp_cmd, killpgrp_cmd, (char *)0, safe_env());
1062 dbprintf(("%s: cannot execute %s: %s\n",
1063 debug_prefix(NULL), killpgrp_cmd, strerror(errno)));
1067 case 0: /* child process */
1076 if (killctl[0] != -1)
1078 if (killctl[1] != -1)
1083 if (strcmp(fstype, "xfs") == 0)
1087 execle(cmd, "xfsdump", "-F", "-J", "-l", level_str, "-", device,
1088 (char *)0, safe_env());
1093 if (strcmp(fstype, "vxfs") == 0)
1097 execle(cmd, "vxdump", dumpkeys, "1048576", "-", device, (char *)0,
1103 if (strcmp(fstype, "advfs") == 0)
1107 execle(cmd, "vdump", dumpkeys, "60", "-", device, (char *)0,
1113 execle(cmd, "backup", dumpkeys, "-", device, (char *)0, safe_env());
1115 execle(cmd, "dump", dumpkeys,
1116 #ifdef HAVE_HONOR_NODUMP
1119 "1048576", "-", device, (char *)0, safe_env());
1123 error("exec %s failed or no dump program available: %s",
1124 cmd, strerror(errno));
1129 amfree(rundump_cmd);
1132 if (killctl[0] != -1)
1134 dumpout = fdopen(pipefd[0],"r");
1136 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
1137 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1138 size = handle_dumpline(line);
1141 if((line = agets(dumpout)) != NULL) {
1142 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1149 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1150 dbprintf(("%s: estimate time for %s level %d: %s\n",
1154 walltime_str(timessub(curclock(), start_time))));
1156 dbprintf(("%s: no size line match in %s%s output for \"%s\"\n",
1157 debug_prefix(NULL), cmd, name, disk));
1158 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1159 dbprintf(("%s: Run %s%s manually to check for errors\n",
1160 debug_prefix(NULL), cmd, name));
1161 } else if(size == 0 && level == 0) {
1162 dbprintf(("%s: possible %s%s problem -- is \"%s\" really empty?\n",
1163 debug_prefix(NULL), cmd, name, disk));
1164 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1166 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1173 if (killctl[1] != -1) {
1174 dbprintf(("%s: asking killpgrp to terminate\n",
1175 debug_prefix_time(NULL)));
1177 for(s = 5; s > 0; --s) {
1179 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1185 * First, try to kill the dump process nicely. If it ignores us
1186 * for several seconds, hit it harder.
1188 dbprintf(("%s: sending SIGTERM to process group %ld\n",
1189 debug_prefix_time(NULL), (long)dumppid));
1190 if (kill(-dumppid, SIGTERM) == -1) {
1191 dbprintf(("%s: kill failed: %s\n",
1192 debug_prefix(NULL), strerror(errno)));
1194 /* Now check whether it dies */
1195 for(s = 5; s > 0; --s) {
1197 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1201 dbprintf(("%s: sending SIGKILL to process group %ld\n",
1202 debug_prefix_time(NULL), (long)dumppid));
1203 if (kill(-dumppid, SIGKILL) == -1) {
1204 dbprintf(("%s: kill failed: %s\n",
1205 debug_prefix(NULL), strerror(errno)));
1207 for(s = 5; s > 0; --s) {
1209 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1213 dbprintf(("%s: waiting for %s%s \"%s\" child\n",
1214 debug_prefix_time(NULL), cmd, name, disk));
1216 dbprintf(("%s: after %s%s \"%s\" wait\n",
1217 debug_prefix_time(NULL), cmd, name, disk));
1234 long getsize_smbtar(disk, amdevice, level, optionns)
1235 char *disk, *amdevice;
1239 int pipefd = -1, nullfd = -1, passwdfd = -1;
1243 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1244 char *share = NULL, *subdir = NULL;
1251 char *error_pn = NULL;
1253 error_pn = stralloc2(get_pname(), "-smbclient");
1255 parsesharename(amdevice, &share, &subdir);
1259 set_pname(error_pn);
1261 error("cannot parse disk entry '%s' for share/subdir", disk);
1263 if ((subdir) && (SAMBA_VERSION < 2)) {
1266 set_pname(error_pn);
1268 error("subdirectory specified for share '%s' but samba not v2 or better", disk);
1270 if ((user_and_password = findpass(share, &domain)) == NULL) {
1273 memset(domain, '\0', strlen(domain));
1276 set_pname(error_pn);
1278 error("cannot find password for %s", disk);
1280 lpass = strlen(user_and_password);
1281 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1282 memset(user_and_password, '\0', lpass);
1283 amfree(user_and_password);
1285 memset(domain, '\0', strlen(domain));
1288 set_pname(error_pn);
1290 error("password field not \'user%%pass\' for %s", disk);
1293 pwtext_len = strlen(pwtext);
1294 if ((sharename = makesharename(share, 0)) == NULL) {
1295 memset(user_and_password, '\0', lpass);
1296 amfree(user_and_password);
1298 memset(domain, '\0', strlen(domain));
1301 set_pname(error_pn);
1303 error("cannot make share name of %s", share);
1305 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1306 memset(user_and_password, '\0', lpass);
1307 amfree(user_and_password);
1309 memset(domain, '\0', strlen(domain));
1312 set_pname(error_pn);
1315 error("could not open /dev/null: %s\n",
1319 #if SAMBA_VERSION >= 2
1321 tarkeys = "archive 0;recurse;du";
1323 tarkeys = "archive 1;recurse;du";
1326 tarkeys = "archive 0;recurse;dir";
1328 tarkeys = "archive 1;recurse;dir";
1331 start_time = curclock();
1333 if (pwtext_len > 0) {
1334 pw_fd_env = "PASSWD_FD";
1336 pw_fd_env = "dummy_PASSWD_FD";
1338 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
1339 &nullfd, &nullfd, &pipefd,
1340 pw_fd_env, &passwdfd,
1343 "-d", SAMBA_DEBUG_LEVEL,
1344 *user_and_password ? "-U" : skip_argument,
1345 *user_and_password ? user_and_password : skip_argument,
1347 domain ? "-W" : skip_argument,
1348 domain ? domain : skip_argument,
1349 #if SAMBA_VERSION >= 2
1350 subdir ? "-D" : skip_argument,
1351 subdir ? subdir : skip_argument,
1356 memset(domain, '\0', strlen(domain));
1360 if(pwtext_len > 0 && fullwrite(passwdfd, pwtext, pwtext_len) < 0) {
1361 int save_errno = errno;
1363 memset(user_and_password, '\0', lpass);
1364 amfree(user_and_password);
1366 set_pname(error_pn);
1368 error("password write failed: %s", strerror(save_errno));
1370 memset(user_and_password, '\0', lpass);
1371 amfree(user_and_password);
1377 dumpout = fdopen(pipefd,"r");
1379 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
1380 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1381 size = handle_dumpline(line);
1384 if((line = agets(dumpout)) != NULL) {
1385 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1392 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1393 dbprintf(("%s: estimate time for %s level %d: %s\n",
1397 walltime_str(timessub(curclock(), start_time))));
1399 dbprintf(("%s: no size line match in %s output for \"%s\"\n",
1400 debug_prefix(NULL), SAMBA_CLIENT, disk));
1401 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1402 } else if(size == 0 && level == 0) {
1403 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
1404 debug_prefix(NULL), SAMBA_CLIENT, disk));
1405 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1407 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1413 kill(-dumppid, SIGTERM);
1415 dbprintf(("%s: waiting for %s \"%s\" child\n",
1416 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1418 dbprintf(("%s: after %s \"%s\" wait\n",
1419 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1431 long getsize_gnutar(disk, amdevice, level, options, dumpsince)
1432 char *disk, *amdevice;
1437 int pipefd = -1, nullfd = -1, dumppid;
1439 FILE *dumpout = NULL;
1440 char *incrname = NULL;
1441 char *basename = NULL;
1442 char *dirname = NULL;
1443 char *inputname = NULL;
1448 char dumptimestr[80];
1454 char *file_exclude = NULL;
1455 char *file_include = NULL;
1458 if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
1459 if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
1460 if(options->include_file) nb_include += options->include_file->nb_element;
1461 if(options->include_list) nb_include += options->include_list->nb_element;
1463 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
1464 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
1466 my_argv = alloc(sizeof(char *) * 21);
1469 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
1471 char number[NUM_STR_SIZE];
1476 basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
1478 g_options->hostname,
1482 * The loop starts at the first character of the host name,
1485 s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
1486 while((ch = *s++) != '\0') {
1487 if(ch == '/' || isspace(ch)) s[-1] = '_';
1490 snprintf(number, sizeof(number), "%d", level);
1491 incrname = vstralloc(basename, "_", number, ".new", NULL);
1495 * Open the listed incremental file from the previous level. Search
1496 * backward until one is found. If none are found (which will also
1497 * be true for a level 0), arrange to read from /dev/null.
1500 while (in == NULL) {
1501 if (--baselevel >= 0) {
1502 snprintf(number, sizeof(number), "%d", baselevel);
1503 inputname = newvstralloc(inputname,
1504 basename, "_", number, NULL);
1506 inputname = newstralloc(inputname, "/dev/null");
1508 if ((in = fopen(inputname, "r")) == NULL) {
1509 int save_errno = errno;
1511 dbprintf(("%s: gnutar: error opening %s: %s\n",
1512 debug_prefix(NULL), inputname, strerror(save_errno)));
1513 if (baselevel < 0) {
1520 * Copy the previous listed incremental file to the new one.
1522 if ((out = fopen(incrname, "w")) == NULL) {
1523 dbprintf(("%s: opening %s: %s\n",
1524 debug_prefix(NULL), incrname, strerror(errno)));
1528 for (; (line = agets(in)) != NULL; free(line)) {
1529 if (fputs(line, out) == EOF || putc('\n', out) == EOF) {
1530 dbprintf(("%s: writing to %s: %s\n",
1531 debug_prefix(NULL), incrname, strerror(errno)));
1538 dbprintf(("%s: reading from %s: %s\n",
1539 debug_prefix(NULL), inputname, strerror(errno)));
1542 if (fclose(in) == EOF) {
1543 dbprintf(("%s: closing %s: %s\n",
1544 debug_prefix(NULL), inputname, strerror(errno)));
1549 if (fclose(out) == EOF) {
1550 dbprintf(("%s: closing %s: %s\n",
1551 debug_prefix(NULL), incrname, strerror(errno)));
1562 gmtm = gmtime(&dumpsince);
1563 snprintf(dumptimestr, sizeof(dumptimestr),
1564 "%04d-%02d-%02d %2d:%02d:%02d GMT",
1565 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
1566 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
1568 dirname = amname_to_dirname(amdevice);
1573 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
1575 my_argv[i++] = GNUTAR;
1577 my_argv[i++] = "tar";
1579 my_argv[i++] = "--create";
1580 my_argv[i++] = "--file";
1581 my_argv[i++] = "/dev/null";
1582 my_argv[i++] = "--directory";
1583 my_argv[i++] = dirname;
1584 my_argv[i++] = "--one-file-system";
1585 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
1586 my_argv[i++] = "--listed-incremental";
1587 my_argv[i++] = incrname;
1589 my_argv[i++] = "--incremental";
1590 my_argv[i++] = "--newer";
1591 my_argv[i++] = dumptimestr;
1593 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
1594 /* --atime-preserve causes gnutar to call
1595 * utime() after reading files in order to
1596 * adjust their atime. However, utime()
1597 * updates the file's ctime, so incremental
1598 * dumps will think the file has changed. */
1599 my_argv[i++] = "--atime-preserve";
1601 my_argv[i++] = "--sparse";
1602 my_argv[i++] = "--ignore-failed-read";
1603 my_argv[i++] = "--totals";
1606 my_argv[i++] = "--exclude-from";
1607 my_argv[i++] = file_exclude;
1611 my_argv[i++] = "--files-from";
1612 my_argv[i++] = file_include;
1617 my_argv[i++] = NULL;
1619 start_time = curclock();
1621 nullfd = open("/dev/null", O_RDWR);
1622 dumppid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
1624 amfree(file_exclude);
1625 amfree(file_include);
1627 dumpout = fdopen(pipefd,"r");
1629 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
1630 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1631 size = handle_dumpline(line);
1634 if((line = agets(dumpout)) != NULL) {
1635 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1642 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1643 dbprintf(("%s: estimate time for %s level %d: %s\n",
1647 walltime_str(timessub(curclock(), start_time))));
1649 dbprintf(("%s: no size line match in %s output for \"%s\"\n",
1650 debug_prefix(NULL), my_argv[0], disk));
1651 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1652 } else if(size == 0 && level == 0) {
1653 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
1654 debug_prefix(NULL), my_argv[0], disk));
1655 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1657 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1663 kill(-dumppid, SIGTERM);
1665 dbprintf(("%s: waiting for %s \"%s\" child\n",
1666 debug_prefix_time(NULL), my_argv[0], disk));
1668 dbprintf(("%s: after %s \"%s\" wait\n",
1669 debug_prefix_time(NULL), my_argv[0], disk));
1691 long getsize_wrapper(program, disk, amdevice, level, options, dumpsince)
1692 char *program, *disk, *amdevice;
1697 int pipefd[2], nullfd, dumppid;
1702 char dumptimestr[80];
1705 char *argvchild[10];
1706 char *newoptstr = NULL;
1710 gmtm = gmtime(&dumpsince);
1711 snprintf(dumptimestr, sizeof(dumptimestr),
1712 "%04d-%02d-%02d %2d:%02d:%02d GMT",
1713 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
1714 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
1716 cmd = vstralloc(DUMPER_DIR, "/", program, NULL);
1719 argvchild[i++] = program;
1720 argvchild[i++] = "estimate";
1722 argvchild[i++] = "full";
1724 char levelstr[NUM_STR_SIZE];
1725 snprintf(levelstr,sizeof(levelstr),"%d",level);
1726 argvchild[i++] = "level";
1727 argvchild[i++] = levelstr;
1729 argvchild[i++] = amdevice;
1730 newoptstr = vstralloc(options->str,"estimate-direct;", NULL);
1731 argvchild[i++] = newoptstr;
1732 argvchild[i] = NULL;
1734 dbprintf(("%s: running %s", debug_prefix_time(NULL), cmd));
1735 for(j = 1; j < i; j++) {
1736 dbprintf((" %s", argvchild[j]));
1739 nullfd = open("/dev/null", O_RDWR);
1742 start_time = curclock();
1744 switch(dumppid = fork()) {
1756 execve(cmd, argvchild, safe_env());
1757 error("exec %s failed: %s", cmd, strerror(errno));
1762 dumpout = fdopen(pipefd[0],"r");
1764 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
1765 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1766 i = sscanf(line,"%ld %ld",&size1, &size2);
1768 size = size1 * size2;
1772 if((line = agets(dumpout)) != NULL) {
1773 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1780 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1781 dbprintf(("%s: estimate time for %s level %d: %s\n",
1785 walltime_str(timessub(curclock(), start_time))));
1787 dbprintf(("%s: no size line match in %s output for \"%s\"\n",
1788 debug_prefix(NULL), cmd, disk));
1789 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1790 } else if(size == 0 && level == 0) {
1791 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
1792 debug_prefix(NULL), cmd, disk));
1793 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1795 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1801 kill(-dumppid, SIGTERM);
1803 dbprintf(("%s: waiting for %s \"%s\" child\n",
1804 debug_prefix_time(NULL), cmd, disk));
1806 dbprintf(("%s: after %s \"%s\" wait\n",
1807 debug_prefix_time(NULL), cmd, disk));
1820 double first_num(str)
1823 * Returns the value of the first integer in a string.
1831 while(ch && !isdigit(ch)) ch = *str++;
1833 while(isdigit(ch) || (ch == '.')) ch = *str++;
1841 long handle_dumpline(str)
1844 * Checks the dump output line against the error and size regex tables.
1850 /* check for size match */
1851 for(rp = re_size; rp->regex != NULL; rp++) {
1852 if(match(rp->regex, str)) {
1853 size = ((first_num(str)*rp->scale+1023.0)/1024.0);
1854 if(size < 0) size = 1; /* found on NeXT -- sigh */