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.171 2006/08/24 01:57:15 paddy_s 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"
48 #define sendsize_debug(i,x) do { \
49 if ((i) <= debug_sebdsize) { \
55 # define SETPGRP setpgid(getpid(), getpid())
56 # define SETPGRP_FAILED() do { \
57 dbprintf(("setpgid(%ld,%ld) failed: %s\n", \
58 (long)getpid(), (long)getpid(), strerror(errno))); \
62 #if defined(SETPGRP_VOID)
63 # define SETPGRP setpgrp()
64 # define SETPGRP_FAILED() do { \
65 dbprintf(("setpgrp() failed: %s\n", strerror(errno))); \
69 # define SETPGRP setpgrp(0, getpid())
70 # define SETPGRP_FAILED() do { \
71 dbprintf(("setpgrp(0,%ld) failed: %s\n", \
72 (long)getpid(), strerror(errno))); \
78 typedef struct level_estimates_s {
84 typedef struct disk_estimates_s {
85 struct disk_estimates_s *next;
94 int program_is_backup_api;
99 level_estimate_t est[DUMP_LEVELS];
102 disk_estimates_t *est_list;
104 static am_feature_t *our_features = NULL;
105 static char *our_feature_string = NULL;
106 static g_option_t *g_options = NULL;
108 /* local functions */
109 int main(int argc, char **argv);
110 void add_diskest(char *disk, char *amdevice, int level, int spindle,
111 int program_is_backup_api, char *prog, char *calcprog,
113 void calc_estimates(disk_estimates_t *est);
114 void free_estimates(disk_estimates_t *est);
115 void dump_calc_estimates(disk_estimates_t *);
116 void star_calc_estimates(disk_estimates_t *);
117 void smbtar_calc_estimates(disk_estimates_t *);
118 void gnutar_calc_estimates(disk_estimates_t *);
119 void backup_api_calc_estimate(disk_estimates_t *);
120 void generic_calc_estimates(disk_estimates_t *);
129 char *prog, *calcprog, *dumpdate;
130 option_t *options = NULL;
131 int program_is_backup_api;
132 disk_estimates_t *est;
133 disk_estimates_t *est1;
134 disk_estimates_t *est_prev;
138 char *err_extra = NULL;
145 char *amdevice = NULL;
146 char *qamdevice = NULL;
148 char *amandates_file;
149 int amandates_read = 0;
150 #if defined(USE_DBMALLOC)
151 unsigned long malloc_hist_1, malloc_size_1;
152 unsigned long malloc_hist_2, malloc_size_2;
155 (void)argc; /* Quiet unused parameter warning */
156 (void)argv; /* Quiet unused parameter warning */
163 set_pname("sendsize");
165 /* Don't die when child closes pipe */
166 signal(SIGPIPE, SIG_IGN);
168 #if defined(USE_DBMALLOC)
169 malloc_size_1 = malloc_inuse(&malloc_hist_1);
172 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
173 dbopen(DBG_SUBDIR_CLIENT);
175 dbprintf(("%s: version %s\n", get_pname(), version()));
177 our_features = am_init_feature_set();
178 our_feature_string = am_feature_to_string(our_features);
180 set_debug_prefix_pid(getpid());
182 conffile = vstralloc(CONFIG_DIR, "/", "amanda-client.conf", NULL);
183 if (read_clientconf(conffile) > 0) {
184 error("error reading conffile: %s", conffile);
189 /* handle all service requests */
191 for(; (line = agets(stdin)) != NULL; free(line)) {
194 if(strncmp_const(line, "OPTIONS ") == 0) {
195 g_options = parse_g_options(line+8, 1);
196 if(!g_options->hostname) {
197 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
198 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
199 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
203 if(am_has_feature(g_options->features, fe_rep_options_features)) {
204 printf("features=%s;", our_feature_string);
206 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
207 printf("maxdumps=%d;", g_options->maxdumps);
209 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
210 printf("hostname=%s;", g_options->hostname);
215 if (g_options->config) {
216 conffile = vstralloc(CONFIG_DIR, "/", g_options->config, "/",
217 "amanda-client.conf", NULL);
218 if (read_clientconf(conffile) > 0) {
219 error("error reading conffile: %s", conffile);
224 dbrename(g_options->config, DBG_SUBDIR_CLIENT);
230 if (amandates_read == 0) {
231 amandates_file = getconf_str(CNF_AMANDATES);
232 if(!start_amandates(amandates_file, 0))
233 error("error [opening %s: %s]", amandates_file,
241 skip_whitespace(s, ch); /* find the program name */
243 err_extra = stralloc("no program name");
244 goto err; /* no program name */
247 skip_non_whitespace(s, ch);
250 program_is_backup_api=0;
251 if(strncmp_const(prog, "CALCSIZE") == 0) {
252 skip_whitespace(s, ch); /* find the program name */
254 err_extra = stralloc("no program name");
258 skip_non_whitespace(s, ch);
260 if (strcmp(calcprog,"BACKUP") == 0) {
261 program_is_backup_api=1;
262 skip_whitespace(s, ch); /* find dumper name */
264 goto err; /* no program */
267 skip_non_whitespace(s, ch);
273 if (strcmp(prog,"BACKUP") == 0) {
274 program_is_backup_api=1;
275 skip_whitespace(s, ch); /* find dumper name */
277 goto err; /* no program */
280 skip_non_whitespace(s, ch);
285 skip_whitespace(s, ch); /* find the disk name */
287 err_extra = stralloc("no disk name");
288 goto err; /* no disk name */
297 skip_quoted_string(s, ch);
298 s[-1] = '\0'; /* terminate the disk name */
299 qdisk = stralloc(fp);
300 disk = unquote_string(qdisk);
302 skip_whitespace(s, ch); /* find the device or level */
304 err_extra = stralloc("bad level");
307 if(!isdigit((int)s[-1])) {
309 skip_quoted_string(s, ch);
311 qamdevice = stralloc(fp);
312 amdevice = unquote_string(qamdevice);
313 skip_whitespace(s, ch); /* find level number */
316 amdevice = stralloc(disk);
317 qamdevice = stralloc(qdisk);
320 /* find the level number */
321 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
322 err_extra = stralloc("bad level");
323 goto err; /* bad level */
325 if (level < 0 || level >= DUMP_LEVELS) {
326 err_extra = stralloc("bad level");
331 skip_whitespace(s, ch); /* find the dump date */
333 err_extra = stralloc("no dumpdate");
334 goto err; /* no dumpdate */
337 skip_non_whitespace(s, ch);
339 (void)dumpdate; /* XXX: Set but not used */
341 spindle = 0; /* default spindle */
343 skip_whitespace(s, ch); /* find the spindle */
345 if(sscanf(s - 1, "%d", &spindle) != 1) {
346 err_extra = stralloc("bad spindle");
347 goto err; /* bad spindle */
351 skip_whitespace(s, ch); /* find the parameters */
353 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
354 options = parse_options(s + 8,
361 options = alloc(SIZEOF(option_t));
362 init_options(options);
364 if(strncmp_const(s-1, "exclude-file=") == 0) {
365 qlist = unquote_string(s+12);
366 options->exclude_file =
367 append_sl(options->exclude_file, qlist);
369 } else if(strncmp_const(s-1, "exclude-list=") == 0) {
370 qlist = unquote_string(s+12);
371 options->exclude_list =
372 append_sl(options->exclude_list, qlist);
374 } else if(strncmp_const(s-1, "include-file=") == 0) {
375 qlist = unquote_string(s+12);
376 options->include_file =
377 append_sl(options->include_file, qlist);
379 } else if(strncmp_const(s-1, "include-list=") == 0) {
380 qlist = unquote_string(s+12);
381 options->include_list =
382 append_sl(options->include_list, qlist);
385 err_extra = vstralloc("Invalid parameter (",
387 goto err; /* should have gotten to end */
389 skip_quoted_string(s, ch);
390 skip_whitespace(s, ch); /* find the inclusion list */
396 options = alloc(SIZEOF(option_t));
397 init_options(options);
401 options = alloc(SIZEOF(option_t));
402 init_options(options);
406 add_diskest(disk, amdevice, level, spindle, program_is_backup_api, prog, calcprog, options);
413 if (g_options == NULL) {
414 error("Missing OPTIONS line in sendsize input\n");
428 * See if we need to wait for a child before we can do anything
433 amwait_t child_status;
437 dbprintf(("%s: waiting for any estimate child: %d running\n",
438 debug_prefix_time(NULL), dumpsrunning));
439 child_pid = wait(&child_status);
440 if(child_pid == -1) {
441 error("wait failed: %s", strerror(errno));
444 if(WIFSIGNALED(child_status)) {
445 dbprintf(("%s: child %ld terminated with signal %d\n",
446 debug_prefix_time(NULL),
447 (long) child_pid, WTERMSIG(child_status)));
449 exit_code = WEXITSTATUS(child_status);
451 dbprintf(("%s: child %ld terminated normally\n",
452 debug_prefix_time(NULL), (long) child_pid));
454 dbprintf(("%s: child %ld terminated with code %d\n",
455 debug_prefix_time(NULL),
456 (long) child_pid, exit_code));
460 * Find the child and mark it done.
462 for(est = est_list; est != NULL; est = est->next) {
463 if(est->child == child_pid) {
468 dbprintf(("%s: unexpected child %ld\n",
469 debug_prefix_time(NULL), (long)child_pid));
477 * If we are already running the maximum number of children
478 * go back and wait until one of them finishes.
480 if(dumpsrunning >= g_options->maxdumps) {
483 continue; /* have to wait first */
486 * Find a new child to start.
488 for(est = est_list; est != NULL; est = est->next) {
490 done = 0; /* more to do */
492 if(est->child != 0 || est->done) {
493 continue; /* child is running or done */
496 * Make sure there is no spindle conflict.
498 if(est->spindle != -1) {
499 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
500 if(est1->child == 0 || est == est1 || est1->done) {
502 * Ignore anything not yet started, ourself,
503 * and anything completed.
507 if(est1->spindle == est->spindle) {
508 break; /* oops -- they match */
512 continue; /* spindle conflict */
515 break; /* start this estimate */
518 if(dumpsrunning > 0) {
519 need_wait = 1; /* nothing to do but wait */
523 if((est->child = fork()) == 0) {
524 set_debug_prefix_pid(getpid());
525 calc_estimates(est); /* child does the estimate */
527 } else if(est->child == -1) {
528 error("calc_estimates fork failed: %s", strerror(errno));
531 dumpsrunning++; /* parent */
536 for(est = est_list; est != NULL; est = est->next) {
542 amfree(our_feature_string);
543 am_release_feature_set(our_features);
545 am_release_feature_set(g_options->features);
546 g_options->features = NULL;
547 amfree(g_options->hostname);
548 amfree(g_options->str);
551 #if defined(USE_DBMALLOC)
552 malloc_size_2 = malloc_inuse(&malloc_hist_2);
554 if(malloc_size_1 != malloc_size_2) {
555 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
562 printf("FORMAT ERROR IN REQUEST PACKET\n");
563 dbprintf(("%s: REQ packet is bogus%s%s\n",
564 debug_prefix_time(NULL),
565 err_extra ? ": " : "",
566 err_extra ? err_extra : ""));
579 int program_is_backup_api,
584 disk_estimates_t *newp, *curp;
591 if (level >= DUMP_LEVELS)
592 level = DUMP_LEVELS - 1;
594 for(curp = est_list; curp != NULL; curp = curp->next) {
595 if(strcmp(curp->amname, disk) == 0) {
596 /* already have disk info, just note the level request */
597 curp->est[level].needestimate = 1;
599 free_sl(options->exclude_file);
600 free_sl(options->exclude_list);
601 free_sl(options->include_file);
602 free_sl(options->include_list);
603 amfree(options->auth);
604 amfree(options->str);
611 newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
612 memset(newp, 0, SIZEOF(*newp));
613 newp->next = est_list;
615 newp->amname = stralloc(disk);
616 newp->qamname = quote_string(disk);
617 newp->amdevice = stralloc(amdevice);
618 newp->qamdevice = quote_string(amdevice);
619 newp->dirname = amname_to_dirname(newp->amdevice);
620 newp->qdirname = quote_string(newp->dirname);
621 newp->program = stralloc(prog);
623 newp->calcprog = stralloc(calcprog);
625 newp->calcprog = NULL;
626 newp->program_is_backup_api = program_is_backup_api;
627 newp->spindle = spindle;
628 newp->est[level].needestimate = 1;
629 newp->options = options;
631 /* fill in dump-since dates */
633 amdp = amandates_lookup(newp->amname);
635 newp->est[0].dumpsince = EPOCH;
636 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
637 dumpdate = amdp->dates[dumplev];
638 for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
639 if(dumpdate > newp->est[estlev].dumpsince)
640 newp->est[estlev].dumpsince = dumpdate;
648 disk_estimates_t * est)
651 amfree(est->qamname);
652 amfree(est->amdevice);
653 amfree(est->qamdevice);
654 amfree(est->dirname);
655 amfree(est->qdirname);
656 amfree(est->program);
658 free_sl(est->options->exclude_file);
659 free_sl(est->options->exclude_list);
660 free_sl(est->options->include_file);
661 free_sl(est->options->include_list);
662 amfree(est->options->str);
663 amfree(est->options->auth);
664 amfree(est->options);
669 * ------------------------------------------------------------------------
675 disk_estimates_t * est)
677 dbprintf(("%s: calculating for amname %s, dirname %s, spindle %d\n",
678 debug_prefix_time(NULL),
679 est->qamname, est->qdirname, est->spindle));
681 if(est->program_is_backup_api == 1)
682 backup_api_calc_estimate(est);
684 #ifndef USE_GENERIC_CALCSIZE
685 if(strcmp(est->program, "DUMP") == 0)
686 dump_calc_estimates(est);
690 if (strcmp(est->program, "GNUTAR") == 0 &&
691 est->amdevice[0] == '/' && est->amdevice[1] == '/')
692 smbtar_calc_estimates(est);
696 if (strcmp(est->program, "GNUTAR") == 0)
697 gnutar_calc_estimates(est);
701 if (est->amdevice[0] == '/' && est->amdevice[1] == '/')
702 dbprintf(("%s: Can't use CALCSIZE for samba estimate: %s %s\n",
703 debug_prefix_time(NULL),
704 est->qamname, est->qdirname));
707 generic_calc_estimates(est);
709 dbprintf(("%s: done with amname %s dirname %s spindle %d\n",
710 debug_prefix_time(NULL),
711 est->qamname, est->qdirname, est->spindle));
715 * ------------------------------------------------------------------------
719 /* local functions */
720 off_t getsize_dump(char *disk, char *amdevice, int level, option_t *options,
722 off_t getsize_smbtar(char *disk, char *amdevice, int level, option_t *options,
724 off_t getsize_gnutar(char *disk, char *amdevice, int level,
725 option_t *options, time_t dumpsince, char **errmsg);
726 off_t getsize_backup_api(char *program, char *disk, char *amdevice, int level,
727 option_t *options, time_t dumpsince, char **errmsg);
728 off_t handle_dumpline(char *str);
729 double first_num(char *str);
732 backup_api_calc_estimate(
733 disk_estimates_t * est)
737 char *errmsg = NULL, *qerrmsg;
739 for(level = 0; level < DUMP_LEVELS; level++) {
740 if (est->est[level].needestimate) {
741 dbprintf(("%s: getting size via backup-api for %s %s level %d\n",
742 debug_prefix_time(NULL), est->qamname, est->qamdevice,
744 size = getsize_backup_api(est->program, est->amname, est->amdevice,
746 est->est[level].dumpsince, &errmsg);
750 printf("%s %d SIZE " OFF_T_FMT "\n", est->qamname, level,
751 (OFF_T_FMT_TYPE)size);
752 if (errmsg && errmsg[0] != '\0') {
753 if(am_has_feature(g_options->features,
754 fe_rep_sendsize_quoted_error)) {
755 qerrmsg = quote_string(errmsg);
756 dbprintf(("errmsg is %s\n", errmsg));
757 printf("%s %d ERROR %s\n",
758 est->qamname, level, qerrmsg);
765 amfunlock(1, "size");
772 generic_calc_estimates(
773 disk_estimates_t * est)
775 int pipefd = -1, nullfd = -1;
777 char *my_argv[DUMP_LEVELS*2+22];
778 char number[NUM_STR_SIZE];
779 int i, level, my_argc;
783 char *file_exclude = NULL;
784 char *file_include = NULL;
786 FILE *dumpout = NULL;
787 off_t size = (off_t)1;
790 amwait_t wait_status;
791 char *errmsg = NULL, *qerrmsg;
793 cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
797 my_argv[my_argc++] = stralloc("calcsize");
798 if (g_options->config)
799 my_argv[my_argc++] = stralloc(g_options->config);
801 my_argv[my_argc++] = stralloc("NOCONFIG");
803 my_argv[my_argc++] = stralloc(est->calcprog);
805 my_argv[my_argc++] = stralloc(est->amname);
806 my_argv[my_argc++] = stralloc(est->dirname);
809 if(est->options->exclude_file)
810 nb_exclude += est->options->exclude_file->nb_element;
811 if(est->options->exclude_list)
812 nb_exclude += est->options->exclude_list->nb_element;
813 if(est->options->include_file)
814 nb_include += est->options->include_file->nb_element;
815 if(est->options->include_list)
816 nb_include += est->options->include_list->nb_element;
819 file_exclude = build_exclude(est->amname,
820 est->amdevice, est->options, 0);
822 file_include = build_include(est->amname,
823 est->amdevice, est->options, 0);
826 my_argv[my_argc++] = stralloc("-X");
827 my_argv[my_argc++] = file_exclude;
831 my_argv[my_argc++] = stralloc("-I");
832 my_argv[my_argc++] = file_include;
834 start_time = curclock();
836 dbprintf(("%s: running cmd: %s", debug_prefix_time(NULL), my_argv[0]));
837 for(i=0; i<my_argc; ++i)
838 dbprintf((" %s", my_argv[i]));
840 for(level = 0; level < DUMP_LEVELS; level++) {
841 if(est->est[level].needestimate) {
842 snprintf(number, SIZEOF(number), "%d", level);
843 my_argv[my_argc++] = stralloc(number);
844 dbprintf((" %s", number));
845 snprintf(number, SIZEOF(number),
846 "%ld", (long)est->est[level].dumpsince);
847 my_argv[my_argc++] = stralloc(number);
848 dbprintf((" %s", number));
851 my_argv[my_argc] = NULL;
854 fflush(stderr); fflush(stdout);
856 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
857 errmsg = vstrallocf("Cannot access /dev/null : %s",
859 dbprintf(("%s\n", errmsg));
863 calcpid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
866 dumpout = fdopen(pipefd,"r");
868 error("Can't fdopen: %s", strerror(errno));
871 match_expr = vstralloc(est->qamname," %d SIZE " OFF_T_FMT, NULL);
872 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
873 OFF_T_FMT_TYPE size_ = (OFF_T_FMT_TYPE)0;
876 if(sscanf(line, match_expr, &level, &size_) == 2) {
877 printf("%s\n", line); /* write to amandad */
878 dbprintf(("%s: estimate size for %s level %d: " OFF_T_FMT " KB\n",
879 debug_prefix_time(NULL),
888 dbprintf(("%s: waiting for %s %s child (pid=%d)\n",
889 debug_prefix_time(NULL), my_argv[0], est->qamdevice, calcpid));
890 waitpid(calcpid, &wait_status, 0);
891 if (WIFSIGNALED(wait_status)) {
892 errmsg = vstrallocf("%s terminated with signal %d: see %s",
893 "calcsize", WTERMSIG(wait_status),
895 } else if (WIFEXITED(wait_status)) {
896 if (WEXITSTATUS(wait_status) != 0) {
897 errmsg = vstrallocf("%s exited with status %d: see %s",
898 "calcsize", WEXITSTATUS(wait_status),
904 errmsg = vstrallocf("%s got bad exit: see %s",
905 "calcsize", debug_fn());
907 dbprintf(("%s: after %s %s wait: child pid=%d status=%d\n",
908 debug_prefix_time(NULL), my_argv[0], est->qamdevice,
909 calcpid, WEXITSTATUS(wait_status)));
911 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
912 dbprintf(("%s: estimate time for %s: %s\n",
913 debug_prefix_time(NULL),
915 walltime_str(timessub(curclock(), start_time))));
918 if (errmsg && errmsg[0] != '\0') {
919 if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
920 qerrmsg = quote_string(errmsg);
921 dbprintf(("errmsg is %s\n", errmsg));
922 printf("%s %d ERROR %s\n",
923 est->qamname, 0, qerrmsg);
928 for(i = 0; i < my_argc; i++) {
937 disk_estimates_t * est)
941 char *errmsg=NULL, *qerrmsg;
943 for(level = 0; level < DUMP_LEVELS; level++) {
944 if(est->est[level].needestimate) {
945 dbprintf(("%s: getting size via dump for %s level %d\n",
946 debug_prefix_time(NULL), est->qamname, level));
947 size = getsize_dump(est->amname, est->amdevice,
948 level, est->options, &errmsg);
952 printf("%s %d SIZE " OFF_T_FMT "\n",
953 est->qamname, level, (OFF_T_FMT_TYPE)size);
954 if (errmsg && errmsg[0] != '\0') {
955 if(am_has_feature(g_options->features,
956 fe_rep_sendsize_quoted_error)) {
957 qerrmsg = quote_string(errmsg);
958 dbprintf(("errmsg is %s\n", errmsg));
959 printf("%s %d ERROR %s\n",
960 est->qamname, level, qerrmsg);
967 amfunlock(1, "size");
974 smbtar_calc_estimates(
975 disk_estimates_t * est)
979 char *errmsg = NULL, *qerrmsg;
981 for(level = 0; level < DUMP_LEVELS; level++) {
982 if(est->est[level].needestimate) {
983 dbprintf(("%s: getting size via smbclient for %s level %d\n",
984 debug_prefix_time(NULL), est->qamname, level));
985 size = getsize_smbtar(est->amname, est->amdevice, level,
986 est->options, &errmsg);
990 printf("%s %d SIZE " OFF_T_FMT "\n",
991 est->qamname, level, (OFF_T_FMT_TYPE)size);
992 if (errmsg && errmsg[0] != '\0') {
993 if(am_has_feature(g_options->features,
994 fe_rep_sendsize_quoted_error)) {
995 qerrmsg = quote_string(errmsg);
996 dbprintf(("errmsg is %s\n", errmsg));
997 printf("%s %d ERROR %s\n",
998 est->qamname, level, qerrmsg);
1005 amfunlock(1, "size");
1013 gnutar_calc_estimates(
1014 disk_estimates_t * est)
1018 char *errmsg = NULL, *qerrmsg;
1020 for(level = 0; level < DUMP_LEVELS; level++) {
1021 if (est->est[level].needestimate) {
1022 dbprintf(("%s: getting size via gnutar for %s level %d\n",
1023 debug_prefix_time(NULL), est->qamname, level));
1024 size = getsize_gnutar(est->amname, est->amdevice, level,
1025 est->options, est->est[level].dumpsince,
1030 printf("%s %d SIZE " OFF_T_FMT "\n",
1031 est->qamname, level, (OFF_T_FMT_TYPE)size);
1032 if (errmsg && errmsg[0] != '\0') {
1033 if(am_has_feature(g_options->features,
1034 fe_rep_sendsize_quoted_error)) {
1035 qerrmsg = quote_string(errmsg);
1036 dbprintf(("errmsg is %s\n", errmsg));
1037 printf("%s %d ERROR %s\n",
1038 est->qamname, level, qerrmsg);
1045 amfunlock(1, "size");
1051 typedef struct regex_s {
1057 regex_t re_size[] = {
1059 {" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1060 {" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1061 {" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
1062 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
1063 {"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
1064 {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1065 {"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
1066 {"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
1067 {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
1068 {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
1069 {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1071 {"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
1072 {"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
1073 {"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
1074 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1076 #ifdef HAVE_DUMP_ESTIMATE
1077 {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1078 /* DU 3.2g dump -E */
1079 {"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
1080 {"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
1085 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
1089 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
1090 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1094 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
1098 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
1102 #if SAMBA_VERSION >= 2
1103 #define SAMBA_DEBUG_LEVEL "0"
1104 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
1106 #define SAMBA_DEBUG_LEVEL "3"
1107 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
1123 int pipefd[2], nullfd, stdoutfd, killctl[2];
1127 char *dumpkeys = NULL;
1128 char *device = NULL;
1129 char *fstype = NULL;
1133 char *rundump_cmd = NULL;
1134 char level_str[NUM_STR_SIZE];
1137 char *qdisk = quote_string(disk);
1140 amwait_t wait_status;
1141 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1145 (void)options; /* Quiet unused parameter warning */
1147 (void)getsize_smbtar; /* Quiet unused parameter warning */
1149 snprintf(level_str, SIZEOF(level_str), "%d", level);
1151 device = amname_to_devname(amdevice);
1152 qdevice = quote_string(device);
1153 fstype = amname_to_fstype(amdevice);
1155 dbprintf(("%s: calculating for device %s with %s\n",
1156 debug_prefix_time(NULL), qdevice, fstype));
1158 cmd = vstralloc(libexecdir, "/rundump", versionsuffix(), NULL);
1159 rundump_cmd = stralloc(cmd);
1160 if (g_options->config)
1161 config = g_options->config;
1163 config = "NOCONFIG";
1164 if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1165 *errmsg = vstrallocf("getsize_dump could not open /dev/null: %s",
1167 dbprintf(("%s\n", *errmsg));
1169 amfree(rundump_cmd);
1176 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1177 if (pipe(pipefd) < 0) {
1178 *errmsg = vstrallocf("getsize_dump could create data pipes: %s",
1180 dbprintf(("%s\n", *errmsg));
1182 amfree(rundump_cmd);
1190 #ifdef XFSDUMP /* { */
1192 if (strcmp(fstype, "xfs") == 0)
1197 name = stralloc(" (xfsdump)");
1198 dbprintf(("%s: running \"%s%s -F -J -l %s - %s\"\n",
1199 debug_prefix_time(NULL), cmd, name, level_str, qdevice));
1203 #ifdef VXDUMP /* { */
1205 if (strcmp(fstype, "vxfs") == 0)
1211 name = stralloc(" (vxdump)");
1213 name = stralloc("");
1214 cmd = newstralloc(cmd, VXDUMP);
1215 config = skip_argument;
1218 dumpkeys = vstralloc(level_str, "s", "f", NULL);
1219 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
1220 debug_prefix_time(NULL), cmd, name, dumpkeys, qdevice));
1224 #ifdef VDUMP /* { */
1226 if (strcmp(fstype, "advfs") == 0)
1231 name = stralloc(" (vdump)");
1234 device = amname_to_dirname(amdevice);
1235 qdevice = quote_string(device);
1236 dumpkeys = vstralloc(level_str, "b", "f", NULL);
1237 dbprintf(("%s: running \"%s%s %s 60 - %s\"\n",
1238 debug_prefix_time(NULL), cmd, name, dumpkeys, qdevice));
1244 # ifdef USE_RUNDUMP /* { */
1245 # ifdef AIX_BACKUP /* { */
1246 name = stralloc(" (backup)");
1248 name = vstralloc(" (", DUMP, ")", NULL);
1251 name = stralloc("");
1252 cmd = newstralloc(cmd, DUMP);
1253 config = skip_argument;
1257 # ifdef AIX_BACKUP /* { */
1258 dumpkeys = vstralloc("-", level_str, "f", NULL);
1259 dbprintf(("%s: running \"%s%s %s - %s\"\n",
1260 debug_prefix_time(NULL), cmd, name, dumpkeys, qdevice));
1262 # ifdef HAVE_DUMP_ESTIMATE
1263 # define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1265 # define PARAM_DUMP_ESTIMATE ""
1267 # ifdef HAVE_HONOR_NODUMP
1268 # define PARAM_HONOR_NODUMP "h"
1270 # define PARAM_HONOR_NODUMP ""
1272 dumpkeys = vstralloc(level_str,
1273 PARAM_DUMP_ESTIMATE,
1277 # ifdef HAVE_DUMP_ESTIMATE
1278 stdoutfd = pipefd[1];
1281 # ifdef HAVE_HONOR_NODUMP /* { */
1282 dbprintf(("%s: running \"%s%s %s 0 1048576 - %s\"\n",
1283 debug_prefix_time(NULL), cmd, name, dumpkeys, qdevice));
1285 dbprintf(("%s: running \"%s%s %s 1048576 - %s\"\n",
1286 debug_prefix_time(NULL), cmd, name, dumpkeys, qdevice));
1293 error("no dump program available");
1297 if (pipe(killctl) < 0) {
1298 dbprintf(("%s: Could not create pipe: %s\n",
1299 debug_prefix_time(NULL), strerror(errno)));
1300 /* Message will be printed later... */
1301 killctl[0] = killctl[1] = -1;
1304 start_time = curclock();
1305 switch(dumppid = fork()) {
1307 *errmsg = vstrallocf("cannot fork for killpgrp: %s",
1309 dbprintf(("%s\n", *errmsg));
1312 amfree(rundump_cmd);
1321 case 0: /* child process */
1324 else if (killctl[0] == -1 || killctl[1] == -1)
1325 dbprintf(("%s: Trying without killpgrp\n", debug_prefix_time(NULL)));
1329 dbprintf(("%s: fork failed, trying without killpgrp\n",
1330 debug_prefix_time(NULL)));
1336 char *killpgrp_cmd = vstralloc(libexecdir, "/killpgrp",
1337 versionsuffix(), NULL);
1338 dbprintf(("%s: running %s\n",
1339 debug_prefix_time(NULL), killpgrp_cmd));
1340 dup2(killctl[0], 0);
1347 if (g_options->config)
1348 config = g_options->config;
1350 config = "NOCONFIG";
1351 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1353 dbprintf(("%s: cannot execute %s: %s\n",
1354 debug_prefix_time(NULL), killpgrp_cmd, strerror(errno)));
1358 case 0: /* child process */
1367 if (killctl[0] != -1)
1369 if (killctl[1] != -1)
1374 if (strcmp(fstype, "xfs") == 0)
1379 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1380 level_str, "-", device, (char *)0, safe_env());
1382 execle(cmd, "xfsdump", "-F", "-J", "-l",
1383 level_str, "-", device, (char *)0, safe_env());
1388 if (strcmp(fstype, "vxfs") == 0)
1393 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1394 "-", device, (char *)0, safe_env());
1396 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1397 device, (char *)0, safe_env());
1402 if (strcmp(fstype, "advfs") == 0)
1407 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1408 device, (char *)0, safe_env());
1410 execle(cmd, "vdump", dumpkeys, "60", "-",
1411 device, (char *)0, safe_env());
1417 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1418 device, (char *)0, safe_env());
1420 execle(cmd, "backup", dumpkeys, "-",
1421 device, (char *)0, safe_env());
1424 execle(cmd, "rundump", config, "dump", dumpkeys,
1425 #ifdef HAVE_HONOR_NODUMP
1428 "1048576", "-", device, (char *)0, safe_env());
1430 execle(cmd, "dump", dumpkeys,
1431 #ifdef HAVE_HONOR_NODUMP
1434 "1048576", "-", device, (char *)0, safe_env());
1439 error("exec %s failed or no dump program available: %s",
1440 cmd, strerror(errno));
1446 amfree(rundump_cmd);
1449 if (killctl[0] != -1)
1451 dumpout = fdopen(pipefd[0],"r");
1453 error("Can't fdopen: %s", strerror(errno));
1457 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1458 if (line[0] == '\0')
1460 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1461 size = handle_dumpline(line);
1462 if(size > (off_t)-1) {
1464 while ((line = agets(dumpout)) != NULL) {
1465 if (line[0] != '\0')
1470 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1477 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1478 dbprintf(("%s: estimate time for %s level %d: %s\n",
1479 debug_prefix_time(NULL),
1482 walltime_str(timessub(curclock(), start_time))));
1483 if(size == (off_t)-1) {
1484 *errmsg = vstrallocf("no size line match in %s%s output",
1486 dbprintf(("%s: %s for %s\n", debug_prefix_time(NULL),
1489 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1490 dbprintf(("%s: Run %s%s manually to check for errors\n",
1491 debug_prefix_time(NULL), cmd, name));
1492 } else if(size == (off_t)0 && level == 0) {
1493 dbprintf(("%s: possible %s%s problem -- is \"%s\" really empty?\n",
1494 debug_prefix_time(NULL), cmd, name, disk));
1495 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1497 dbprintf(("%s: estimate size for %s level %d: " OFF_T_FMT " KB\n",
1498 debug_prefix_time(NULL),
1501 (OFF_T_FMT_TYPE)size));
1504 if (killctl[1] != -1) {
1505 dbprintf(("%s: asking killpgrp to terminate\n",
1506 debug_prefix_time(NULL)));
1508 for(s = 5; s > 0; --s) {
1510 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1516 * First, try to kill the dump process nicely. If it ignores us
1517 * for several seconds, hit it harder.
1519 dbprintf(("%s: sending SIGTERM to process group %ld\n",
1520 debug_prefix_time(NULL), (long)dumppid));
1521 if (kill(-dumppid, SIGTERM) == -1) {
1522 dbprintf(("%s: kill failed: %s\n",
1523 debug_prefix_time(NULL), strerror(errno)));
1525 /* Now check whether it dies */
1526 for(s = 5; s > 0; --s) {
1528 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1532 dbprintf(("%s: sending SIGKILL to process group %ld\n",
1533 debug_prefix_time(NULL), (long)dumppid));
1534 if (kill(-dumppid, SIGKILL) == -1) {
1535 dbprintf(("%s: kill failed: %s\n",
1536 debug_prefix_time(NULL), strerror(errno)));
1538 for(s = 5; s > 0; --s) {
1540 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1544 dbprintf(("%s: waiting for %s%s \"%s\" child\n",
1545 debug_prefix_time(NULL), cmd, name, qdisk));
1546 waitpid(dumppid, &wait_status, 0);
1547 if (WIFSIGNALED(wait_status)) {
1548 *errmsg = vstrallocf("%s terminated with signal %d: see %s",
1549 cmd, WTERMSIG(wait_status), debug_fn());
1550 } else if (WIFEXITED(wait_status)) {
1551 if (WEXITSTATUS(wait_status) != 0) {
1552 *errmsg = vstrallocf("%s exited with status %d: see %s",
1553 cmd, WEXITSTATUS(wait_status), debug_fn());
1558 *errmsg = vstrallocf("%s got bad exit: see %s",
1561 dbprintf(("%s: after %s%s %s wait\n",
1562 debug_prefix_time(NULL), cmd, name, qdisk));
1589 int pipefd = -1, nullfd = -1, passwdfd = -1;
1593 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1594 char *share = NULL, *subdir = NULL;
1601 char *error_pn = NULL;
1602 char *qdisk = quote_string(disk);
1603 amwait_t wait_status;
1605 (void)options; /* Quiet unused parameter warning */
1607 error_pn = stralloc2(get_pname(), "-smbclient");
1609 parsesharename(amdevice, &share, &subdir);
1613 set_pname(error_pn);
1615 error("cannot parse disk entry %s for share/subdir", qdisk);
1618 if ((subdir) && (SAMBA_VERSION < 2)) {
1621 set_pname(error_pn);
1623 error("subdirectory specified for share %s but samba not v2 or better", qdisk);
1626 if ((user_and_password = findpass(share, &domain)) == NULL) {
1629 memset(domain, '\0', strlen(domain));
1632 set_pname(error_pn);
1634 error("cannot find password for %s", disk);
1637 lpass = strlen(user_and_password);
1638 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1639 memset(user_and_password, '\0', (size_t)lpass);
1640 amfree(user_and_password);
1642 memset(domain, '\0', strlen(domain));
1645 set_pname(error_pn);
1647 error("password field not \'user%%pass\' for %s", disk);
1651 pwtext_len = strlen(pwtext);
1652 if ((sharename = makesharename(share, 0)) == NULL) {
1653 memset(user_and_password, '\0', (size_t)lpass);
1654 amfree(user_and_password);
1656 memset(domain, '\0', strlen(domain));
1659 set_pname(error_pn);
1661 error("cannot make share name of %s", share);
1664 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1665 memset(user_and_password, '\0', (size_t)lpass);
1666 amfree(user_and_password);
1668 memset(domain, '\0', strlen(domain));
1671 set_pname(error_pn);
1674 error("could not open /dev/null: %s\n",
1679 #if SAMBA_VERSION >= 2
1681 tarkeys = "archive 0;recurse;du";
1683 tarkeys = "archive 1;recurse;du";
1686 tarkeys = "archive 0;recurse;dir";
1688 tarkeys = "archive 1;recurse;dir";
1691 start_time = curclock();
1693 if (pwtext_len > 0) {
1694 pw_fd_env = "PASSWD_FD";
1696 pw_fd_env = "dummy_PASSWD_FD";
1698 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
1699 &nullfd, &nullfd, &pipefd,
1700 pw_fd_env, &passwdfd,
1703 "-d", SAMBA_DEBUG_LEVEL,
1704 *user_and_password ? "-U" : skip_argument,
1705 *user_and_password ? user_and_password : skip_argument,
1707 domain ? "-W" : skip_argument,
1708 domain ? domain : skip_argument,
1709 #if SAMBA_VERSION >= 2
1710 subdir ? "-D" : skip_argument,
1711 subdir ? subdir : skip_argument,
1716 memset(domain, '\0', strlen(domain));
1720 if(pwtext_len > 0 && fullwrite(passwdfd, pwtext, (size_t)pwtext_len) < 0) {
1721 int save_errno = errno;
1723 memset(user_and_password, '\0', (size_t)lpass);
1724 amfree(user_and_password);
1726 set_pname(error_pn);
1728 error("password write failed: %s", strerror(save_errno));
1731 memset(user_and_password, '\0', (size_t)lpass);
1732 amfree(user_and_password);
1738 dumpout = fdopen(pipefd,"r");
1740 error("Can't fdopen: %s", strerror(errno));
1744 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1745 if (line[0] == '\0')
1747 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1748 size = handle_dumpline(line);
1751 while ((line = agets(dumpout)) != NULL) {
1752 if (line[0] != '\0')
1757 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
1764 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1765 dbprintf(("%s: estimate time for %s level %d: %s\n",
1766 debug_prefix_time(NULL),
1769 walltime_str(timessub(curclock(), start_time))));
1770 if(size == (off_t)-1) {
1771 *errmsg = vstrallocf("no size line match in %s output",
1773 dbprintf(("%s: %s for %s\n", debug_prefix_time(NULL),
1775 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1776 } else if(size == (off_t)0 && level == 0) {
1777 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
1778 debug_prefix_time(NULL), SAMBA_CLIENT, disk));
1779 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
1781 dbprintf(("%s: estimate size for %s level %d: " OFF_T_FMT " KB\n",
1782 debug_prefix_time(NULL),
1785 (OFF_T_FMT_TYPE)size));
1787 kill(-dumppid, SIGTERM);
1789 dbprintf(("%s: waiting for %s \"%s\" child\n",
1790 debug_prefix_time(NULL), SAMBA_CLIENT, qdisk));
1791 waitpid(dumppid, &wait_status, 0);
1792 if (WIFSIGNALED(wait_status)) {
1793 *errmsg = vstrallocf("%s terminated with signal %d: see %s",
1794 "smbclient", WTERMSIG(wait_status), debug_fn());
1795 } else if (WIFEXITED(wait_status)) {
1796 if (WEXITSTATUS(wait_status) != 0) {
1797 *errmsg = vstrallocf("%s exited with status %d: see %s",
1798 "smbclient", WEXITSTATUS(wait_status),
1804 *errmsg = vstrallocf("%s got bad exit: see %s",
1805 "smbclient", debug_fn());
1807 dbprintf(("%s: after %s %s wait\n",
1808 debug_prefix_time(NULL), SAMBA_CLIENT, qdisk));
1830 int pipefd = -1, nullfd = -1;
1832 off_t size = (off_t)-1;
1833 FILE *dumpout = NULL;
1834 char *incrname = NULL;
1835 char *basename = NULL;
1836 char *dirname = NULL;
1837 char *inputname = NULL;
1842 char dumptimestr[80];
1848 char *file_exclude = NULL;
1849 char *file_include = NULL;
1854 char *qdisk = quote_string(disk);
1855 char *gnutar_list_dir;
1856 amwait_t wait_status;
1858 if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
1859 if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
1860 if(options->include_file) nb_include += options->include_file->nb_element;
1861 if(options->include_list) nb_include += options->include_list->nb_element;
1863 if(nb_exclude > 0) file_exclude = build_exclude(disk, amdevice, options, 0);
1864 if(nb_include > 0) file_include = build_include(disk, amdevice, options, 0);
1866 my_argv = alloc(SIZEOF(char *) * 22);
1869 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
1870 if (strlen(gnutar_list_dir) == 0)
1871 gnutar_list_dir = NULL;
1872 if (gnutar_list_dir) {
1873 char number[NUM_STR_SIZE];
1878 basename = vstralloc(gnutar_list_dir,
1880 g_options->hostname,
1884 * The loop starts at the first character of the host name,
1887 s = basename + strlen(gnutar_list_dir) + 1;
1888 while((ch = *s++) != '\0') {
1889 if(ch == '/' || isspace(ch)) s[-1] = '_';
1892 snprintf(number, SIZEOF(number), "%d", level);
1893 incrname = vstralloc(basename, "_", number, ".new", NULL);
1897 * Open the listed incremental file from the previous level. Search
1898 * backward until one is found. If none are found (which will also
1899 * be true for a level 0), arrange to read from /dev/null.
1903 while (infd == -1) {
1904 if (--baselevel >= 0) {
1905 snprintf(number, SIZEOF(number), "%d", baselevel);
1906 inputname = newvstralloc(inputname,
1907 basename, "_", number, NULL);
1909 inputname = newstralloc(inputname, "/dev/null");
1911 if ((infd = open(inputname, O_RDONLY)) == -1) {
1913 *errmsg = vstrallocf("gnutar: error opening %s: %s",
1914 inputname, strerror(errno));
1915 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
1916 if (baselevel < 0) {
1924 * Copy the previous listed incremental file to the new one.
1926 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1927 *errmsg = vstrallocf("opening %s: %s",
1928 incrname, strerror(errno));
1929 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
1933 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1934 if (fullwrite(outfd, &buf, (size_t)nb) < nb) {
1935 *errmsg = vstrallocf("writing to %s: %s",
1936 incrname, strerror(errno));
1937 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
1943 *errmsg = vstrallocf("reading from %s: %s",
1944 inputname, strerror(errno));
1945 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
1949 if (close(infd) != 0) {
1950 *errmsg = vstrallocf("closing %s: %s",
1951 inputname, strerror(errno));
1952 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
1955 if (close(outfd) != 0) {
1956 *errmsg = vstrallocf("closing %s: %s",
1957 incrname, strerror(errno));
1958 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
1966 gmtm = gmtime(&dumpsince);
1967 snprintf(dumptimestr, SIZEOF(dumptimestr),
1968 "%04d-%02d-%02d %2d:%02d:%02d GMT",
1969 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
1970 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
1972 dirname = amname_to_dirname(amdevice);
1974 cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
1975 my_argv[i++] = "runtar";
1976 if (g_options->config)
1977 my_argv[i++] = g_options->config;
1979 my_argv[i++] = "NOCONFIG";
1982 my_argv[i++] = GNUTAR;
1984 my_argv[i++] = "tar";
1986 my_argv[i++] = "--create";
1987 my_argv[i++] = "--file";
1988 my_argv[i++] = "/dev/null";
1989 my_argv[i++] = "--directory";
1990 my_argv[i++] = dirname;
1991 my_argv[i++] = "--one-file-system";
1992 if (gnutar_list_dir) {
1993 my_argv[i++] = "--listed-incremental";
1994 my_argv[i++] = incrname;
1996 my_argv[i++] = "--incremental";
1997 my_argv[i++] = "--newer";
1998 my_argv[i++] = dumptimestr;
2000 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2001 /* --atime-preserve causes gnutar to call
2002 * utime() after reading files in order to
2003 * adjust their atime. However, utime()
2004 * updates the file's ctime, so incremental
2005 * dumps will think the file has changed. */
2006 my_argv[i++] = "--atime-preserve";
2008 my_argv[i++] = "--sparse";
2009 my_argv[i++] = "--ignore-failed-read";
2010 my_argv[i++] = "--totals";
2013 my_argv[i++] = "--exclude-from";
2014 my_argv[i++] = file_exclude;
2018 my_argv[i++] = "--files-from";
2019 my_argv[i++] = file_include;
2024 my_argv[i++] = NULL;
2026 start_time = curclock();
2028 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2029 *errmsg = vstrallocf("Cannot access /dev/null : %s",
2031 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
2035 dumppid = pipespawnv(cmd, STDERR_PIPE, &nullfd, &nullfd, &pipefd, my_argv);
2037 dumpout = fdopen(pipefd,"r");
2039 error("Can't fdopen: %s", strerror(errno));
2043 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2044 if (line[0] == '\0')
2046 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
2047 size = handle_dumpline(line);
2048 if(size > (off_t)-1) {
2050 while ((line = agets(dumpout)) != NULL) {
2051 if (line[0] != '\0') {
2057 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
2065 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
2066 dbprintf(("%s: estimate time for %s level %d: %s\n",
2067 debug_prefix_time(NULL),
2070 walltime_str(timessub(curclock(), start_time))));
2071 if(size == (off_t)-1) {
2072 *errmsg = vstrallocf("no size line match in %s output", my_argv[0]);
2073 dbprintf(("%s: %s for %s\n", debug_prefix_time(NULL),
2075 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
2076 } else if(size == (off_t)0 && level == 0) {
2077 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
2078 debug_prefix_time(NULL), my_argv[0], disk));
2079 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
2081 dbprintf(("%s: estimate size for %s level %d: " OFF_T_FMT " KB\n",
2082 debug_prefix_time(NULL),
2085 (OFF_T_FMT_TYPE)size));
2087 kill(-dumppid, SIGTERM);
2089 dbprintf(("%s: waiting for %s \"%s\" child\n",
2090 debug_prefix_time(NULL), my_argv[0], qdisk));
2091 waitpid(dumppid, &wait_status, 0);
2092 if (WIFSIGNALED(wait_status)) {
2093 *errmsg = vstrallocf("%s terminated with signal %d: see %s",
2094 cmd, WTERMSIG(wait_status), debug_fn());
2095 } else if (WIFEXITED(wait_status)) {
2096 if (WEXITSTATUS(wait_status) != 0) {
2097 *errmsg = vstrallocf("%s exited with status %d: see %s",
2098 cmd, WEXITSTATUS(wait_status), debug_fn());
2103 *errmsg = vstrallocf("%s got bad exit: see %s",
2106 dbprintf(("%s: after %s %s wait\n",
2107 debug_prefix_time(NULL), my_argv[0], qdisk));
2121 amfree(file_exclude);
2122 amfree(file_include);
2143 int pipeinfd[2], pipeoutfd[2], nullfd;
2145 off_t size = (off_t)-1;
2146 FILE *dumpout, *toolin;
2149 char dumptimestr[80];
2152 char *argvchild[10];
2153 char *newoptstr = NULL;
2156 char *qdisk = quote_string(disk);
2157 char *qamdevice = quote_string(amdevice);
2158 amwait_t wait_status;
2159 char levelstr[NUM_STR_SIZE];
2160 backup_support_option_t *bsu;
2163 gmtm = gmtime(&dumpsince);
2164 snprintf(dumptimestr, SIZEOF(dumptimestr),
2165 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2166 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2167 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2169 cmd = vstralloc(DUMPER_DIR, "/", program, NULL);
2171 bsu = backup_support_option(program, g_options, disk, amdevice);
2174 argvchild[i++] = program;
2175 argvchild[i++] = "estimate";
2176 if (bsu->message_line == 1) {
2177 argvchild[i++] = "--message";
2178 argvchild[i++] = "line";
2180 if (g_options->config && bsu->config == 1) {
2181 argvchild[i++] = "--config";
2182 argvchild[i++] = g_options->config;
2184 if (g_options->hostname && bsu->host == 1) {
2185 argvchild[i++] = "--host";
2186 argvchild[i++] = g_options->hostname;
2188 argvchild[i++] = "--device";
2189 argvchild[i++] = amdevice;
2190 if (disk && bsu->disk == 1) {
2191 argvchild[i++] = "--disk";
2192 argvchild[i++] = disk;
2194 if (level <= bsu->max_level) {
2195 argvchild[i++] = "--level";
2196 snprintf(levelstr,SIZEOF(levelstr),"%d",level);
2197 argvchild[i++] = levelstr;
2200 argvchild[i] = NULL;
2202 dbprintf(("%s: running %s", debug_prefix_time(NULL), cmd));
2203 for(j = 1; j < i; j++) {
2204 dbprintf((" %s", argvchild[j]));
2208 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2209 *errmsg = vstrallocf("Cannot access /dev/null : %s",
2211 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
2215 if (pipe(pipeinfd) < 0) {
2216 *errmsg = vstrallocf("getsize_backup_api could create data pipes: %s",
2218 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
2222 if (pipe(pipeoutfd) < 0) {
2223 *errmsg = vstrallocf("getsize_backup_api could create data pipes: %s",
2225 dbprintf(("%s: %s\n", debug_prefix_time(NULL), *errmsg));
2229 start_time = curclock();
2231 switch(dumppid = fork()) {
2238 dup2(pipeinfd[0], 0);
2239 dup2(pipeoutfd[1], 1);
2241 aclose(pipeinfd[1]);
2242 aclose(pipeoutfd[0]);
2244 execve(cmd, argvchild, safe_env());
2245 error("exec %s failed: %s", cmd, strerror(errno));
2250 aclose(pipeinfd[0]);
2251 aclose(pipeoutfd[1]);
2253 toolin = fdopen(pipeinfd[1],"w");
2255 error("Can't fdopen: %s", strerror(errno));
2259 output_tool_property(toolin, options);
2263 dumpout = fdopen(pipeoutfd[0],"r");
2265 error("Can't fdopen: %s", strerror(errno));
2269 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2270 OFF_T_FMT_TYPE size1_ = (OFF_T_FMT_TYPE)0;
2271 OFF_T_FMT_TYPE size2_ = (OFF_T_FMT_TYPE)0;
2272 if (line[0] == '\0')
2274 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
2275 i = sscanf(line, OFF_T_FMT " " OFF_T_FMT, &size1_, &size2_);
2276 size1 = (off_t)size1_;
2277 size2 = (off_t)size2_;
2279 size = size1 * size2;
2283 while ((line = agets(dumpout)) != NULL) {
2284 if (line[0] != '\0')
2289 dbprintf(("%s: %s\n", debug_prefix_time(NULL), line));
2296 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
2297 dbprintf(("%s: estimate time for %s level %d: %s\n",
2298 debug_prefix_time(NULL),
2301 walltime_str(timessub(curclock(), start_time))));
2302 if(size == (off_t)-1) {
2303 *errmsg = vstrallocf("no size line match in %s output", cmd);
2304 dbprintf(("%s: %s for %s\n", debug_prefix_time(NULL), cmd, qdisk));
2305 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
2306 } else if(size == (off_t)0 && level == 0) {
2307 dbprintf(("%s: possible %s problem -- is \"%s\" really empty?\n",
2308 debug_prefix_time(NULL), cmd, qdisk));
2309 dbprintf(("%s: .....\n", debug_prefix_time(NULL)));
2311 dbprintf(("%s: estimate size for %s level %d: " OFF_T_FMT " KB\n",
2312 debug_prefix_time(NULL),
2315 (OFF_T_FMT_TYPE)size));
2317 kill(-dumppid, SIGTERM);
2319 dbprintf(("%s: waiting for %s \"%s\" child\n",
2320 debug_prefix_time(NULL), cmd, qdisk));
2321 waitpid(dumppid, &wait_status, 0);
2322 if (WIFSIGNALED(wait_status)) {
2323 *errmsg = vstrallocf("%s terminated with signal %d: see %s",
2324 cmd, WTERMSIG(wait_status), debug_fn());
2325 } else if (WIFEXITED(wait_status)) {
2326 if (WEXITSTATUS(wait_status) != 0) {
2327 *errmsg = vstrallocf("%s exited with status %d: see %s", cmd,
2328 WEXITSTATUS(wait_status), debug_fn());
2333 *errmsg = vstrallocf("%s got bad exit: see %s",
2336 dbprintf(("%s: after %s %s wait\n",
2337 debug_prefix_time(NULL), cmd, qdisk));
2353 * Returns the value of the first integer in a string.
2365 while(ch && !isdigit(ch)) ch = *str++;
2367 while(isdigit(ch) || (ch == '.')) ch = *str++;
2376 * Checks the dump output line against the error and size regex tables.
2386 /* check for size match */
2388 for(rp = re_size; rp->regex != NULL; rp++) {
2389 if(match(rp->regex, str)) {
2390 size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2392 size = 1.0; /* found on NeXT -- sigh */