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 2003/10/24 20:38:23 kovert 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;
86 level_estimate_t est[DUMP_LEVELS];
89 disk_estimates_t *est_list;
91 static am_feature_t *our_features = NULL;
92 static char *our_feature_string = NULL;
93 static g_option_t *g_options = NULL;
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 *));
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;
120 char *err_extra = NULL;
122 unsigned long malloc_hist_1, malloc_size_1;
123 unsigned long malloc_hist_2, malloc_size_2;
131 for(fd = 3; fd < FD_SETSIZE; fd++) {
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).
143 set_pname("sendsize");
145 malloc_size_1 = malloc_inuse(&malloc_hist_1);
147 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
150 dbprintf(("%s: version %s\n", get_pname(), version()));
152 our_features = am_init_feature_set();
153 our_feature_string = am_feature_to_string(our_features);
155 set_debug_prefix_pid(getpid());
157 /* handle all service requests */
161 for(; (line = agets(stdin)) != NULL; free(line)) {
162 #define sc "OPTIONS "
163 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
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';
173 if(am_has_feature(g_options->features, fe_rep_options_features)) {
174 printf("features=%s;", our_feature_string);
176 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
177 printf("maxdumps=%d;", g_options->maxdumps);
179 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
180 printf("hostname=%s;", g_options->hostname);
190 skip_whitespace(s, ch); /* find the program name */
192 err_extra = "no program name";
193 goto err; /* no program name */
196 skip_non_whitespace(s, ch);
199 skip_whitespace(s, ch); /* find the disk name */
201 err_extra = "no disk name";
202 goto err; /* no disk name */
205 skip_non_whitespace(s, ch);
208 skip_whitespace(s, ch); /* find the device or level */
210 err_extra = "bad level";
213 if(!isdigit((int)s[-1])) {
215 skip_non_whitespace(s, ch);
217 amdevice = stralloc(fp);
218 skip_whitespace(s, ch); /* find level number */
221 amdevice = stralloc(disk);
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 */
231 skip_whitespace(s, ch); /* find the dump date */
233 err_extra = "no dumpdate";
234 goto err; /* no dumpdate */
237 skip_non_whitespace(s, ch);
240 spindle = 0; /* default spindle */
242 skip_whitespace(s, ch); /* find the spindle */
244 if(sscanf(s - 1, "%d", &spindle) != 1) {
245 err_extra = "bad spindle";
246 goto err; /* bad spindle */
250 skip_whitespace(s, ch); /* find the exclusion list */
252 if(strncmp(s-1, "OPTIONS |;",10) == 0) {
253 options = parse_options(s + 8,
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);
266 if(strncmp(s-1, "exclude-list=", 13) == 0) {
267 options->exclude_list =
268 append_sl(options->exclude_list, s+12);
271 skip_non_whitespace(s, ch);
273 err_extra = "extra text at end";
274 goto err; /* should have gotten to end */
279 options = alloc(sizeof(option_t));
280 init_options(options);
284 add_diskest(disk, amdevice, level, spindle, prog, options);
298 * See if we need to wait for a child before we can do anything
303 amwait_t child_status;
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));
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)));
318 exit_code = WEXITSTATUS(child_status);
320 dbprintf(("%s: child %ld terminated normally\n",
321 debug_prefix_time(NULL), (long) child_pid));
323 dbprintf(("%s: child %ld terminated with code %d\n",
324 debug_prefix_time(NULL),
325 (long) child_pid, exit_code));
329 * Find the child and mark it done.
331 for(est = est_list; est != NULL; est = est->next) {
332 if(est->child == child_pid) {
337 dbprintf(("%s: unexpected child %ld\n",
338 debug_prefix_time(NULL), (long)child_pid));
346 * If we are already running the maximum number of children
347 * go back and wait until one of them finishes.
349 if(dumpsrunning >= g_options->maxdumps) {
352 continue; /* have to wait first */
355 * Find a new child to start.
357 for(est = est_list; est != NULL; est = est->next) {
359 done = 0; /* more to do */
361 if(est->child != 0 || est->done) {
362 continue; /* child is running or done */
365 * Make sure there is no spindle conflict.
367 if(est->spindle != -1) {
368 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
369 if(est1->child == 0 || est == est1 || est1->done) {
371 * Ignore anything not yet started, ourself,
372 * and anything completed.
376 if(est1->spindle == est->spindle) {
377 break; /* oops -- they match */
381 continue; /* spindle conflict */
384 break; /* start this estimate */
387 if(dumpsrunning > 0) {
388 need_wait = 1; /* nothing to do but wait */
392 if((est->child = fork()) == 0) {
393 set_debug_prefix_pid(getpid());
394 calc_estimates(est); /* child does the estimate */
396 } else if(est->child == -1) {
397 error("calc_estimates fork failed: %s", strerror(errno));
399 dumpsrunning++; /* parent */
404 for(est = est_list; est != NULL; est = est->next) {
410 amfree(our_feature_string);
411 am_release_feature_set(our_features);
413 am_release_feature_set(g_options->features);
414 g_options->features = NULL;
415 amfree(g_options->str);
416 amfree(g_options->hostname);
419 malloc_size_2 = malloc_inuse(&malloc_hist_2);
421 if(malloc_size_1 != malloc_size_2) {
422 #if defined(USE_DBMALLOC)
423 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
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 : ""));
440 void add_diskest(disk, amdevice, level, spindle, prog, options)
441 char *disk, *amdevice, *prog;
445 disk_estimates_t *newp, *curp;
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;
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);
466 newp = (disk_estimates_t *) alloc(sizeof(disk_estimates_t));
467 memset(newp, 0, sizeof(*newp));
468 newp->next = est_list;
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;
478 /* fill in dump-since dates */
480 amdp = amandates_lookup(newp->amname);
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;
493 void free_estimates(est)
494 disk_estimates_t *est;
497 amfree(est->amdevice);
498 amfree(est->dirname);
499 amfree(est->program);
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);
511 * ------------------------------------------------------------------------
515 void calc_estimates(est)
516 disk_estimates_t *est;
518 dbprintf(("%s: calculating for amname '%s', dirname '%s', spindle %d\n",
519 debug_prefix_time(NULL),
520 est->amname, est->dirname, est->spindle));
522 #ifndef USE_GENERIC_CALCSIZE
523 if(strcmp(est->program, "DUMP") == 0)
524 dump_calc_estimates(est);
528 if (strcmp(est->program, "GNUTAR") == 0 &&
529 est->amdevice[0] == '/' && est->amdevice[1] == '/')
530 smbtar_calc_estimates(est);
534 if (strcmp(est->program, "GNUTAR") == 0)
535 gnutar_calc_estimates(est);
538 generic_calc_estimates(est);
540 dbprintf(("%s: done with amname '%s', dirname '%s', spindle %d\n",
541 debug_prefix_time(NULL),
542 est->amname, est->dirname, est->spindle));
545 void generic_calc_estimates(est)
546 disk_estimates_t *est;
549 char *argv[DUMP_LEVELS*2+10];
550 char number[NUM_STR_SIZE];
551 int i, level, argc, calcpid;
553 cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
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);
564 argv[argc++] = stralloc(est->amdevice);
565 argv[argc++] = stralloc(est->dirname);
567 dbprintf(("%s: running cmd: %s", debug_prefix_time(NULL), argv[0]));
568 for(i=0; i<argc; ++i)
569 dbprintf((" %s", argv[i]));
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));
585 fflush(stderr); fflush(stdout);
587 switch(calcpid = fork()) {
589 error("%s: fork returned: %s", cmd, strerror(errno));
593 execve(cmd, argv, safe_env());
594 error("%s: execve returned: %s", cmd, strerror(errno));
597 for(i = 0; i < argc; i++) {
602 dbprintf(("%s: waiting for %s \"%s\" child\n",
603 debug_prefix_time(NULL), argv[0], est->amdevice));
605 dbprintf(("%s: after %s \"%s\" wait\n",
606 debug_prefix_time(NULL), argv[0], est->amdevice));
611 * ------------------------------------------------------------------------
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));
624 void dump_calc_estimates(est)
625 disk_estimates_t *est;
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);
638 fseek(stdout, (off_t)0, SEEK_SET);
640 printf("%s %d SIZE %ld\n", est->amname, level, size);
643 amfunlock(1, "size");
649 void smbtar_calc_estimates(est)
650 disk_estimates_t *est;
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);
663 fseek(stdout, (off_t)0, SEEK_SET);
665 printf("%s %d SIZE %ld\n", est->amname, level, size);
668 amfunlock(1, "size");
675 void gnutar_calc_estimates(est)
676 disk_estimates_t *est;
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);
690 fseek(stdout, (off_t)0, SEEK_SET);
692 printf("%s %d SIZE %ld\n", est->amname, level, size);
695 amfunlock(1, "size");
701 typedef struct regex_s {
706 regex_t re_size[] = {
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},
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 */
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 */
734 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
738 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
739 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
743 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
746 #ifdef USE_QUICK_AND_DIRTY_ESTIMATES
747 {"amqde estimate: [0-9][0-9]* kb", 1024}, /* amqde */
751 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
755 #if SAMBA_VERSION >= 2
756 #define SAMBA_DEBUG_LEVEL "0"
757 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
759 #define SAMBA_DEBUG_LEVEL "3"
760 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
768 long getsize_dump(disk, amdevice, level, options)
769 char *disk, *amdevice;
773 int pipefd[2], nullfd, stdoutfd, killctl[2];
777 char *dumpkeys = NULL;
783 char *rundump_cmd = NULL;
784 char level_str[NUM_STR_SIZE];
788 ap_snprintf(level_str, sizeof(level_str), "%d", level);
790 device = amname_to_devname(amdevice);
791 fstype = amname_to_fstype(amdevice);
793 dbprintf(("%s: calculating for device '%s' with '%s'\n",
794 debug_prefix_time(NULL), device, fstype));
796 cmd = vstralloc(libexecdir, "/rundump", versionsuffix(), NULL);
797 rundump_cmd = stralloc(cmd);
799 stdoutfd = nullfd = open("/dev/null", O_RDWR);
800 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
803 #ifdef XFSDUMP /* { */
805 if (strcmp(fstype, "xfs") == 0)
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));
816 #ifdef VXDUMP /* { */
818 if (strcmp(fstype, "vxfs") == 0)
824 name = stralloc(" (vxdump)");
827 cmd = newstralloc(cmd, VXDUMP);
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));
837 if (strcmp(fstype, "advfs") == 0)
842 name = stralloc(" (vdump)");
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));
853 # ifdef USE_RUNDUMP /* { */
854 # ifdef AIX_BACKUP /* { */
855 name = stralloc(" (backup)");
857 name = vstralloc(" (", DUMP, ")", NULL);
861 cmd = newstralloc(cmd, DUMP);
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));
869 dumpkeys = vstralloc(level_str,
870 # ifdef HAVE_DUMP_ESTIMATE /* { */
873 # ifdef HAVE_HONOR_NODUMP /* { */
878 # ifdef HAVE_DUMP_ESTIMATE
879 stdoutfd = pipefd[1];
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));
886 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
887 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
894 error("no dump program available");
899 start_time = curclock();
900 switch(dumppid = fork()) {
902 dbprintf(("%s: cannot fork for killpgrp: %s\n",
903 debug_prefix(NULL), strerror(errno)));
912 case 0: /* child process */
915 else if (killctl[0] == -1 || killctl[1] == -1)
916 dbprintf(("%s: pipe for killpgrp failed, trying without killpgrp\n",
917 debug_prefix(NULL)));
921 dbprintf(("%s: fork failed, trying without killpgrp\n",
922 debug_prefix(NULL)));
927 char *killpgrp_cmd = vstralloc(libexecdir, "/killpgrp",
928 versionsuffix(), NULL);
929 dbprintf(("%s: running %s\n",
930 debug_prefix_time(NULL), killpgrp_cmd));
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)));
944 case 0: /* child process */
953 if (killctl[0] != -1)
955 if (killctl[1] != -1)
960 if (strcmp(fstype, "xfs") == 0)
964 execle(cmd, "xfsdump", "-F", "-J", "-l", level_str, "-", device,
965 (char *)0, safe_env());
970 if (strcmp(fstype, "vxfs") == 0)
974 execle(cmd, "vxdump", dumpkeys, "1048576", "-", device, (char *)0,
980 if (strcmp(fstype, "advfs") == 0)
984 execle(cmd, "vdump", dumpkeys, "60", "-", device, (char *)0,
990 execle(cmd, "backup", dumpkeys, "-", device, (char *)0, safe_env());
992 execle(cmd, "dump", dumpkeys,
993 #ifdef HAVE_HONOR_NODUMP
996 "1048576", "-", device, (char *)0, safe_env());
1000 error("exec %s failed or no dump program available: %s",
1001 cmd, strerror(errno));
1006 amfree(rundump_cmd);
1009 if (killctl[0] != -1)
1011 dumpout = fdopen(pipefd[0],"r");
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);
1018 if((line = agets(dumpout)) != NULL) {
1019 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1026 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1027 dbprintf(("%s: estimate time for %s level %d: %s\n",
1031 walltime_str(timessub(curclock(), start_time))));
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)));
1041 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1047 if (killctl[1] != -1) {
1048 dbprintf(("%s: asking killpgrp to terminate\n",
1049 debug_prefix_time(NULL)));
1051 for(s = 5; s > 0; --s) {
1053 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1059 * First, try to kill the dump process nicely. If it ignores us
1060 * for several seconds, hit it harder.
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)));
1068 /* Now check whether it dies */
1069 for(s = 5; s > 0; --s) {
1071 if (waitpid(dumppid, NULL, WNOHANG) != -1)
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)));
1081 for(s = 5; s > 0; --s) {
1083 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1087 dbprintf(("%s: waiting for %s%s \"%s\" child\n",
1088 debug_prefix_time(NULL), cmd, name, disk));
1090 dbprintf(("%s: after %s%s \"%s\" wait\n",
1091 debug_prefix_time(NULL), cmd, name, disk));
1108 long getsize_smbtar(disk, amdevice, level, optionns)
1109 char *disk, *amdevice;
1113 int pipefd = -1, nullfd = -1, passwdfd = -1;
1117 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1118 char *share = NULL, *subdir = NULL;
1125 char *error_pn = NULL;
1127 error_pn = stralloc2(get_pname(), "-smbclient");
1129 parsesharename(amdevice, &share, &subdir);
1133 set_pname(error_pn);
1135 error("cannot parse disk entry '%s' for share/subdir", disk);
1137 if ((subdir) && (SAMBA_VERSION < 2)) {
1140 set_pname(error_pn);
1142 error("subdirectory specified for share '%s' but samba not v2 or better", disk);
1144 if ((user_and_password = findpass(share, &domain)) == NULL) {
1147 memset(domain, '\0', strlen(domain));
1150 set_pname(error_pn);
1152 error("cannot find password for %s", disk);
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);
1159 memset(domain, '\0', strlen(domain));
1162 set_pname(error_pn);
1164 error("password field not \'user%%pass\' for %s", disk);
1167 pwtext_len = strlen(pwtext);
1168 if ((sharename = makesharename(share, 0)) == NULL) {
1169 memset(user_and_password, '\0', lpass);
1170 amfree(user_and_password);
1172 memset(domain, '\0', strlen(domain));
1175 set_pname(error_pn);
1177 error("cannot make share name of %s", share);
1179 nullfd = open("/dev/null", O_RDWR);
1181 #if SAMBA_VERSION >= 2
1183 tarkeys = "archive 0;recurse;du";
1185 tarkeys = "archive 1;recurse;du";
1188 tarkeys = "archive 0;recurse;dir";
1190 tarkeys = "archive 1;recurse;dir";
1193 start_time = curclock();
1195 if (pwtext_len > 0) {
1196 pw_fd_env = "PASSWD_FD";
1198 pw_fd_env = "dummy_PASSWD_FD";
1200 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
1201 &nullfd, &nullfd, &pipefd,
1202 pw_fd_env, &passwdfd,
1205 "-d", SAMBA_DEBUG_LEVEL,
1206 *user_and_password ? "-U" : skip_argument,
1207 *user_and_password ? user_and_password : skip_argument,
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,
1218 memset(domain, '\0', strlen(domain));
1222 if(pwtext_len > 0 && fullwrite(passwdfd, pwtext, pwtext_len) < 0) {
1223 int save_errno = errno;
1225 memset(user_and_password, '\0', lpass);
1226 amfree(user_and_password);
1228 set_pname(error_pn);
1230 error("password write failed: %s", strerror(save_errno));
1232 memset(user_and_password, '\0', lpass);
1233 amfree(user_and_password);
1239 dumpout = fdopen(pipefd,"r");
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);
1246 if((line = agets(dumpout)) != NULL) {
1247 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1254 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1255 dbprintf(("%s: estimate time for %s level %d: %s\n",
1259 walltime_str(timessub(curclock(), start_time))));
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)));
1269 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1275 kill(-dumppid, SIGTERM);
1277 dbprintf(("%s: waiting for %s \"%s\" child\n",
1278 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1280 dbprintf(("%s: after %s \"%s\" wait\n",
1281 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1293 long getsize_gnutar(disk, amdevice, level, options, dumpsince)
1294 char *disk, *amdevice;
1299 int pipefd = -1, nullfd = -1, dumppid;
1301 FILE *dumpout = NULL;
1302 char *incrname = NULL;
1303 char *basename = NULL;
1304 char *dirname = NULL;
1305 char *inputname = NULL;
1310 char dumptimestr[80];
1316 char *file_exclude = NULL;
1317 char *file_include = NULL;
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;
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);
1328 my_argv = alloc(sizeof(char *) * 21);
1331 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
1333 char number[NUM_STR_SIZE];
1338 basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
1340 g_options->hostname,
1344 * The loop starts at the first character of the host name,
1347 s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
1348 while((ch = *s++) != '\0') {
1349 if(ch == '/' || isspace(ch)) s[-1] = '_';
1352 ap_snprintf(number, sizeof(number), "%d", level);
1353 incrname = vstralloc(basename, "_", number, ".new", NULL);
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.
1362 while (in == NULL) {
1363 if (--baselevel >= 0) {
1364 ap_snprintf(number, sizeof(number), "%d", baselevel);
1365 inputname = newvstralloc(inputname,
1366 basename, "_", number, NULL);
1368 inputname = newstralloc(inputname, "/dev/null");
1370 if ((in = fopen(inputname, "r")) == NULL) {
1371 int save_errno = errno;
1373 dbprintf(("%s: gnutar: error opening %s: %s\n",
1374 debug_prefix(NULL), inputname, strerror(save_errno)));
1375 if (baselevel < 0) {
1382 * Copy the previous listed incremental file to the new one.
1384 if ((out = fopen(incrname, "w")) == NULL) {
1385 dbprintf(("%s: opening %s: %s\n",
1386 debug_prefix(NULL), incrname, strerror(errno)));
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)));
1400 dbprintf(("%s: reading from %s: %s\n",
1401 debug_prefix(NULL), inputname, strerror(errno)));
1404 if (fclose(in) == EOF) {
1405 dbprintf(("%s: closing %s: %s\n",
1406 debug_prefix(NULL), inputname, strerror(errno)));
1411 if (fclose(out) == EOF) {
1412 dbprintf(("%s: closing %s: %s\n",
1413 debug_prefix(NULL), incrname, strerror(errno)));
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);
1430 dirname = amname_to_dirname(amdevice);
1434 #ifdef USE_QUICK_AND_DIRTY_ESTIMATES
1435 ap_snprintf(dumptimestr, sizeof(dumptimestr), "%ld", dumpsince);
1437 cmd = vstralloc(libexecdir, "/", "amqde", versionsuffix(), NULL);
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;
1446 /* [XXX] need to also consider implementation of --files-from */
1447 my_argv[i++] = dirname;
1448 my_argv[i++] = NULL;
1451 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
1453 my_argv[i++] = GNUTAR;
1455 my_argv[i++] = "tar";
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;
1467 my_argv[i++] = "--incremental";
1468 my_argv[i++] = "--newer";
1469 my_argv[i++] = dumptimestr;
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";
1479 my_argv[i++] = "--sparse";
1480 my_argv[i++] = "--ignore-failed-read";
1481 my_argv[i++] = "--totals";
1484 my_argv[i++] = "--exclude-from";
1485 my_argv[i++] = file_exclude;
1489 my_argv[i++] = "--files-from";
1490 my_argv[i++] = file_include;
1495 #endif /* USE_QUICK_AND_DIRTY_ESTIMATES */
1496 my_argv[i++] = NULL;
1498 start_time = curclock();
1500 nullfd = open("/dev/null", O_RDWR);
1501 dumppid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
1503 amfree(file_exclude);
1504 amfree(file_include);
1506 dumpout = fdopen(pipefd,"r");
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);
1513 if((line = agets(dumpout)) != NULL) {
1514 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1521 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1522 dbprintf(("%s: estimate time for %s level %d: %s\n",
1526 walltime_str(timessub(curclock(), start_time))));
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)));
1536 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1542 kill(-dumppid, SIGTERM);
1544 dbprintf(("%s: waiting for %s \"%s\" child\n",
1545 debug_prefix_time(NULL), my_argv[0], disk));
1547 dbprintf(("%s: after %s \"%s\" wait\n",
1548 debug_prefix_time(NULL), my_argv[0], disk));
1571 double first_num(str)
1574 * Returns the value of the first integer in a string.
1582 while(ch && !isdigit(ch)) ch = *str++;
1584 while(isdigit(ch) || (ch == '.')) ch = *str++;
1592 long handle_dumpline(str)
1595 * Checks the dump output line against the error and size regex tables.
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 */