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.3 2005/04/06 12:31:04 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;
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;
123 unsigned long malloc_hist_1, malloc_size_1;
124 unsigned long malloc_hist_2, malloc_size_2;
132 for(fd = 3; fd < FD_SETSIZE; fd++) {
134 * Make sure nobody spoofs us with a lot of extra open files
135 * that would cause an open we do to get a very high file
136 * descriptor, which in turn might be used as an index into
137 * an array (e.g. an fd_set).
144 set_pname("sendsize");
146 malloc_size_1 = malloc_inuse(&malloc_hist_1);
148 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
151 dbprintf(("%s: version %s\n", get_pname(), version()));
153 our_features = am_init_feature_set();
154 our_feature_string = am_feature_to_string(our_features);
156 set_debug_prefix_pid(getpid());
158 /* handle all service requests */
162 for(; (line = agets(stdin)) != NULL; free(line)) {
163 #define sc "OPTIONS "
164 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
166 g_options = parse_g_options(line+8, 1);
167 if(!g_options->hostname) {
168 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
169 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
170 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
174 if(am_has_feature(g_options->features, fe_rep_options_features)) {
175 printf("features=%s;", our_feature_string);
177 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
178 printf("maxdumps=%d;", g_options->maxdumps);
180 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
181 printf("hostname=%s;", g_options->hostname);
191 skip_whitespace(s, ch); /* find the program name */
193 err_extra = "no program name";
194 goto err; /* no program name */
197 skip_non_whitespace(s, ch);
200 if(strncmp(prog, "CALCSIZE", 8) == 0) {
201 skip_whitespace(s, ch); /* find the program name */
203 err_extra = "no program name";
207 skip_non_whitespace(s, ch);
214 skip_whitespace(s, ch); /* find the disk name */
216 err_extra = "no disk name";
217 goto err; /* no disk name */
220 skip_non_whitespace(s, ch);
223 skip_whitespace(s, ch); /* find the device or level */
225 err_extra = "bad level";
228 if(!isdigit((int)s[-1])) {
230 skip_non_whitespace(s, ch);
232 amdevice = stralloc(fp);
233 skip_whitespace(s, ch); /* find level number */
236 amdevice = stralloc(disk);
239 /* find the level number */
240 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
241 err_extra = "bad level";
242 goto err; /* bad level */
246 skip_whitespace(s, ch); /* find the dump date */
248 err_extra = "no dumpdate";
249 goto err; /* no dumpdate */
252 skip_non_whitespace(s, ch);
255 spindle = 0; /* default spindle */
257 skip_whitespace(s, ch); /* find the spindle */
259 if(sscanf(s - 1, "%d", &spindle) != 1) {
260 err_extra = "bad spindle";
261 goto err; /* bad spindle */
265 skip_whitespace(s, ch); /* find the exclusion list */
267 if(strncmp(s-1, "OPTIONS |;",10) == 0) {
268 options = parse_options(s + 8,
275 options = alloc(sizeof(option_t));
276 init_options(options);
277 if(strncmp(s-1, "exclude-file=", 13) == 0) {
278 options->exclude_file =
279 append_sl(options->exclude_file, s+12);
281 if(strncmp(s-1, "exclude-list=", 13) == 0) {
282 options->exclude_list =
283 append_sl(options->exclude_list, s+12);
286 skip_non_whitespace(s, ch);
288 err_extra = "extra text at end";
289 goto err; /* should have gotten to end */
294 options = alloc(sizeof(option_t));
295 init_options(options);
299 add_diskest(disk, amdevice, level, spindle, prog, calcprog, options);
313 * See if we need to wait for a child before we can do anything
318 amwait_t child_status;
322 dbprintf(("%s: waiting for any estimate child: %d running\n",
323 debug_prefix_time(NULL), dumpsrunning));
324 child_pid = wait(&child_status);
325 if(child_pid == -1) {
326 error("wait failed: %s", strerror(errno));
328 if(WIFSIGNALED(child_status)) {
329 dbprintf(("%s: child %ld terminated with signal %d\n",
330 debug_prefix_time(NULL),
331 (long) child_pid, WTERMSIG(child_status)));
333 exit_code = WEXITSTATUS(child_status);
335 dbprintf(("%s: child %ld terminated normally\n",
336 debug_prefix_time(NULL), (long) child_pid));
338 dbprintf(("%s: child %ld terminated with code %d\n",
339 debug_prefix_time(NULL),
340 (long) child_pid, exit_code));
344 * Find the child and mark it done.
346 for(est = est_list; est != NULL; est = est->next) {
347 if(est->child == child_pid) {
352 dbprintf(("%s: unexpected child %ld\n",
353 debug_prefix_time(NULL), (long)child_pid));
361 * If we are already running the maximum number of children
362 * go back and wait until one of them finishes.
364 if(dumpsrunning >= g_options->maxdumps) {
367 continue; /* have to wait first */
370 * Find a new child to start.
372 for(est = est_list; est != NULL; est = est->next) {
374 done = 0; /* more to do */
376 if(est->child != 0 || est->done) {
377 continue; /* child is running or done */
380 * Make sure there is no spindle conflict.
382 if(est->spindle != -1) {
383 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
384 if(est1->child == 0 || est == est1 || est1->done) {
386 * Ignore anything not yet started, ourself,
387 * and anything completed.
391 if(est1->spindle == est->spindle) {
392 break; /* oops -- they match */
396 continue; /* spindle conflict */
399 break; /* start this estimate */
402 if(dumpsrunning > 0) {
403 need_wait = 1; /* nothing to do but wait */
407 if((est->child = fork()) == 0) {
408 set_debug_prefix_pid(getpid());
409 calc_estimates(est); /* child does the estimate */
411 } else if(est->child == -1) {
412 error("calc_estimates fork failed: %s", strerror(errno));
414 dumpsrunning++; /* parent */
419 for(est = est_list; est != NULL; est = est->next) {
425 amfree(our_feature_string);
426 am_release_feature_set(our_features);
428 am_release_feature_set(g_options->features);
429 g_options->features = NULL;
430 amfree(g_options->str);
431 amfree(g_options->hostname);
434 malloc_size_2 = malloc_inuse(&malloc_hist_2);
436 if(malloc_size_1 != malloc_size_2) {
437 #if defined(USE_DBMALLOC)
438 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
445 printf("FORMAT ERROR IN REQUEST PACKET\n");
446 dbprintf(("%s: REQ packet is bogus%s%s\n",
447 debug_prefix_time(NULL),
448 err_extra ? ": " : "",
449 err_extra ? err_extra : ""));
455 void add_diskest(disk, amdevice, level, spindle, prog, calcprog, options)
456 char *disk, *amdevice, *prog, *calcprog;
460 disk_estimates_t *newp, *curp;
465 for(curp = est_list; curp != NULL; curp = curp->next) {
466 if(strcmp(curp->amname, disk) == 0) {
467 /* already have disk info, just note the level request */
468 curp->est[level].needestimate = 1;
470 free_sl(options->exclude_file);
471 free_sl(options->exclude_list);
472 free_sl(options->include_file);
473 free_sl(options->include_list);
474 amfree(options->str);
481 newp = (disk_estimates_t *) alloc(sizeof(disk_estimates_t));
482 memset(newp, 0, sizeof(*newp));
483 newp->next = est_list;
485 newp->amname = stralloc(disk);
486 newp->amdevice = stralloc(amdevice);
487 newp->dirname = amname_to_dirname(newp->amdevice);
488 newp->program = stralloc(prog);
490 newp->calcprog = stralloc(calcprog);
492 newp->calcprog = NULL;
493 newp->spindle = spindle;
494 newp->est[level].needestimate = 1;
495 newp->options = options;
497 /* fill in dump-since dates */
499 amdp = amandates_lookup(newp->amname);
501 newp->est[0].dumpsince = EPOCH;
502 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
503 dumpdate = amdp->dates[dumplev];
504 for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
505 if(dumpdate > newp->est[estlev].dumpsince)
506 newp->est[estlev].dumpsince = dumpdate;
512 void free_estimates(est)
513 disk_estimates_t *est;
516 amfree(est->amdevice);
517 amfree(est->dirname);
518 amfree(est->program);
520 free_sl(est->options->exclude_file);
521 free_sl(est->options->exclude_list);
522 free_sl(est->options->include_file);
523 free_sl(est->options->include_list);
524 amfree(est->options->str);
525 amfree(est->options);
530 * ------------------------------------------------------------------------
534 void calc_estimates(est)
535 disk_estimates_t *est;
537 dbprintf(("%s: calculating for amname '%s', dirname '%s', spindle %d\n",
538 debug_prefix_time(NULL),
539 est->amname, est->dirname, est->spindle));
541 #ifndef USE_GENERIC_CALCSIZE
542 if(strcmp(est->program, "DUMP") == 0)
543 dump_calc_estimates(est);
547 if (strcmp(est->program, "GNUTAR") == 0 &&
548 est->amdevice[0] == '/' && est->amdevice[1] == '/')
549 smbtar_calc_estimates(est);
553 if (strcmp(est->program, "GNUTAR") == 0)
554 gnutar_calc_estimates(est);
558 if (est->amdevice[0] == '/' && est->amdevice[1] == '/')
559 dbprintf(("%s: Can't use CALCSIZE for samba estimate: %s %s\n",
560 est->amname, est->dirname));
563 generic_calc_estimates(est);
565 dbprintf(("%s: done with amname '%s', dirname '%s', spindle %d\n",
566 debug_prefix_time(NULL),
567 est->amname, est->dirname, est->spindle));
570 void generic_calc_estimates(est)
571 disk_estimates_t *est;
573 int pipefd = -1, nullfd = -1;
575 char *my_argv[DUMP_LEVELS*2+20];
576 char number[NUM_STR_SIZE];
577 int i, level, my_argc, calcpid;
580 char *file_exclude = NULL;
581 char *file_include = NULL;
583 FILE *dumpout = NULL;
588 cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
591 my_argv[my_argc++] = stralloc("calcsize");
592 my_argv[my_argc++] = stralloc(est->calcprog);
594 my_argv[my_argc++] = stralloc(est->amname);
595 my_argv[my_argc++] = stralloc(est->dirname);
598 if(est->options->exclude_file)
599 nb_exclude += est->options->exclude_file->nb_element;
600 if(est->options->exclude_list)
601 nb_exclude += est->options->exclude_list->nb_element;
602 if(est->options->include_file)
603 nb_include += est->options->include_file->nb_element;
604 if(est->options->include_list)
605 nb_include += est->options->include_list->nb_element;
608 file_exclude = build_exclude(est->amname, est->amdevice,est->options,0);
610 file_include = build_include(est->amname, est->amdevice,est->options,0);
613 my_argv[my_argc++] = stralloc("-X");
614 my_argv[my_argc++] = file_exclude;
618 my_argv[my_argc++] = stralloc("-I");
619 my_argv[my_argc++] = file_include;
622 start_time = curclock();
624 dbprintf(("%s: running cmd: %s", debug_prefix_time(NULL), my_argv[0]));
625 for(i=0; i<my_argc; ++i)
626 dbprintf((" %s", my_argv[i]));
628 for(level = 0; level < DUMP_LEVELS; level++) {
629 if(est->est[level].needestimate) {
630 ap_snprintf(number, sizeof(number), "%d", level);
631 my_argv[my_argc++] = stralloc(number);
632 dbprintf((" %s", number));
633 ap_snprintf(number, sizeof(number),
634 "%ld", (long)est->est[level].dumpsince);
635 my_argv[my_argc++] = stralloc(number);
636 dbprintf((" %s", number));
639 my_argv[my_argc] = NULL;
642 fflush(stderr); fflush(stdout);
644 nullfd = open("/dev/null", O_RDWR);
645 calcpid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
648 dumpout = fdopen(pipefd,"r");
649 match_expr = vstralloc(est->amname," %d SIZE %ld", NULL);
650 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
651 if(sscanf(line, match_expr, &level, &size) == 2) {
652 printf("%s\n", line); /* write to amandad */
653 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
662 dbprintf(("%s: waiting for %s \"%s\" child\n",
663 debug_prefix_time(NULL), my_argv[0], est->amdevice));
665 dbprintf(("%s: after %s \"%s\" wait\n",
666 debug_prefix_time(NULL), my_argv[0], est->amdevice));
668 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
669 dbprintf(("%s: estimate time for %s: %s\n",
672 walltime_str(timessub(curclock(), start_time))));
674 for(i = 0; i < my_argc; i++) {
682 * ------------------------------------------------------------------------
686 /* local functions */
687 void dump_calc_estimates P((disk_estimates_t *est));
688 long getsize_dump P((char *disk, char *amdevice, int level, option_t *options));
689 long getsize_smbtar P((char *disk, char *amdevice, int level, option_t *options));
690 long getsize_gnutar P((char *disk, char *amdevice, int level,
691 option_t *options, time_t dumpsince));
692 long handle_dumpline P((char *str));
693 double first_num P((char *str));
695 void dump_calc_estimates(est)
696 disk_estimates_t *est;
701 for(level = 0; level < DUMP_LEVELS; level++) {
702 if(est->est[level].needestimate) {
703 dbprintf(("%s: getting size via dump for %s level %d\n",
704 debug_prefix_time(NULL), est->amname, level));
705 size = getsize_dump(est->amname, est->amdevice,level, est->options);
709 fseek(stdout, (off_t)0, SEEK_SET);
711 printf("%s %d SIZE %ld\n", est->amname, level, size);
714 amfunlock(1, "size");
720 void smbtar_calc_estimates(est)
721 disk_estimates_t *est;
726 for(level = 0; level < DUMP_LEVELS; level++) {
727 if(est->est[level].needestimate) {
728 dbprintf(("%s: getting size via smbclient for %s level %d\n",
729 debug_prefix_time(NULL), est->amname, level));
730 size = getsize_smbtar(est->amname, est->amdevice, level, est->options);
734 fseek(stdout, (off_t)0, SEEK_SET);
736 printf("%s %d SIZE %ld\n", est->amname, level, size);
739 amfunlock(1, "size");
746 void gnutar_calc_estimates(est)
747 disk_estimates_t *est;
752 for(level = 0; level < DUMP_LEVELS; level++) {
753 if (est->est[level].needestimate) {
754 dbprintf(("%s: getting size via gnutar for %s level %d\n",
755 debug_prefix_time(NULL), est->amname, level));
756 size = getsize_gnutar(est->amname, est->amdevice, level,
757 est->options, est->est[level].dumpsince);
761 fseek(stdout, (off_t)0, SEEK_SET);
763 printf("%s %d SIZE %ld\n", est->amname, level, size);
766 amfunlock(1, "size");
772 typedef struct regex_s {
777 regex_t re_size[] = {
779 {" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
780 {" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
781 {" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
782 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
783 {"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
784 {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
785 {"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
786 {"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
787 {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
788 {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
789 {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
791 {"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
792 {"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
793 {"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
794 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
796 #ifdef HAVE_DUMP_ESTIMATE
797 {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
798 /* DU 3.2g dump -E */
799 {"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
800 {"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
805 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
809 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
810 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
814 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
817 #ifdef USE_QUICK_AND_DIRTY_ESTIMATES
818 {"amqde estimate: [0-9][0-9]* kb", 1024}, /* amqde */
822 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
826 #if SAMBA_VERSION >= 2
827 #define SAMBA_DEBUG_LEVEL "0"
828 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
830 #define SAMBA_DEBUG_LEVEL "3"
831 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
839 long getsize_dump(disk, amdevice, level, options)
840 char *disk, *amdevice;
844 int pipefd[2], nullfd, stdoutfd, killctl[2];
848 char *dumpkeys = NULL;
854 char *rundump_cmd = NULL;
855 char level_str[NUM_STR_SIZE];
859 ap_snprintf(level_str, sizeof(level_str), "%d", level);
861 device = amname_to_devname(amdevice);
862 fstype = amname_to_fstype(amdevice);
864 dbprintf(("%s: calculating for device '%s' with '%s'\n",
865 debug_prefix_time(NULL), device, fstype));
867 cmd = vstralloc(libexecdir, "/rundump", versionsuffix(), NULL);
868 rundump_cmd = stralloc(cmd);
870 stdoutfd = nullfd = open("/dev/null", O_RDWR);
871 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
874 #ifdef XFSDUMP /* { */
876 if (strcmp(fstype, "xfs") == 0)
881 name = stralloc(" (xfsdump)");
882 dbprintf(("%s: running \"%s%s -F -J -l %s - %s\"\n",
883 debug_prefix_time(NULL), cmd, name, level_str, device));
887 #ifdef VXDUMP /* { */
889 if (strcmp(fstype, "vxfs") == 0)
895 name = stralloc(" (vxdump)");
898 cmd = newstralloc(cmd, VXDUMP);
900 dumpkeys = vstralloc(level_str, "s", "f", NULL);
901 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
902 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
908 if (strcmp(fstype, "advfs") == 0)
913 name = stralloc(" (vdump)");
915 device = amname_to_dirname(amdevice);
916 dumpkeys = vstralloc(level_str, "b", "f", NULL);
917 dbprintf(("%s: running \"%s%s %s 60 - %s\"\n",
918 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
924 # ifdef USE_RUNDUMP /* { */
925 # ifdef AIX_BACKUP /* { */
926 name = stralloc(" (backup)");
928 name = vstralloc(" (", DUMP, ")", NULL);
932 cmd = newstralloc(cmd, DUMP);
935 # ifdef AIX_BACKUP /* { */
936 dumpkeys = vstralloc("-", level_str, "f", NULL);
937 dbprintf(("%s: running \"%s%s %s - %s\"\n",
938 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
940 dumpkeys = vstralloc(level_str,
941 # ifdef HAVE_DUMP_ESTIMATE /* { */
944 # ifdef HAVE_HONOR_NODUMP /* { */
949 # ifdef HAVE_DUMP_ESTIMATE
950 stdoutfd = pipefd[1];
953 # ifdef HAVE_HONOR_NODUMP /* { */
954 dbprintf(("%s: running \"%s%s %s 0 1048576 - %s\"\n",
955 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
957 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
958 debug_prefix_time(NULL), cmd, name, dumpkeys, device));
965 error("no dump program available");
970 start_time = curclock();
971 switch(dumppid = fork()) {
973 dbprintf(("%s: cannot fork for killpgrp: %s\n",
974 debug_prefix(NULL), strerror(errno)));
983 case 0: /* child process */
986 else if (killctl[0] == -1 || killctl[1] == -1)
987 dbprintf(("%s: pipe for killpgrp failed, trying without killpgrp\n",
988 debug_prefix(NULL)));
992 dbprintf(("%s: fork failed, trying without killpgrp\n",
993 debug_prefix(NULL)));
998 char *killpgrp_cmd = vstralloc(libexecdir, "/killpgrp",
999 versionsuffix(), NULL);
1000 dbprintf(("%s: running %s\n",
1001 debug_prefix_time(NULL), killpgrp_cmd));
1002 dup2(killctl[0], 0);
1009 execle(killpgrp_cmd, killpgrp_cmd, (char *)0, safe_env());
1010 dbprintf(("%s: cannot execute %s: %s\n",
1011 debug_prefix(NULL), killpgrp_cmd, strerror(errno)));
1015 case 0: /* child process */
1024 if (killctl[0] != -1)
1026 if (killctl[1] != -1)
1031 if (strcmp(fstype, "xfs") == 0)
1035 execle(cmd, "xfsdump", "-F", "-J", "-l", level_str, "-", device,
1036 (char *)0, safe_env());
1041 if (strcmp(fstype, "vxfs") == 0)
1045 execle(cmd, "vxdump", dumpkeys, "1048576", "-", device, (char *)0,
1051 if (strcmp(fstype, "advfs") == 0)
1055 execle(cmd, "vdump", dumpkeys, "60", "-", device, (char *)0,
1061 execle(cmd, "backup", dumpkeys, "-", device, (char *)0, safe_env());
1063 execle(cmd, "dump", dumpkeys,
1064 #ifdef HAVE_HONOR_NODUMP
1067 "1048576", "-", device, (char *)0, safe_env());
1071 error("exec %s failed or no dump program available: %s",
1072 cmd, strerror(errno));
1077 amfree(rundump_cmd);
1080 if (killctl[0] != -1)
1082 dumpout = fdopen(pipefd[0],"r");
1084 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
1085 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1086 size = handle_dumpline(line);
1089 if((line = agets(dumpout)) != NULL) {
1090 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1097 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1098 dbprintf(("%s: estimate time for %s level %d: %s\n",
1102 walltime_str(timessub(curclock(), start_time))));
1104 dbprintf(("%s: no size line match in %s%s output for \"%s\"\n",
1105 debug_prefix(NULL), cmd, name, disk));
1106 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1107 } else if(size == 0 && level == 0) {
1108 dbprintf(("%s: possible %s%s problem -- is \"%s\" really empty?\n",
1109 debug_prefix(NULL), cmd, name, disk));
1110 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1112 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1118 if (killctl[1] != -1) {
1119 dbprintf(("%s: asking killpgrp to terminate\n",
1120 debug_prefix_time(NULL)));
1122 for(s = 5; s > 0; --s) {
1124 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1130 * First, try to kill the dump process nicely. If it ignores us
1131 * for several seconds, hit it harder.
1133 dbprintf(("%s: sending SIGTERM to process group %ld\n",
1134 debug_prefix_time(NULL), (long)dumppid));
1135 if (kill(-dumppid, SIGTERM) == -1) {
1136 dbprintf(("%s: kill failed: %s\n",
1137 debug_prefix(NULL), strerror(errno)));
1139 /* Now check whether it dies */
1140 for(s = 5; s > 0; --s) {
1142 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1146 dbprintf(("%s: sending SIGKILL to process group %ld\n",
1147 debug_prefix_time(NULL), (long)dumppid));
1148 if (kill(-dumppid, SIGKILL) == -1) {
1149 dbprintf(("%s: kill failed: %s\n",
1150 debug_prefix(NULL), strerror(errno)));
1152 for(s = 5; s > 0; --s) {
1154 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1158 dbprintf(("%s: waiting for %s%s \"%s\" child\n",
1159 debug_prefix_time(NULL), cmd, name, disk));
1161 dbprintf(("%s: after %s%s \"%s\" wait\n",
1162 debug_prefix_time(NULL), cmd, name, disk));
1179 long getsize_smbtar(disk, amdevice, level, optionns)
1180 char *disk, *amdevice;
1184 int pipefd = -1, nullfd = -1, passwdfd = -1;
1188 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1189 char *share = NULL, *subdir = NULL;
1196 char *error_pn = NULL;
1198 error_pn = stralloc2(get_pname(), "-smbclient");
1200 parsesharename(amdevice, &share, &subdir);
1204 set_pname(error_pn);
1206 error("cannot parse disk entry '%s' for share/subdir", disk);
1208 if ((subdir) && (SAMBA_VERSION < 2)) {
1211 set_pname(error_pn);
1213 error("subdirectory specified for share '%s' but samba not v2 or better", disk);
1215 if ((user_and_password = findpass(share, &domain)) == NULL) {
1218 memset(domain, '\0', strlen(domain));
1221 set_pname(error_pn);
1223 error("cannot find password for %s", disk);
1225 lpass = strlen(user_and_password);
1226 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1227 memset(user_and_password, '\0', lpass);
1228 amfree(user_and_password);
1230 memset(domain, '\0', strlen(domain));
1233 set_pname(error_pn);
1235 error("password field not \'user%%pass\' for %s", disk);
1238 pwtext_len = strlen(pwtext);
1239 if ((sharename = makesharename(share, 0)) == NULL) {
1240 memset(user_and_password, '\0', lpass);
1241 amfree(user_and_password);
1243 memset(domain, '\0', strlen(domain));
1246 set_pname(error_pn);
1248 error("cannot make share name of %s", share);
1250 nullfd = open("/dev/null", O_RDWR);
1252 #if SAMBA_VERSION >= 2
1254 tarkeys = "archive 0;recurse;du";
1256 tarkeys = "archive 1;recurse;du";
1259 tarkeys = "archive 0;recurse;dir";
1261 tarkeys = "archive 1;recurse;dir";
1264 start_time = curclock();
1266 if (pwtext_len > 0) {
1267 pw_fd_env = "PASSWD_FD";
1269 pw_fd_env = "dummy_PASSWD_FD";
1271 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
1272 &nullfd, &nullfd, &pipefd,
1273 pw_fd_env, &passwdfd,
1276 "-d", SAMBA_DEBUG_LEVEL,
1277 *user_and_password ? "-U" : skip_argument,
1278 *user_and_password ? user_and_password : skip_argument,
1280 domain ? "-W" : skip_argument,
1281 domain ? domain : skip_argument,
1282 #if SAMBA_VERSION >= 2
1283 subdir ? "-D" : skip_argument,
1284 subdir ? subdir : skip_argument,
1289 memset(domain, '\0', strlen(domain));
1293 if(pwtext_len > 0 && fullwrite(passwdfd, pwtext, pwtext_len) < 0) {
1294 int save_errno = errno;
1296 memset(user_and_password, '\0', lpass);
1297 amfree(user_and_password);
1299 set_pname(error_pn);
1301 error("password write failed: %s", strerror(save_errno));
1303 memset(user_and_password, '\0', lpass);
1304 amfree(user_and_password);
1310 dumpout = fdopen(pipefd,"r");
1312 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
1313 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1314 size = handle_dumpline(line);
1317 if((line = agets(dumpout)) != NULL) {
1318 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1325 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1326 dbprintf(("%s: estimate time for %s level %d: %s\n",
1330 walltime_str(timessub(curclock(), start_time))));
1332 dbprintf(("%s: no size line match in %s output for \"%s\"\n",
1333 debug_prefix(NULL), SAMBA_CLIENT, disk));
1334 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1335 } else if(size == 0 && level == 0) {
1336 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
1337 debug_prefix(NULL), SAMBA_CLIENT, disk));
1338 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1340 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1346 kill(-dumppid, SIGTERM);
1348 dbprintf(("%s: waiting for %s \"%s\" child\n",
1349 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1351 dbprintf(("%s: after %s \"%s\" wait\n",
1352 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1364 long getsize_gnutar(disk, amdevice, level, options, dumpsince)
1365 char *disk, *amdevice;
1370 int pipefd = -1, nullfd = -1, dumppid;
1372 FILE *dumpout = NULL;
1373 char *incrname = NULL;
1374 char *basename = NULL;
1375 char *dirname = NULL;
1376 char *inputname = NULL;
1381 char dumptimestr[80];
1387 char *file_exclude = NULL;
1388 char *file_include = NULL;
1391 if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
1392 if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
1393 if(options->include_file) nb_include += options->include_file->nb_element;
1394 if(options->include_list) nb_include += options->include_list->nb_element;
1396 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
1397 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
1399 my_argv = alloc(sizeof(char *) * 21);
1402 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
1404 char number[NUM_STR_SIZE];
1409 basename = vstralloc(GNUTAR_LISTED_INCREMENTAL_DIR,
1411 g_options->hostname,
1415 * The loop starts at the first character of the host name,
1418 s = basename + sizeof(GNUTAR_LISTED_INCREMENTAL_DIR);
1419 while((ch = *s++) != '\0') {
1420 if(ch == '/' || isspace(ch)) s[-1] = '_';
1423 ap_snprintf(number, sizeof(number), "%d", level);
1424 incrname = vstralloc(basename, "_", number, ".new", NULL);
1428 * Open the listed incremental file from the previous level. Search
1429 * backward until one is found. If none are found (which will also
1430 * be true for a level 0), arrange to read from /dev/null.
1433 while (in == NULL) {
1434 if (--baselevel >= 0) {
1435 ap_snprintf(number, sizeof(number), "%d", baselevel);
1436 inputname = newvstralloc(inputname,
1437 basename, "_", number, NULL);
1439 inputname = newstralloc(inputname, "/dev/null");
1441 if ((in = fopen(inputname, "r")) == NULL) {
1442 int save_errno = errno;
1444 dbprintf(("%s: gnutar: error opening %s: %s\n",
1445 debug_prefix(NULL), inputname, strerror(save_errno)));
1446 if (baselevel < 0) {
1453 * Copy the previous listed incremental file to the new one.
1455 if ((out = fopen(incrname, "w")) == NULL) {
1456 dbprintf(("%s: opening %s: %s\n",
1457 debug_prefix(NULL), incrname, strerror(errno)));
1461 for (; (line = agets(in)) != NULL; free(line)) {
1462 if (fputs(line, out) == EOF || putc('\n', out) == EOF) {
1463 dbprintf(("%s: writing to %s: %s\n",
1464 debug_prefix(NULL), incrname, strerror(errno)));
1471 dbprintf(("%s: reading from %s: %s\n",
1472 debug_prefix(NULL), inputname, strerror(errno)));
1475 if (fclose(in) == EOF) {
1476 dbprintf(("%s: closing %s: %s\n",
1477 debug_prefix(NULL), inputname, strerror(errno)));
1482 if (fclose(out) == EOF) {
1483 dbprintf(("%s: closing %s: %s\n",
1484 debug_prefix(NULL), incrname, strerror(errno)));
1495 gmtm = gmtime(&dumpsince);
1496 ap_snprintf(dumptimestr, sizeof(dumptimestr),
1497 "%04d-%02d-%02d %2d:%02d:%02d GMT",
1498 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
1499 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
1501 dirname = amname_to_dirname(amdevice);
1505 #ifdef USE_QUICK_AND_DIRTY_ESTIMATES
1506 ap_snprintf(dumptimestr, sizeof(dumptimestr), "%ld", dumpsince);
1508 cmd = vstralloc(libexecdir, "/", "amqde", versionsuffix(), NULL);
1510 my_argv[i++] = vstralloc(libexecdir, "/", "amqde", versionsuffix(), NULL);
1511 my_argv[i++] = "-s";
1512 my_argv[i++] = dumptimestr;
1513 if(file_exclude) { /* at present, this is not used... */
1514 my_argv[i++] = "-x";
1515 my_argv[i++] = file_exclude;
1517 /* [XXX] need to also consider implementation of --files-from */
1518 my_argv[i++] = dirname;
1519 my_argv[i++] = NULL;
1522 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
1524 my_argv[i++] = GNUTAR;
1526 my_argv[i++] = "tar";
1528 my_argv[i++] = "--create";
1529 my_argv[i++] = "--file";
1530 my_argv[i++] = "/dev/null";
1531 my_argv[i++] = "--directory";
1532 my_argv[i++] = dirname;
1533 my_argv[i++] = "--one-file-system";
1534 my_argv[i++] = "--numeric-owner";
1535 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
1536 my_argv[i++] = "--listed-incremental";
1537 my_argv[i++] = incrname;
1539 my_argv[i++] = "--incremental";
1540 my_argv[i++] = "--newer";
1541 my_argv[i++] = dumptimestr;
1543 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
1544 /* --atime-preserve causes gnutar to call
1545 * utime() after reading files in order to
1546 * adjust their atime. However, utime()
1547 * updates the file's ctime, so incremental
1548 * dumps will think the file has changed. */
1549 my_argv[i++] = "--atime-preserve";
1551 my_argv[i++] = "--sparse";
1552 my_argv[i++] = "--ignore-failed-read";
1553 my_argv[i++] = "--totals";
1556 my_argv[i++] = "--exclude-from";
1557 my_argv[i++] = file_exclude;
1561 my_argv[i++] = "--files-from";
1562 my_argv[i++] = file_include;
1567 #endif /* USE_QUICK_AND_DIRTY_ESTIMATES */
1568 my_argv[i++] = NULL;
1570 start_time = curclock();
1572 nullfd = open("/dev/null", O_RDWR);
1573 dumppid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
1575 amfree(file_exclude);
1576 amfree(file_include);
1578 dumpout = fdopen(pipefd,"r");
1580 for(size = -1; (line = agets(dumpout)) != NULL; free(line)) {
1581 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1582 size = handle_dumpline(line);
1585 if((line = agets(dumpout)) != NULL) {
1586 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1593 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1594 dbprintf(("%s: estimate time for %s level %d: %s\n",
1598 walltime_str(timessub(curclock(), start_time))));
1600 dbprintf(("%s: no size line match in %s output for \"%s\"\n",
1601 debug_prefix(NULL), my_argv[0], disk));
1602 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1603 } else if(size == 0 && level == 0) {
1604 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
1605 debug_prefix(NULL), my_argv[0], disk));
1606 dbprintf(("%s: .....\n", debug_prefix(NULL)));
1608 dbprintf(("%s: estimate size for %s level %d: %ld KB\n",
1614 kill(-dumppid, SIGTERM);
1616 dbprintf(("%s: waiting for %s \"%s\" child\n",
1617 debug_prefix_time(NULL), my_argv[0], disk));
1619 dbprintf(("%s: after %s \"%s\" wait\n",
1620 debug_prefix_time(NULL), my_argv[0], disk));
1643 double first_num(str)
1646 * Returns the value of the first integer in a string.
1654 while(ch && !isdigit(ch)) ch = *str++;
1656 while(isdigit(ch) || (ch == '.')) ch = *str++;
1664 long handle_dumpline(str)
1667 * Checks the dump output line against the error and size regex tables.
1673 /* check for size match */
1674 for(rp = re_size; rp->regex != NULL; rp++) {
1675 if(match(rp->regex, str)) {
1676 size = ((first_num(str)*rp->scale+1023.0)/1024.0);
1677 if(size < 0) size = 1; /* found on NeXT -- sigh */