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 10421 2008-03-06 18:48:30Z martineau $
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, ...) do { \
49 if ((i) <= debug_sebdsize) { \
50 dbprintf(__VA_ARGS__); \
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;
93 level_estimate_t est[DUMP_LEVELS];
96 disk_estimates_t *est_list;
98 static am_feature_t *our_features = NULL;
99 static char *our_feature_string = NULL;
100 static g_option_t *g_options = NULL;
101 static gboolean amandates_started = FALSE;
103 /* local functions */
104 int main(int argc, char **argv);
105 void dle_add_diskest(dle_t *dle);
106 void calc_estimates(disk_estimates_t *est);
107 void free_estimates(disk_estimates_t *est);
108 void dump_calc_estimates(disk_estimates_t *);
109 void star_calc_estimates(disk_estimates_t *);
110 void smbtar_calc_estimates(disk_estimates_t *);
111 void gnutar_calc_estimates(disk_estimates_t *);
112 void application_api_calc_estimate(disk_estimates_t *);
113 void generic_calc_estimates(disk_estimates_t *);
122 disk_estimates_t *est;
123 disk_estimates_t *est1;
124 disk_estimates_t *est_prev;
128 char *err_extra = NULL;
134 char *qamdevice = NULL;
138 (void)argc; /* Quiet unused parameter warning */
139 (void)argv; /* Quiet unused parameter warning */
144 * Configure program for internationalization:
145 * 1) Only set the message locale for now.
146 * 2) Set textdomain for all amanda related programs to "amanda"
147 * We don't want to be forced to support dozens of message catalogs.
149 setlocale(LC_MESSAGES, "C");
150 textdomain("amanda");
155 set_pname("sendsize");
157 /* Don't die when child closes pipe */
158 signal(SIGPIPE, SIG_IGN);
160 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
161 dbopen(DBG_SUBDIR_CLIENT);
163 dbprintf(_("version %s\n"), version());
165 our_features = am_init_feature_set();
166 our_feature_string = am_feature_to_string(our_features);
168 config_init(CONFIG_INIT_CLIENT, NULL);
169 /* (check for config errors comes later) */
171 check_running_as(RUNNING_AS_CLIENT_LOGIN);
173 /* handle all service requests */
175 for(; (line = agets(stdin)) != NULL; free(line)) {
178 if(strncmp_const(line, "OPTIONS ") == 0) {
179 g_options = parse_g_options(line+8, 1);
180 if(!g_options->hostname) {
181 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
182 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
183 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
186 g_printf("OPTIONS ");
187 if(am_has_feature(g_options->features, fe_rep_options_features)) {
188 g_printf("features=%s;", our_feature_string);
190 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
191 g_printf("maxdumps=%d;", g_options->maxdumps);
193 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
194 g_printf("hostname=%s;", g_options->hostname);
199 if (g_options->config) {
200 /* overlay this configuration on the existing (nameless) configuration */
201 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
204 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
207 /* check for any config errors now */
208 if (config_errors(&errlist) >= CFGERR_ERRORS) {
209 char *errstr = config_errors_to_error_string(errlist);
210 g_printf("%s\n", errstr);
215 if (am_has_feature(g_options->features, fe_req_xml)) {
225 skip_whitespace(s, ch); /* find the program name */
227 err_extra = stralloc(_("no program name"));
228 goto err; /* no program name */
230 dle->program = s - 1;
231 skip_non_whitespace(s, ch);
234 dle->program_is_application_api=0;
235 if(strncmp_const(dle->program, "CALCSIZE") == 0) {
236 skip_whitespace(s, ch); /* find the program name */
238 err_extra = stralloc(_("no program name"));
242 dle->program = s - 1;
243 skip_non_whitespace(s, ch);
245 if (strcmp(dle->program,"APPLICATION") == 0) {
246 dle->program_is_application_api=1;
247 skip_whitespace(s, ch); /* find dumper name */
249 goto err; /* no program */
251 dle->program = s - 1;
252 skip_non_whitespace(s, ch);
258 if (strcmp(dle->program,"APPLICATION") == 0) {
259 dle->program_is_application_api=1;
260 skip_whitespace(s, ch); /* find dumper name */
262 goto err; /* no program */
264 dle->program = s - 1;
265 skip_non_whitespace(s, ch);
269 dle->program = stralloc(dle->program);
271 skip_whitespace(s, ch); /* find the disk name */
273 err_extra = stralloc(_("no disk name"));
274 goto err; /* no disk name */
281 skip_quoted_string(s, ch);
282 s[-1] = '\0'; /* terminate the disk name */
283 qdisk = stralloc(fp);
284 dle->disk = unquote_string(qdisk);
286 skip_whitespace(s, ch); /* find the device or level */
288 err_extra = stralloc(_("bad level"));
291 if(!isdigit((int)s[-1])) {
293 skip_quoted_string(s, ch);
295 qamdevice = stralloc(fp);
296 dle->device = unquote_string(qamdevice);
297 skip_whitespace(s, ch); /* find level number */
300 dle->device = stralloc(dle->disk);
301 qamdevice = stralloc(qdisk);
304 /* find the level number */
305 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
306 err_extra = stralloc(_("bad level"));
307 goto err; /* bad level */
309 if (level < 0 || level >= DUMP_LEVELS) {
310 err_extra = stralloc(_("bad level"));
314 dle->level = g_slist_append(dle->level, GINT_TO_POINTER(level));
316 skip_whitespace(s, ch); /* find the dump date */
318 err_extra = stralloc(_("no dumpdate"));
319 goto err; /* no dumpdate */
322 skip_non_whitespace(s, ch);
324 (void)dumpdate; /* XXX: Set but not used */
326 dle->spindle = 0; /* default spindle */
328 skip_whitespace(s, ch); /* find the spindle */
330 if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
331 err_extra = stralloc(_("bad spindle"));
332 goto err; /* bad spindle */
336 skip_whitespace(s, ch); /* find the parameters */
338 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
346 if(strncmp_const(s-1, "exclude-file=") == 0) {
347 qlist = unquote_string(s+12);
349 append_sl(dle->exclude_file, qlist);
351 } else if(strncmp_const(s-1, "exclude-list=") == 0) {
352 qlist = unquote_string(s+12);
354 append_sl(dle->exclude_list, qlist);
356 } else if(strncmp_const(s-1, "include-file=") == 0) {
357 qlist = unquote_string(s+12);
359 append_sl(dle->include_file, qlist);
361 } else if(strncmp_const(s-1, "include-list=") == 0) {
362 qlist = unquote_string(s+12);
364 append_sl(dle->include_list, qlist);
367 err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
368 goto err; /* should have gotten to end */
370 skip_quoted_string(s, ch);
371 skip_whitespace(s, ch); /* find the inclusion list */
379 dle_add_diskest(dle);
382 if (g_options == NULL) {
383 g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
384 error(_("Missing OPTIONS line in sendsize input\n"));
389 if (am_has_feature(g_options->features, fe_req_xml)) {
393 dles = amxml_parse_node_FILE(stdin, &errmsg);
398 for (dle = dles; dle != NULL; dle = dle->next) {
399 dle_add_diskest(dle);
403 if (amandates_started) {
406 amandates_started = FALSE;
409 for(est = est_list; est != NULL; est = est->next) {
410 run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, g_options, est->dle,
420 * See if we need to wait for a child before we can do anything
425 amwait_t child_status;
428 dbprintf(_("waiting for any estimate child: %d running\n"),
430 child_pid = wait(&child_status);
431 if(child_pid == -1) {
432 error(_("wait failed: %s"), strerror(errno));
436 if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
437 char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
438 char *child_status_str = str_exit_status(child_name, child_status);
439 dbprintf("%s\n", child_status_str);
440 amfree(child_status_str);
445 * Find the child and mark it done.
447 for(est = est_list; est != NULL; est = est->next) {
448 if(est->child == child_pid) {
453 dbprintf(_("unexpected child %ld\n"), (long)child_pid);
458 run_client_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
463 * If we are already running the maximum number of children
464 * go back and wait until one of them finishes.
466 if(dumpsrunning >= g_options->maxdumps) {
469 continue; /* have to wait first */
472 * Find a new child to start.
474 for(est = est_list; est != NULL; est = est->next) {
476 done = 0; /* more to do */
478 if(est->child != 0 || est->done) {
479 continue; /* child is running or done */
482 * Make sure there is no spindle conflict.
484 if(est->dle->spindle != -1) {
485 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
486 if(est1->child == 0 || est == est1 || est1->done) {
488 * Ignore anything not yet started, ourself,
489 * and anything completed.
493 if(est1->dle->spindle == est->dle->spindle) {
494 break; /* oops -- they match */
498 continue; /* spindle conflict */
501 break; /* start this estimate */
504 if(dumpsrunning > 0) {
505 need_wait = 1; /* nothing to do but wait */
509 run_client_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
512 if((est->child = fork()) == 0) {
513 calc_estimates(est); /* child does the estimate */
515 } else if(est->child == -1) {
516 error(_("calc_estimates fork failed: %s"), strerror(errno));
519 dumpsrunning++; /* parent */
523 for(est = est_list; est != NULL; est = est->next) {
524 run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, g_options, est->dle,
529 for(est = est_list; est != NULL; est = est->next) {
535 amfree(our_feature_string);
536 am_release_feature_set(our_features);
538 free_g_options(g_options);
544 g_printf(_("FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
545 dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
548 g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
549 dbprintf(_("REQ packet is bogus\n"));
552 free_g_options(g_options);
563 disk_estimates_t *newp, *curp;
568 char *amandates_file;
569 gboolean need_amandates = FALSE;
573 g_printf(_("ERROR Missing level in request\n"));
577 /* should we use amandates for this? */
579 need_amandates = TRUE;
580 if (strcmp(dle->program, "GNUTAR") == 0) {
581 /* GNUTAR only needs amandates if gnutar_list_dir is NULL */
582 char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
583 if (!gnutar_list_dir || !*gnutar_list_dir)
584 need_amandates = TRUE;
587 /* start amandates here, before adding this DLE to est_list, in case
588 * we encounter an error. */
589 if (need_amandates) {
590 if (!amandates_started) {
591 amandates_file = getconf_str(CNF_AMANDATES);
592 if(!start_amandates(amandates_file, 0)) {
593 char *errstr = strerror(errno);
594 char *qamname = quote_string(dle->disk);
595 char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
596 char *qerrmsg = quote_string(errmsg);
597 g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
603 amandates_started = TRUE;
607 while (level != NULL) {
608 if (GPOINTER_TO_INT(level->data) < 0)
609 level->data = GINT_TO_POINTER(0);
610 if (GPOINTER_TO_INT(level->data) >= DUMP_LEVELS)
611 level->data = GINT_TO_POINTER(DUMP_LEVELS - 1);
612 level = g_slist_next(level);
615 for(curp = est_list; curp != NULL; curp = curp->next) {
616 if(strcmp(curp->dle->disk, dle->disk) == 0) {
617 /* already have disk info, just note the level request */
619 while (level != NULL) {
620 curp->est[GPOINTER_TO_INT(level->data)].needestimate = 1;
621 level = g_slist_next(level);
628 newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
629 memset(newp, 0, SIZEOF(*newp));
630 newp->next = est_list;
632 newp->qamname = quote_string(dle->disk);
634 newp->qamdevice = quote_string(dle->device);
635 newp->dirname = amname_to_dirname(dle->device);
636 newp->qdirname = quote_string(newp->dirname);
638 newp->qamdevice = stralloc("");
639 newp->dirname = stralloc("");
640 newp->qdirname = stralloc("");
643 while (level != NULL) {
644 newp->est[GPOINTER_TO_INT(level->data)].needestimate = 1;
645 level = g_slist_next(level);
649 /* fill in dump-since dates */
650 if (need_amandates) {
651 amdp = amandates_lookup(newp->dle->disk);
653 newp->est[0].dumpsince = EPOCH;
654 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
655 dumpdate = amdp->dates[dumplev];
656 for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
657 if(dumpdate > newp->est[estlev].dumpsince)
658 newp->est[estlev].dumpsince = dumpdate;
662 /* just zero everything out */
663 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
664 newp->est[dumplev].dumpsince = 0;
672 disk_estimates_t * est)
674 amfree(est->qamname);
675 amfree(est->qamdevice);
676 amfree(est->dirname);
677 amfree(est->qdirname);
684 * ------------------------------------------------------------------------
690 disk_estimates_t * est)
692 dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
693 est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
695 if(est->dle->program_is_application_api == 1)
696 application_api_calc_estimate(est);
698 if(est->dle->calcsize == 1)
699 if (est->dle->device[0] == '/' && est->dle->device[1] == '/')
700 dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
701 est->qamname, est->qdirname);
703 generic_calc_estimates(est);
705 #ifndef USE_GENERIC_CALCSIZE
706 if(strcmp(est->dle->program, "DUMP") == 0)
707 dump_calc_estimates(est);
711 if (strcmp(est->dle->program, "GNUTAR") == 0 &&
712 est->dle->device[0] == '/' && est->dle->device[1] == '/')
713 smbtar_calc_estimates(est);
717 if (strcmp(est->dle->program, "GNUTAR") == 0)
718 gnutar_calc_estimates(est);
721 dbprintf(_("Invalid program: %s %s %s\n"),
722 est->qamname, est->qdirname, est->dle->program);
724 dbprintf(_("done with amname %s dirname %s spindle %d\n"),
725 est->qamname, est->qdirname, est->dle->spindle);
729 * ------------------------------------------------------------------------
733 /* local functions */
734 off_t getsize_dump(dle_t *dle, int level, char **errmsg);
735 off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
736 off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
737 off_t getsize_star(dle_t *dle, int level, time_t dumpsince, char **errmsg);
738 off_t getsize_application_api(disk_estimates_t *est, int nb_level,
739 int *levels, backup_support_option_t *bsu);
740 off_t handle_dumpline(char *str);
741 double first_num(char *str);
744 application_api_calc_estimate(
745 disk_estimates_t * est)
749 int levels[DUMP_LEVELS];
751 backup_support_option_t *bsu;
754 bsu = backup_support_option(est->dle->program, g_options, est->dle->disk,
755 est->dle->device, &errarray);
758 for (i=0; i < errarray->len; i++) {
762 line = g_ptr_array_index(errarray, i);
763 if(am_has_feature(g_options->features,
764 fe_rep_sendsize_quoted_error)) {
765 errmsg = g_strdup_printf(_("Application '%s': %s"),
766 est->dle->program, line);
767 qerrmsg = quote_string(errmsg);
768 for (level = 0; level < DUMP_LEVELS; level++) {
769 if (est->est[level].needestimate) {
770 g_printf(_("%s %d ERROR %s\n"),
771 est->dle->disk, level, qerrmsg);
772 dbprintf(_("%s %d ERROR A %s\n"),
773 est->qamname, level, qerrmsg);
780 if (i == 0) { /* nothing in errarray */
783 errmsg = g_strdup_printf(
784 _("Application '%s': cannon execute support command"),
786 qerrmsg = quote_string(errmsg);
787 for (level = 0; level < DUMP_LEVELS; level++) {
788 if (est->est[level].needestimate) {
789 g_printf(_("%s %d ERROR %s\n"),
790 est->dle->disk, level, qerrmsg);
791 dbprintf(_("%s %d ERROR %s\n"),
792 est->qamname, level, qerrmsg);
798 for (level = 0; level < DUMP_LEVELS; level++) {
799 est->est[level].needestimate = 0;
801 g_ptr_array_free(errarray, TRUE);
804 for(level = 0; level < DUMP_LEVELS; level++) {
805 if (est->est[level].needestimate) {
806 if (level > bsu->max_level) {
807 /* planner will not even consider this level */
808 g_printf("%s %d SIZE %lld\n", est->qamname, level,
810 est->est[level].needestimate = 0;
811 } else if (est->dle->estimate == ES_SERVER) {
812 /* planner will consider this level, */
813 /* but use a server-side estimate */
814 g_printf("%s %d SIZE %lld\n", est->qamname, level,
816 est->est[level].needestimate = 0;
818 levels[nb_level++] = level;
826 if (bsu->multi_estimate) {
827 for (i=0;i<nb_level;i++) {
828 dbprintf(_("getting size via application API for %s %s level %d\n"),
829 est->qamname, est->qamdevice, levels[i]);
831 getsize_application_api(est, nb_level, levels, bsu);
834 for(level = 0; level < DUMP_LEVELS; level++) {
835 if (est->est[level].needestimate) {
837 _("getting size via application API for %s %s level %d\n"),
838 est->qamname, est->qamdevice, level);
840 getsize_application_api(est, 1, levels, bsu);
848 generic_calc_estimates(
849 disk_estimates_t * est)
851 int pipefd = -1, nullfd = -1;
854 char *my_argv[DUMP_LEVELS*2+22];
855 char number[NUM_STR_SIZE];
856 int i, level, my_argc;
860 char *file_exclude = NULL;
861 char *file_include = NULL;
863 FILE *dumpout = NULL;
864 off_t size = (off_t)1;
867 amwait_t wait_status;
868 char *errmsg = NULL, *qerrmsg;
869 char tmppath[PATH_MAX];
872 cmd = vstralloc(amlibexecdir, "/", "calcsize", versionsuffix(), NULL);
876 my_argv[my_argc++] = stralloc("calcsize");
877 if (g_options->config)
878 my_argv[my_argc++] = stralloc(g_options->config);
880 my_argv[my_argc++] = stralloc("NOCONFIG");
882 my_argv[my_argc++] = stralloc(est->dle->program);
883 canonicalize_pathname(est->dle->disk, tmppath);
884 my_argv[my_argc++] = stralloc(tmppath);
885 canonicalize_pathname(est->dirname, tmppath);
886 my_argv[my_argc++] = stralloc(tmppath);
888 if (est->dle->exclude_file)
889 nb_exclude += est->dle->exclude_file->nb_element;
890 if (est->dle->exclude_list)
891 nb_exclude += est->dle->exclude_list->nb_element;
892 if (est->dle->include_file)
893 nb_include += est->dle->include_file->nb_element;
894 if (est->dle->include_list)
895 nb_include += est->dle->include_list->nb_element;
898 file_exclude = build_exclude(est->dle, 0);
900 file_include = build_include(est->dle, 0);
903 my_argv[my_argc++] = stralloc("-X");
904 my_argv[my_argc++] = file_exclude;
908 my_argv[my_argc++] = stralloc("-I");
909 my_argv[my_argc++] = file_include;
911 start_time = curclock();
913 cmdline = stralloc(my_argv[0]);
914 for(i = 1; i < my_argc; i++)
915 cmdline = vstrextend(&cmdline, " ", my_argv[i], NULL);
916 dbprintf(_("running: \"%s\"\n"), cmdline);
919 for(level = 0; level < DUMP_LEVELS; level++) {
920 if(est->est[level].needestimate) {
921 g_snprintf(number, SIZEOF(number), "%d", level);
922 my_argv[my_argc++] = stralloc(number);
923 dbprintf(" %s", number);
924 g_snprintf(number, SIZEOF(number),
925 "%ld", (long)est->est[level].dumpsince);
926 my_argv[my_argc++] = stralloc(number);
927 dbprintf(" %s", number);
930 my_argv[my_argc] = NULL;
933 fflush(stderr); fflush(stdout);
935 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
936 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
938 dbprintf("%s\n", errmsg);
942 calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
943 &nullfd, &nullfd, &pipefd, my_argv);
946 dumpout = fdopen(pipefd,"r");
948 error(_("Can't fdopen: %s"), strerror(errno));
951 match_expr = vstralloc(" %d SIZE %lld", NULL);
952 len = strlen(est->qamname);
953 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
954 long long size_ = (long long)0;
955 if (line[0] == '\0' || (int)strlen(line) <= len)
957 /* Don't use sscanf for est->qamname because it can have a '%'. */
958 if (strncmp(line, est->qamname, len) == 0 &&
959 sscanf(line+len, match_expr, &level, &size_) == 2) {
960 g_printf("%s\n", line); /* write to amandad */
961 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
970 dbprintf(_("waiting for %s %s child (pid=%d)\n"),
971 my_argv[0], est->qamdevice, (int)calcpid);
972 waitpid(calcpid, &wait_status, 0);
973 if (WIFSIGNALED(wait_status)) {
974 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
975 "calcsize", WTERMSIG(wait_status),
977 } else if (WIFEXITED(wait_status)) {
978 if (WEXITSTATUS(wait_status) != 0) {
979 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
980 "calcsize", WEXITSTATUS(wait_status),
986 errmsg = vstrallocf(_("%s got bad exit: see %s"),
989 dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
990 my_argv[0], est->qamdevice,
991 (int)calcpid, WEXITSTATUS(wait_status));
993 dbprintf(_(".....\n"));
994 dbprintf(_("estimate time for %s: %s\n"),
996 walltime_str(timessub(curclock(), start_time)));
999 if (errmsg && errmsg[0] != '\0') {
1000 if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
1001 qerrmsg = quote_string(errmsg);
1002 dbprintf(_("errmsg is %s\n"), errmsg);
1003 g_printf("%s %d ERROR %s\n",
1004 est->qamname, 0, qerrmsg);
1009 for(i = 0; i < my_argc; i++) {
1017 dump_calc_estimates(
1018 disk_estimates_t * est)
1022 char *errmsg=NULL, *qerrmsg;
1024 for(level = 0; level < DUMP_LEVELS; level++) {
1025 if(est->est[level].needestimate) {
1026 dbprintf(_("getting size via dump for %s level %d\n"),
1027 est->qamname, level);
1028 size = getsize_dump(est->dle, level, &errmsg);
1032 g_printf(_("%s %d SIZE %lld\n"),
1033 est->qamname, level, (long long)size);
1034 if (errmsg && errmsg[0] != '\0') {
1035 if(am_has_feature(g_options->features,
1036 fe_rep_sendsize_quoted_error)) {
1037 qerrmsg = quote_string(errmsg);
1038 dbprintf(_("errmsg is %s\n"), errmsg);
1039 g_printf("%s %d ERROR %s\n",
1040 est->qamname, level, qerrmsg);
1047 amfunlock(1, "size");
1054 smbtar_calc_estimates(
1055 disk_estimates_t * est)
1059 char *errmsg = NULL, *qerrmsg;
1061 for(level = 0; level < DUMP_LEVELS; level++) {
1062 if(est->est[level].needestimate) {
1063 dbprintf(_("getting size via smbclient for %s level %d\n"),
1064 est->qamname, level);
1065 size = getsize_smbtar(est->dle, level, &errmsg);
1069 g_printf(_("%s %d SIZE %lld\n"),
1070 est->qamname, level, (long long)size);
1071 if (errmsg && errmsg[0] != '\0') {
1072 if(am_has_feature(g_options->features,
1073 fe_rep_sendsize_quoted_error)) {
1074 qerrmsg = quote_string(errmsg);
1075 dbprintf(_("errmsg is %s\n"), errmsg);
1076 g_printf("%s %d ERROR %s\n",
1077 est->qamname, level, qerrmsg);
1084 amfunlock(1, "size");
1092 gnutar_calc_estimates(
1093 disk_estimates_t * est)
1097 char *errmsg = NULL, *qerrmsg;
1099 for(level = 0; level < DUMP_LEVELS; level++) {
1100 if (est->est[level].needestimate) {
1101 dbprintf(_("getting size via gnutar for %s level %d\n"),
1102 est->qamname, level);
1103 size = getsize_gnutar(est->dle, level,
1104 est->est[level].dumpsince,
1109 g_printf(_("%s %d SIZE %lld\n"),
1110 est->qamname, level, (long long)size);
1111 if (errmsg && errmsg[0] != '\0') {
1112 if(am_has_feature(g_options->features,
1113 fe_rep_sendsize_quoted_error)) {
1114 qerrmsg = quote_string(errmsg);
1115 dbprintf(_("errmsg is %s\n"), errmsg);
1116 g_printf(_("%s %d ERROR %s\n"),
1117 est->qamname, level, qerrmsg);
1124 amfunlock(1, "size");
1130 typedef struct regex_s {
1136 regex_scale_t re_size[] = {
1138 {" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1139 {" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1140 {" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
1141 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
1142 {"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
1143 {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1144 {"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
1145 {"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
1146 {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
1147 {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
1148 {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1150 {"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
1151 {"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
1152 {"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
1153 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1155 #ifdef HAVE_DUMP_ESTIMATE
1156 {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1157 /* DU 3.2g dump -E */
1158 {"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
1159 {"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
1164 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
1168 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
1169 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1173 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
1177 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
1181 #if SAMBA_VERSION >= 2
1182 #define SAMBA_DEBUG_LEVEL "0"
1183 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
1185 #define SAMBA_DEBUG_LEVEL "3"
1186 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
1200 int pipefd[2], nullfd, stdoutfd, killctl[2];
1204 char *dumpkeys = NULL;
1205 char *device = NULL;
1206 char *fstype = NULL;
1210 char *rundump_cmd = NULL;
1211 char level_str[NUM_STR_SIZE];
1214 char *qdisk = quote_string(dle->disk);
1217 amwait_t wait_status;
1218 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1223 return -2; /* planner will not even consider this level */
1224 if (dle->estimate == ES_SERVER)
1225 return -1; /* planner will consider this level, */
1226 /* but use a server-side estimate */
1228 g_snprintf(level_str, SIZEOF(level_str), "%d", level);
1230 device = amname_to_devname(dle->device);
1231 qdevice = quote_string(device);
1232 fstype = amname_to_fstype(dle->device);
1234 dbprintf(_("calculating for device %s with %s\n"),
1237 cmd = vstralloc(amlibexecdir, "/rundump", versionsuffix(), NULL);
1238 rundump_cmd = stralloc(cmd);
1239 if (g_options->config)
1240 config = g_options->config;
1242 config = "NOCONFIG";
1243 if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1244 *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
1246 dbprintf("%s\n", *errmsg);
1248 amfree(rundump_cmd);
1255 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1256 if (pipe(pipefd) < 0) {
1257 *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
1259 dbprintf("%s\n", *errmsg);
1261 amfree(rundump_cmd);
1269 #ifdef XFSDUMP /* { */
1271 if (strcmp(fstype, "xfs") == 0)
1276 name = stralloc(" (xfsdump)");
1277 dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
1278 cmd, name, level_str, qdevice);
1282 #ifdef VXDUMP /* { */
1284 if (strcmp(fstype, "vxfs") == 0)
1290 name = stralloc(" (vxdump)");
1292 name = stralloc("");
1293 cmd = newstralloc(cmd, VXDUMP);
1294 config = skip_argument;
1297 dumpkeys = vstralloc(level_str, "s", "f", NULL);
1298 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1299 cmd, name, dumpkeys, qdevice);
1303 #ifdef VDUMP /* { */
1305 if (strcmp(fstype, "advfs") == 0)
1310 name = stralloc(" (vdump)");
1313 device = amname_to_dirname(amdevice);
1314 qdevice = quote_string(device);
1315 dumpkeys = vstralloc(level_str, "b", "f", NULL);
1316 dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1317 cmd, name, dumpkeys, qdevice);
1323 # ifdef USE_RUNDUMP /* { */
1324 # ifdef AIX_BACKUP /* { */
1325 name = stralloc(" (backup)");
1327 name = vstralloc(" (", DUMP, ")", NULL);
1330 name = stralloc("");
1331 cmd = newstralloc(cmd, DUMP);
1332 config = skip_argument;
1336 # ifdef AIX_BACKUP /* { */
1337 dumpkeys = vstralloc("-", level_str, "f", NULL);
1338 dbprintf(_("running \"%s%s %s - %s\"\n"),
1339 cmd, name, dumpkeys, qdevice);
1341 # ifdef HAVE_DUMP_ESTIMATE
1342 # define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1344 # define PARAM_DUMP_ESTIMATE ""
1346 # ifdef HAVE_HONOR_NODUMP
1347 # define PARAM_HONOR_NODUMP "h"
1349 # define PARAM_HONOR_NODUMP ""
1351 dumpkeys = vstralloc(level_str,
1352 PARAM_DUMP_ESTIMATE,
1356 # ifdef HAVE_DUMP_ESTIMATE
1357 stdoutfd = pipefd[1];
1360 # ifdef HAVE_HONOR_NODUMP /* { */
1361 dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1362 cmd, name, dumpkeys, qdevice);
1364 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1365 cmd, name, dumpkeys, qdevice);
1372 error(_("no dump program available"));
1376 if (pipe(killctl) < 0) {
1377 dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1378 /* Message will be printed later... */
1379 killctl[0] = killctl[1] = -1;
1382 start_time = curclock();
1383 switch(dumppid = fork()) {
1385 *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1387 dbprintf("%s\n", *errmsg);
1390 amfree(rundump_cmd);
1399 case 0: /* child process */
1402 else if (killctl[0] == -1 || killctl[1] == -1)
1403 dbprintf(_("Trying without killpgrp\n"));
1407 dbprintf(_("fork failed, trying without killpgrp\n"));
1413 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp",
1414 versionsuffix(), NULL);
1415 dbprintf(_("running %s\n"), killpgrp_cmd);
1416 dup2(killctl[0], 0);
1423 if (g_options->config)
1424 config = g_options->config;
1426 config = "NOCONFIG";
1428 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1430 dbprintf(_("cannot execute %s: %s\n"),
1431 killpgrp_cmd, strerror(errno));
1435 case 0: /* child process */
1444 if (killctl[0] != -1)
1446 if (killctl[1] != -1)
1452 if (strcmp(fstype, "xfs") == 0)
1457 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1458 level_str, "-", device, (char *)0, safe_env());
1460 execle(cmd, "xfsdump", "-F", "-J", "-l",
1461 level_str, "-", device, (char *)0, safe_env());
1466 if (strcmp(fstype, "vxfs") == 0)
1471 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1472 "-", device, (char *)0, safe_env());
1474 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1475 device, (char *)0, safe_env());
1480 if (strcmp(fstype, "advfs") == 0)
1485 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1486 device, (char *)0, safe_env());
1488 execle(cmd, "vdump", dumpkeys, "60", "-",
1489 device, (char *)0, safe_env());
1495 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1496 device, (char *)0, safe_env());
1498 execle(cmd, "backup", dumpkeys, "-",
1499 device, (char *)0, safe_env());
1502 execle(cmd, "rundump", config, "dump", dumpkeys,
1503 #ifdef HAVE_HONOR_NODUMP
1506 "1048576", "-", device, (char *)0, safe_env());
1508 execle(cmd, "dump", dumpkeys,
1509 #ifdef HAVE_HONOR_NODUMP
1512 "1048576", "-", device, (char *)0, safe_env());
1517 error(_("exec %s failed or no dump program available: %s"),
1518 cmd, strerror(errno));
1524 amfree(rundump_cmd);
1527 if (killctl[0] != -1)
1529 dumpout = fdopen(pipefd[0],"r");
1531 error(_("Can't fdopen: %s"), strerror(errno));
1535 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1536 if (line[0] == '\0')
1538 dbprintf("%s\n", line);
1539 size = handle_dumpline(line);
1540 if(size > (off_t)-1) {
1542 while ((line = agets(dumpout)) != NULL) {
1543 if (line[0] != '\0')
1548 dbprintf("%s\n", line);
1555 dbprintf(".....\n");
1556 dbprintf(_("estimate time for %s level %d: %s\n"),
1559 walltime_str(timessub(curclock(), start_time)));
1560 if(size == (off_t)-1) {
1561 *errmsg = vstrallocf(_("no size line match in %s%s output"),
1563 dbprintf(_("%s for %s\n"),
1566 dbprintf(".....\n");
1567 dbprintf(_("Run %s%s manually to check for errors\n"),
1569 } else if(size == (off_t)0 && level == 0) {
1570 dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1571 cmd, name, dle->disk);
1572 dbprintf(".....\n");
1574 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1580 if (killctl[1] != -1) {
1581 dbprintf(_("asking killpgrp to terminate\n"));
1583 for(s = 5; s > 0; --s) {
1585 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1591 * First, try to kill the dump process nicely. If it ignores us
1592 * for several seconds, hit it harder.
1594 dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1595 if (kill(-dumppid, SIGTERM) == -1) {
1596 dbprintf(_("kill failed: %s\n"), strerror(errno));
1598 /* Now check whether it dies */
1599 for(s = 5; s > 0; --s) {
1601 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1605 dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1606 if (kill(-dumppid, SIGKILL) == -1) {
1607 dbprintf(_("kill failed: %s\n"), strerror(errno));
1609 for(s = 5; s > 0; --s) {
1611 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1615 dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1616 waitpid(dumppid, &wait_status, 0);
1617 if (WIFSIGNALED(wait_status)) {
1618 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1619 cmd, WTERMSIG(wait_status), dbfn());
1620 } else if (WIFEXITED(wait_status)) {
1621 if (WEXITSTATUS(wait_status) != 0) {
1622 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1623 cmd, WEXITSTATUS(wait_status), dbfn());
1628 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1631 dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1656 int pipefd = -1, nullfd = -1, passwdfd = -1;
1660 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1661 char *share = NULL, *subdir = NULL;
1668 char *error_pn = NULL;
1669 char *qdisk = quote_string(dle->disk);
1670 amwait_t wait_status;
1672 error_pn = stralloc2(get_pname(), "-smbclient");
1675 return -2; /* planner will not even consider this level */
1676 if (dle->estimate == ES_SERVER)
1677 return -1; /* planner will consider this level, */
1678 /* but use a server-side estimate */
1680 parsesharename(dle->device, &share, &subdir);
1684 set_pname(error_pn);
1686 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1689 if ((subdir) && (SAMBA_VERSION < 2)) {
1692 set_pname(error_pn);
1694 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1697 if ((user_and_password = findpass(share, &domain)) == NULL) {
1700 memset(domain, '\0', strlen(domain));
1703 set_pname(error_pn);
1705 error(_("cannot find password for %s"), dle->disk);
1708 lpass = strlen(user_and_password);
1709 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1710 memset(user_and_password, '\0', (size_t)lpass);
1711 amfree(user_and_password);
1713 memset(domain, '\0', strlen(domain));
1716 set_pname(error_pn);
1718 error(_("password field not \'user%%pass\' for %s"), dle->disk);
1722 pwtext_len = strlen(pwtext);
1723 if ((sharename = makesharename(share, 0)) == NULL) {
1724 memset(user_and_password, '\0', (size_t)lpass);
1725 amfree(user_and_password);
1727 memset(domain, '\0', strlen(domain));
1730 set_pname(error_pn);
1732 error(_("cannot make share name of %s"), share);
1735 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1736 memset(user_and_password, '\0', (size_t)lpass);
1737 amfree(user_and_password);
1739 memset(domain, '\0', strlen(domain));
1742 set_pname(error_pn);
1745 error(_("could not open /dev/null: %s\n"),
1750 #if SAMBA_VERSION >= 2
1752 tarkeys = "archive 0;recurse;du";
1754 tarkeys = "archive 1;recurse;du";
1757 tarkeys = "archive 0;recurse;dir";
1759 tarkeys = "archive 1;recurse;dir";
1762 start_time = curclock();
1764 if (pwtext_len > 0) {
1765 pw_fd_env = "PASSWD_FD";
1767 pw_fd_env = "dummy_PASSWD_FD";
1769 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
1770 &nullfd, &nullfd, &pipefd,
1771 pw_fd_env, &passwdfd,
1774 "-d", SAMBA_DEBUG_LEVEL,
1775 *user_and_password ? "-U" : skip_argument,
1776 *user_and_password ? user_and_password : skip_argument,
1778 domain ? "-W" : skip_argument,
1779 domain ? domain : skip_argument,
1780 #if SAMBA_VERSION >= 2
1781 subdir ? "-D" : skip_argument,
1782 subdir ? subdir : skip_argument,
1787 memset(domain, '\0', strlen(domain));
1791 if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
1792 int save_errno = errno;
1794 memset(user_and_password, '\0', (size_t)lpass);
1795 amfree(user_and_password);
1797 set_pname(error_pn);
1799 error(_("password write failed: %s"), strerror(save_errno));
1802 memset(user_and_password, '\0', (size_t)lpass);
1803 amfree(user_and_password);
1809 dumpout = fdopen(pipefd,"r");
1811 error(_("Can't fdopen: %s"), strerror(errno));
1815 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1816 if (line[0] == '\0')
1818 dbprintf("%s\n", line);
1819 size = handle_dumpline(line);
1822 while ((line = agets(dumpout)) != NULL) {
1823 if (line[0] != '\0')
1828 dbprintf("%s\n", line);
1835 dbprintf(".....\n");
1836 dbprintf(_("estimate time for %s level %d: %s\n"),
1839 walltime_str(timessub(curclock(), start_time)));
1840 if(size == (off_t)-1) {
1841 *errmsg = vstrallocf(_("no size line match in %s output"),
1843 dbprintf(_("%s for %s\n"),
1845 dbprintf(".....\n");
1846 } else if(size == (off_t)0 && level == 0) {
1847 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1848 SAMBA_CLIENT, dle->disk);
1849 dbprintf(".....\n");
1851 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1856 kill(-dumppid, SIGTERM);
1858 dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1859 waitpid(dumppid, &wait_status, 0);
1860 if (WIFSIGNALED(wait_status)) {
1861 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1862 SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
1863 } else if (WIFEXITED(wait_status)) {
1864 if (WEXITSTATUS(wait_status) != 0) {
1865 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1866 SAMBA_CLIENT, WEXITSTATUS(wait_status),
1872 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1873 SAMBA_CLIENT, dbfn());
1875 dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
1895 int pipefd = -1, nullfd = -1;
1897 off_t size = (off_t)-1;
1898 FILE *dumpout = NULL;
1899 char *incrname = NULL;
1900 char *basename = NULL;
1901 char *dirname = NULL;
1902 char *inputname = NULL;
1907 char dumptimestr[80];
1913 char *file_exclude = NULL;
1914 char *file_include = NULL;
1919 char *qdisk = quote_string(dle->disk);
1920 char *gnutar_list_dir;
1921 amwait_t wait_status;
1922 char tmppath[PATH_MAX];
1925 return -2; /* planner will not even consider this level */
1926 if (dle->estimate == ES_SERVER)
1927 return -1; /* planner will consider this level, */
1928 /* but use a server-side estimate */
1930 if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
1931 if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
1932 if(dle->include_file) nb_include += dle->include_file->nb_element;
1933 if(dle->include_list) nb_include += dle->include_list->nb_element;
1935 if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
1936 if(nb_include > 0) file_include = build_include(dle, 0);
1938 my_argv = alloc(SIZEOF(char *) * 22);
1941 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
1942 if (strlen(gnutar_list_dir) == 0)
1943 gnutar_list_dir = NULL;
1944 if (gnutar_list_dir) {
1945 char number[NUM_STR_SIZE];
1947 char *sdisk = sanitise_filename(dle->disk);
1949 basename = vstralloc(gnutar_list_dir,
1951 g_options->hostname,
1956 g_snprintf(number, SIZEOF(number), "%d", level);
1957 incrname = vstralloc(basename, "_", number, ".new", NULL);
1961 * Open the listed incremental file from the previous level. Search
1962 * backward until one is found. If none are found (which will also
1963 * be true for a level 0), arrange to read from /dev/null.
1967 while (infd == -1) {
1968 if (--baselevel >= 0) {
1969 g_snprintf(number, SIZEOF(number), "%d", baselevel);
1970 inputname = newvstralloc(inputname,
1971 basename, "_", number, NULL);
1973 inputname = newstralloc(inputname, "/dev/null");
1975 if ((infd = open(inputname, O_RDONLY)) == -1) {
1977 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
1978 inputname, strerror(errno));
1979 dbprintf("%s\n", *errmsg);
1980 if (baselevel < 0) {
1988 * Copy the previous listed incremental file to the new one.
1990 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1991 *errmsg = vstrallocf(_("opening %s: %s"),
1992 incrname, strerror(errno));
1993 dbprintf("%s\n", *errmsg);
1997 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1998 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
1999 *errmsg = vstrallocf(_("writing to %s: %s"),
2000 incrname, strerror(errno));
2001 dbprintf("%s\n", *errmsg);
2007 *errmsg = vstrallocf(_("reading from %s: %s"),
2008 inputname, strerror(errno));
2009 dbprintf("%s\n", *errmsg);
2013 if (close(infd) != 0) {
2014 *errmsg = vstrallocf(_("closing %s: %s"),
2015 inputname, strerror(errno));
2016 dbprintf("%s\n", *errmsg);
2019 if (close(outfd) != 0) {
2020 *errmsg = vstrallocf(_("closing %s: %s"),
2021 incrname, strerror(errno));
2022 dbprintf("%s\n", *errmsg);
2030 gmtm = gmtime(&dumpsince);
2031 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2032 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2033 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2034 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2036 dirname = amname_to_dirname(dle->device);
2038 cmd = vstralloc(amlibexecdir, "/", "runtar", versionsuffix(), NULL);
2039 my_argv[i++] = "runtar";
2040 if (g_options->config)
2041 my_argv[i++] = g_options->config;
2043 my_argv[i++] = "NOCONFIG";
2046 my_argv[i++] = GNUTAR;
2048 my_argv[i++] = "tar";
2050 my_argv[i++] = "--create";
2051 my_argv[i++] = "--file";
2052 my_argv[i++] = "/dev/null";
2053 /* use --numeric-owner for estimates, to reduce the number of user/group
2054 * lookups required */
2055 my_argv[i++] = "--numeric-owner";
2056 my_argv[i++] = "--directory";
2057 canonicalize_pathname(dirname, tmppath);
2058 my_argv[i++] = tmppath;
2059 my_argv[i++] = "--one-file-system";
2060 if (gnutar_list_dir) {
2061 my_argv[i++] = "--listed-incremental";
2062 my_argv[i++] = incrname;
2064 my_argv[i++] = "--incremental";
2065 my_argv[i++] = "--newer";
2066 my_argv[i++] = dumptimestr;
2068 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2069 /* --atime-preserve causes gnutar to call
2070 * utime() after reading files in order to
2071 * adjust their atime. However, utime()
2072 * updates the file's ctime, so incremental
2073 * dumps will think the file has changed. */
2074 my_argv[i++] = "--atime-preserve";
2076 my_argv[i++] = "--sparse";
2077 my_argv[i++] = "--ignore-failed-read";
2078 my_argv[i++] = "--totals";
2081 my_argv[i++] = "--exclude-from";
2082 my_argv[i++] = file_exclude;
2086 my_argv[i++] = "--files-from";
2087 my_argv[i++] = file_include;
2092 my_argv[i++] = NULL;
2094 start_time = curclock();
2096 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2097 *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2099 dbprintf("%s\n", *errmsg);
2103 dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
2104 &nullfd, &nullfd, &pipefd, my_argv);
2106 dumpout = fdopen(pipefd,"r");
2108 error(_("Can't fdopen: %s"), strerror(errno));
2112 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2113 if (line[0] == '\0')
2115 dbprintf("%s\n", line);
2116 size = handle_dumpline(line);
2117 if(size > (off_t)-1) {
2119 while ((line = agets(dumpout)) != NULL) {
2120 if (line[0] != '\0') {
2126 dbprintf("%s\n", line);
2134 dbprintf(".....\n");
2135 dbprintf(_("estimate time for %s level %d: %s\n"),
2138 walltime_str(timessub(curclock(), start_time)));
2139 if(size == (off_t)-1) {
2140 *errmsg = vstrallocf(_("no size line match in %s output"), my_argv[0]);
2141 dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2142 dbprintf(".....\n");
2143 } else if(size == (off_t)0 && level == 0) {
2144 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2145 my_argv[0], dle->disk);
2146 dbprintf(".....\n");
2148 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2153 kill(-dumppid, SIGTERM);
2155 dbprintf(_("waiting for %s \"%s\" child\n"), my_argv[0], qdisk);
2156 waitpid(dumppid, &wait_status, 0);
2157 if (WIFSIGNALED(wait_status)) {
2158 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2159 cmd, WTERMSIG(wait_status), dbfn());
2160 } else if (WIFEXITED(wait_status)) {
2161 if (WEXITSTATUS(wait_status) != 0) {
2162 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2163 cmd, WEXITSTATUS(wait_status), dbfn());
2168 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2171 dbprintf(_("after %s %s wait\n"), my_argv[0], qdisk);
2185 amfree(file_exclude);
2186 amfree(file_include);
2198 getsize_application_api(
2199 disk_estimates_t *est,
2202 backup_support_option_t *bsu)
2204 dle_t *dle = est->dle;
2205 int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
2207 off_t size = (off_t)-1;
2215 char *newoptstr = NULL;
2218 char *qdisk = quote_string(dle->disk);
2219 char *qamdevice = quote_string(dle->device);
2220 amwait_t wait_status;
2221 char levelstr[NUM_STR_SIZE];
2224 char *errmsg = NULL;
2226 cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
2229 k = application_property_argv_size(dle);
2230 for (scriptlist = dle->scriptlist; scriptlist != NULL;
2231 scriptlist = scriptlist->next) {
2232 script = (script_t *)scriptlist->data;
2233 if (script->result && script->result->proplist) {
2234 k += property_argv_size(script->result->proplist);
2237 argvchild = g_new0(char *, 16 + k + DUMP_LEVELS*2);
2238 argvchild[i++] = dle->program;
2239 argvchild[i++] = "estimate";
2240 if (bsu->message_line == 1) {
2241 argvchild[i++] = "--message";
2242 argvchild[i++] = "line";
2244 if (g_options->config && bsu->config == 1) {
2245 argvchild[i++] = "--config";
2246 argvchild[i++] = g_options->config;
2248 if (g_options->hostname && bsu->host == 1) {
2249 argvchild[i++] = "--host";
2250 argvchild[i++] = g_options->hostname;
2252 argvchild[i++] = "--device";
2253 argvchild[i++] = dle->device;
2254 if (dle->disk && bsu->disk == 1) {
2255 argvchild[i++] = "--disk";
2256 argvchild[i++] = dle->disk;
2258 for (j=0; j < nb_level; j++) {
2259 argvchild[i++] = "--level";
2260 g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
2261 argvchild[i++] = stralloc(levelstr);
2263 if (dle->calcsize && bsu->calcsize) {
2264 argvchild[i++] = "--calcsize";
2267 i += application_property_add_to_argv(&argvchild[i], dle, bsu);
2269 for (scriptlist = dle->scriptlist; scriptlist != NULL;
2270 scriptlist = scriptlist->next) {
2271 script = (script_t *)scriptlist->data;
2272 if (script->result && script->result->proplist) {
2273 i += property_add_to_argv(&argvchild[i],
2274 script->result->proplist);
2278 argvchild[i] = NULL;
2280 cmdline = stralloc(cmd);
2281 for(j = 1; j < i; j++)
2282 cmdline = vstrextend(&cmdline, " ", argvchild[j], NULL);
2283 dbprintf("running: \"%s\"\n", cmdline);
2286 if (pipe(pipeerrfd) < 0) {
2287 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2292 if (pipe(pipeinfd) < 0) {
2293 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2298 if (pipe(pipeoutfd) < 0) {
2299 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2304 start_time = curclock();
2306 switch(dumppid = fork()) {
2313 dup2(pipeinfd[0], 0);
2314 dup2(pipeoutfd[1], 1);
2315 dup2(pipeerrfd[1], 2);
2316 aclose(pipeinfd[1]);
2317 aclose(pipeoutfd[0]);
2318 aclose(pipeerrfd[0]);
2321 execve(cmd, argvchild, safe_env());
2322 error(_("exec %s failed: %s"), cmd, strerror(errno));
2327 aclose(pipeinfd[0]);
2328 aclose(pipeoutfd[1]);
2329 aclose(pipeerrfd[1]);
2330 aclose(pipeinfd[1]);
2332 dumpout = fdopen(pipeoutfd[0],"r");
2334 error(_("Can't fdopen: %s"), strerror(errno));
2338 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2339 long long size1_ = (long long)0;
2340 long long size2_ = (long long)0;
2342 if (line[0] == '\0')
2344 dbprintf("%s\n", line);
2345 if (strncmp(line,"ERROR ", 6) == 0) {
2346 char *errmsg, *qerrmsg;
2348 errmsg = stralloc(line+6);
2349 qerrmsg = quote_string(errmsg);
2350 dbprintf(_("errmsg is %s\n"), errmsg);
2351 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2355 i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
2357 i = sscanf(line, "%lld %lld", &size1_, &size2_);
2360 char *errmsg, *qerrmsg;
2362 errmsg = vstrallocf(_("bad line %s"), line);
2363 qerrmsg = quote_string(errmsg);
2364 dbprintf(_("errmsg is %s\n"), errmsg);
2365 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2370 size1 = (off_t)size1_;
2371 size2 = (off_t)size2_;
2372 if (size1 <= 0 || size2 <=0)
2374 else if (size1 * size2 > 0)
2375 size = size1 * size2;
2376 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2380 g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
2384 dumperr = fdopen(pipeerrfd[0],"r");
2386 error(_("Can't fdopen: %s"), strerror(errno));
2390 while ((line = agets(dumperr)) != NULL) {
2391 if (strlen(line) > 0) {
2392 char *err = g_strdup_printf(_("Application '%s': %s"),
2393 dle->program, line);
2394 char *qerr = quote_string(err);
2395 for (j=0; j < nb_level; j++) {
2396 fprintf(stdout, "%s %d ERROR %s\n",
2397 est->qamname, levels[j], qerr);
2399 dbprintf("ERROR %s", qerr);
2406 dbprintf(".....\n");
2407 if (nb_level == 1) {
2408 dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
2409 levels[0], walltime_str(timessub(curclock(), start_time)));
2411 dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
2412 walltime_str(timessub(curclock(), start_time)));
2415 kill(-dumppid, SIGTERM);
2417 dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2418 waitpid(dumppid, &wait_status, 0);
2419 if (WIFSIGNALED(wait_status)) {
2420 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2421 cmd, WTERMSIG(wait_status), dbfn());
2422 } else if (WIFEXITED(wait_status)) {
2423 if (WEXITSTATUS(wait_status) != 0) {
2424 errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2425 WEXITSTATUS(wait_status), dbfn());
2430 errmsg = vstrallocf(_("%s got bad exit: see %s"),
2433 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2445 char *qerrmsg = quote_string(errmsg);
2446 dbprintf(_("errmsg is %s\n"), errmsg);
2447 for (j=0; j < nb_level; j++) {
2448 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
2458 * Returns the value of the first integer in a string.
2470 while(ch && !isdigit(ch)) ch = *str++;
2472 while(isdigit(ch) || (ch == '.')) ch = *str++;
2481 * Checks the dump output line against the error and size regex tables.
2491 /* check for size match */
2493 for(rp = re_size; rp->regex != NULL; rp++) {
2494 if(match(rp->regex, str)) {
2495 size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2497 size = 1.0; /* found on NeXT -- sigh */