2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
4 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
28 * $Id: sendsize.c 10421 2008-03-06 18:48:30Z martineau $
30 * send estimated backup sizes using dump
35 #include "pipespawn.h"
36 #include "amfeatures.h"
37 #include "amandates.h"
41 #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 {
82 int server; /* server can do estimate */
85 typedef struct disk_estimates_s {
86 struct disk_estimates_s *next;
94 level_estimate_t est[DUMP_LEVELS];
97 disk_estimates_t *est_list;
99 static am_feature_t *our_features = NULL;
100 static char *our_feature_string = NULL;
101 static g_option_t *g_options = NULL;
102 static gboolean amandates_started = FALSE;
104 /* local functions */
105 int main(int argc, char **argv);
106 void dle_add_diskest(dle_t *dle);
107 void calc_estimates(disk_estimates_t *est);
108 void free_estimates(disk_estimates_t *est);
109 void dump_calc_estimates(disk_estimates_t *);
110 void star_calc_estimates(disk_estimates_t *);
111 void smbtar_calc_estimates(disk_estimates_t *);
112 void gnutar_calc_estimates(disk_estimates_t *);
113 void application_api_calc_estimate(disk_estimates_t *);
114 void generic_calc_estimates(disk_estimates_t *);
123 disk_estimates_t *est;
124 disk_estimates_t *est1;
125 disk_estimates_t *est_prev;
126 disk_estimates_t *est_next;
130 char *err_extra = NULL;
136 char *qamdevice = NULL;
141 (void)argc; /* Quiet unused parameter warning */
142 (void)argv; /* Quiet unused parameter warning */
144 if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
145 printf("sendsize-%s\n", VERSION);
152 * Configure program for internationalization:
153 * 1) Only set the message locale for now.
154 * 2) Set textdomain for all amanda related programs to "amanda"
155 * We don't want to be forced to support dozens of message catalogs.
157 setlocale(LC_MESSAGES, "C");
158 textdomain("amanda");
164 set_pname("sendsize");
166 /* Don't die when child closes pipe */
167 signal(SIGPIPE, SIG_IGN);
169 add_amanda_log_handler(amanda_log_stderr);
170 add_amanda_log_handler(amanda_log_syslog);
171 dbopen(DBG_SUBDIR_CLIENT);
173 dbprintf(_("version %s\n"), VERSION);
175 our_features = am_init_feature_set();
176 our_feature_string = am_feature_to_string(our_features);
178 config_init(CONFIG_INIT_CLIENT, NULL);
179 /* (check for config errors comes later) */
181 check_running_as(RUNNING_AS_CLIENT_LOGIN);
183 /* handle all service requests */
185 for(; (line = agets(stdin)) != NULL; free(line)) {
188 if(strncmp_const(line, "OPTIONS ") == 0) {
189 g_options = parse_g_options(line+8, 1);
190 if(!g_options->hostname) {
191 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
192 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
193 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
196 g_printf("OPTIONS ");
197 if(am_has_feature(g_options->features, fe_rep_options_features)) {
198 g_printf("features=%s;", our_feature_string);
200 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
201 g_printf("maxdumps=%d;", g_options->maxdumps);
203 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
204 g_printf("hostname=%s;", g_options->hostname);
209 if (g_options->config) {
210 /* overlay this configuration on the existing (nameless) configuration */
211 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
214 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
217 /* check for any config errors now */
218 if (config_errors(&errlist) >= CFGERR_ERRORS) {
219 char *errstr = config_errors_to_error_string(errlist);
220 g_printf("%s\n", errstr);
225 if (am_has_feature(g_options->features, fe_req_xml)) {
235 skip_whitespace(s, ch); /* find the program name */
237 err_extra = stralloc(_("no program name"));
238 goto err; /* no program name */
240 dle->program = s - 1;
241 skip_non_whitespace(s, ch);
244 dle->program_is_application_api=0;
245 if(strncmp_const(dle->program, "CALCSIZE") == 0) {
246 skip_whitespace(s, ch); /* find the program name */
248 err_extra = stralloc(_("no program name"));
251 dle->estimatelist = g_slist_append(dle->estimatelist,
252 GINT_TO_POINTER(ES_CALCSIZE));
253 dle->program = s - 1;
254 skip_non_whitespace(s, ch);
256 if (strcmp(dle->program,"APPLICATION") == 0) {
257 dle->program_is_application_api=1;
258 skip_whitespace(s, ch); /* find dumper name */
260 goto err; /* no program */
262 dle->program = s - 1;
263 skip_non_whitespace(s, ch);
268 dle->estimatelist = g_slist_append(dle->estimatelist,
269 GINT_TO_POINTER(ES_CLIENT));
270 if (strcmp(dle->program,"APPLICATION") == 0) {
271 dle->program_is_application_api=1;
272 skip_whitespace(s, ch); /* find dumper name */
274 goto err; /* no program */
276 dle->program = s - 1;
277 skip_non_whitespace(s, ch);
281 dle->program = stralloc(dle->program);
283 skip_whitespace(s, ch); /* find the disk name */
285 err_extra = stralloc(_("no disk name"));
286 goto err; /* no disk name */
293 skip_quoted_string(s, ch);
294 s[-1] = '\0'; /* terminate the disk name */
295 qdisk = stralloc(fp);
296 dle->disk = unquote_string(qdisk);
298 skip_whitespace(s, ch); /* find the device or level */
300 err_extra = stralloc(_("bad level"));
303 if(!isdigit((int)s[-1])) {
305 skip_quoted_string(s, ch);
307 qamdevice = stralloc(fp);
308 dle->device = unquote_string(qamdevice);
309 skip_whitespace(s, ch); /* find level number */
312 dle->device = stralloc(dle->disk);
313 qamdevice = stralloc(qdisk);
316 /* find the level number */
317 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
318 err_extra = stralloc(_("bad level"));
319 goto err; /* bad level */
321 if (level < 0 || level >= DUMP_LEVELS) {
322 err_extra = stralloc(_("bad level"));
326 alevel = g_new0(am_level_t, 1);
327 alevel->level = level;
328 dle->levellist = g_slist_append(dle->levellist, alevel);
330 skip_whitespace(s, ch); /* find the dump date */
332 err_extra = stralloc(_("no dumpdate"));
333 goto err; /* no dumpdate */
336 skip_non_whitespace(s, ch);
338 (void)dumpdate; /* XXX: Set but not used */
340 dle->spindle = 0; /* default spindle */
342 skip_whitespace(s, ch); /* find the spindle */
344 if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
345 err_extra = stralloc(_("bad spindle"));
346 goto err; /* bad spindle */
350 skip_whitespace(s, ch); /* find the parameters */
352 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
360 if(strncmp_const(s-1, "exclude-file=") == 0) {
361 qlist = unquote_string(s+12);
363 append_sl(dle->exclude_file, qlist);
365 } else if(strncmp_const(s-1, "exclude-list=") == 0) {
366 qlist = unquote_string(s+12);
368 append_sl(dle->exclude_list, qlist);
370 } else if(strncmp_const(s-1, "include-file=") == 0) {
371 qlist = unquote_string(s+12);
373 append_sl(dle->include_file, qlist);
375 } else if(strncmp_const(s-1, "include-list=") == 0) {
376 qlist = unquote_string(s+12);
378 append_sl(dle->include_list, qlist);
381 err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
382 goto err; /* should have gotten to end */
384 skip_quoted_string(s, ch);
385 skip_whitespace(s, ch); /* find the inclusion list */
393 dle_add_diskest(dle);
396 if (g_options == NULL) {
397 g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
398 error(_("Missing OPTIONS line in sendsize input\n"));
403 if (am_has_feature(g_options->features, fe_req_xml)) {
407 dles = amxml_parse_node_FILE(stdin, &errmsg);
412 for (dle = dles; dle != NULL; dle = dle->next) {
413 dle_add_diskest(dle);
417 if (amandates_started) {
420 amandates_started = FALSE;
424 for(est = est_list; est != NULL; est = est_next) {
425 int good = merge_dles_properties(est->dle, 0);
426 est_next = est->next;
428 if (est == est_list) {
431 est_prev->next = est_next;
437 for(est = est_list; est != NULL; est = est->next) {
438 run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, g_options, est->dle,
448 * See if we need to wait for a child before we can do anything
453 amwait_t child_status;
456 dbprintf(_("waiting for any estimate child: %d running\n"),
458 child_pid = wait(&child_status);
459 if(child_pid == -1) {
460 error(_("wait failed: %s"), strerror(errno));
464 if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
465 char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
466 char *child_status_str = str_exit_status(child_name, child_status);
467 dbprintf("%s\n", child_status_str);
468 amfree(child_status_str);
473 * Find the child and mark it done.
475 for(est = est_list; est != NULL; est = est->next) {
476 if(est->child == child_pid) {
481 dbprintf(_("unexpected child %ld\n"), (long)child_pid);
486 run_client_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
491 * If we are already running the maximum number of children
492 * go back and wait until one of them finishes.
494 if(dumpsrunning >= g_options->maxdumps) {
497 continue; /* have to wait first */
500 * Find a new child to start.
502 for(est = est_list; est != NULL; est = est->next) {
504 done = 0; /* more to do */
506 if(est->child != 0 || est->done) {
507 continue; /* child is running or done */
510 * Make sure there is no spindle conflict.
512 if(est->dle->spindle != -1) {
513 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
514 if(est1->child == 0 || est == est1 || est1->done) {
516 * Ignore anything not yet started, ourself,
517 * and anything completed.
521 if(est1->dle->spindle == est->dle->spindle) {
522 break; /* oops -- they match */
526 continue; /* spindle conflict */
529 break; /* start this estimate */
532 if(dumpsrunning > 0) {
533 need_wait = 1; /* nothing to do but wait */
537 run_client_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
540 if((est->child = fork()) == 0) {
541 calc_estimates(est); /* child does the estimate */
543 } else if(est->child == -1) {
544 error(_("calc_estimates fork failed: %s"), strerror(errno));
547 dumpsrunning++; /* parent */
551 for(est = est_list; est != NULL; est = est->next) {
552 run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, g_options, est->dle,
557 for(est = est_list; est != NULL; est = est->next) {
563 amfree(our_feature_string);
564 am_release_feature_set(our_features);
566 free_g_options(g_options);
572 g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
573 dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
576 g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
577 dbprintf(_("REQ packet is bogus\n"));
580 free_g_options(g_options);
591 disk_estimates_t *newp, *curp;
595 levellist_t levellist;
596 char *amandates_file;
597 gboolean need_amandates = FALSE;
600 if (dle->levellist == NULL) {
601 g_printf(_("ERROR Missing level in request\n"));
605 /* should we use amandates for this? */
606 for (el = dle->estimatelist; el != NULL; el=el->next) {
607 estimate_t estimate = (estimate_t)GPOINTER_TO_INT(el->data);
608 if (estimate == ES_CALCSIZE)
609 need_amandates = TRUE;
612 if (strcmp(dle->program, "GNUTAR") == 0) {
613 /* GNUTAR only needs amandates if gnutar_list_dir is NULL */
614 char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
615 if (!gnutar_list_dir || !*gnutar_list_dir)
616 need_amandates = TRUE;
619 /* start amandates here, before adding this DLE to est_list, in case
620 * we encounter an error. */
621 if (need_amandates) {
622 if (!amandates_started) {
623 amandates_file = getconf_str(CNF_AMANDATES);
624 if(!start_amandates(amandates_file, 0)) {
625 char *errstr = strerror(errno);
626 char *qamname = quote_string(dle->disk);
627 char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
628 char *qerrmsg = quote_string(errmsg);
629 g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
635 amandates_started = TRUE;
639 levellist = dle->levellist;
640 while (levellist != NULL) {
641 am_level_t *alevel = (am_level_t *)levellist->data;
642 if (alevel->level < 0)
644 if (alevel->level >= DUMP_LEVELS)
645 alevel->level = DUMP_LEVELS - 1;
646 levellist = g_slist_next(levellist);
649 for(curp = est_list; curp != NULL; curp = curp->next) {
650 if(strcmp(curp->dle->disk, dle->disk) == 0) {
651 /* already have disk info, just note the level request */
652 levellist = dle->levellist;
653 while (levellist != NULL) {
654 am_level_t *alevel = (am_level_t *)levellist->data;
655 int level = alevel->level;
656 curp->est[level].needestimate = 1;
657 curp->est[level].server = alevel->server;
658 levellist = g_slist_next(levellist);
665 newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
666 memset(newp, 0, SIZEOF(*newp));
667 newp->next = est_list;
669 newp->qamname = quote_string(dle->disk);
671 newp->qamdevice = quote_string(dle->device);
672 newp->dirname = amname_to_dirname(dle->device);
673 newp->qdirname = quote_string(newp->dirname);
675 newp->qamdevice = stralloc("");
676 newp->dirname = stralloc("");
677 newp->qdirname = stralloc("");
679 levellist = dle->levellist;
680 while (levellist != NULL) {
681 am_level_t *alevel = (am_level_t *)levellist->data;
682 newp->est[alevel->level].needestimate = 1;
683 newp->est[alevel->level].server = alevel->server;
684 levellist = g_slist_next(levellist);
688 /* fill in dump-since dates */
689 if (need_amandates) {
690 amdp = amandates_lookup(newp->dle->disk);
692 newp->est[0].dumpsince = EPOCH;
693 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
694 dumpdate = amdp->dates[dumplev];
695 for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
696 if(dumpdate > newp->est[estlev].dumpsince)
697 newp->est[estlev].dumpsince = dumpdate;
701 /* just zero everything out */
702 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
703 newp->est[dumplev].dumpsince = 0;
711 disk_estimates_t * est)
713 amfree(est->qamname);
714 amfree(est->qamdevice);
715 amfree(est->dirname);
716 amfree(est->qdirname);
723 * ------------------------------------------------------------------------
729 disk_estimates_t * est)
731 dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
732 est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
734 if(est->dle->program_is_application_api == 1)
735 application_api_calc_estimate(est);
740 estimate_t estimate_method = ES_ES;
741 estimate_t client_method = ES_ES;
743 /* find estimate method to use */
744 for (el = est->dle->estimatelist; el != NULL; el = el->next) {
745 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
746 if (estimate == ES_SERVER) {
747 if (estimate_method == ES_ES)
748 estimate_method = ES_SERVER;
750 if (estimate == ES_CLIENT ||
751 (estimate == ES_CALCSIZE &&
752 (est->dle->device[0] != '/' || est->dle->device[1] != '/'))) {
753 if (client_method == ES_ES)
754 client_method = estimate;
755 if (estimate_method == ES_ES)
756 estimate_method = estimate;
760 /* do server estimate */
761 if (estimate_method == ES_SERVER) {
762 for (level = 0; level < DUMP_LEVELS; level++) {
763 if (est->est[level].needestimate) {
764 if (est->est[level].server || client_method == ES_ES) {
765 g_printf(_("%s %d SIZE -1\n"), est->qamname, level);
766 est->est[level].needestimate = 0;
772 if (client_method == ES_ES && estimate_method != ES_SERVER) {
773 g_printf(_("%s %d SIZE -2\n"), est->qamname, 0);
774 dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
775 est->qamname, est->qdirname);
776 } else if (client_method == ES_CALCSIZE) {
777 generic_calc_estimates(est);
778 } else if (client_method == ES_CLIENT) {
779 #ifndef USE_GENERIC_CALCSIZE
780 if (strcmp(est->dle->program, "DUMP") == 0)
781 dump_calc_estimates(est);
785 if (strcmp(est->dle->program, "GNUTAR") == 0 &&
786 est->dle->device[0] == '/' && est->dle->device[1] == '/')
787 smbtar_calc_estimates(est);
791 if (strcmp(est->dle->program, "GNUTAR") == 0)
792 gnutar_calc_estimates(est);
795 dbprintf(_("Invalid program: %s %s %s\n"),
796 est->qamname, est->qdirname, est->dle->program);
800 dbprintf(_("done with amname %s dirname %s spindle %d\n"),
801 est->qamname, est->qdirname, est->dle->spindle);
805 * ------------------------------------------------------------------------
809 /* local functions */
810 off_t getsize_dump(dle_t *dle, int level, char **errmsg);
811 off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
812 off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
813 off_t getsize_application_api(disk_estimates_t *est, int nb_level,
814 int *levels, backup_support_option_t *bsu);
815 off_t handle_dumpline(char *str);
816 double first_num(char *str);
819 application_api_calc_estimate(
820 disk_estimates_t * est)
824 int levels[DUMP_LEVELS];
826 backup_support_option_t *bsu;
830 estimate_t estimate_method = ES_ES;
831 estimate_t client_method = ES_ES;
832 int has_calcsize = 0;
835 bsu = backup_support_option(est->dle->program, g_options, est->dle->disk,
836 est->dle->device, &errarray);
839 for (i=0; i < errarray->len; i++) {
843 line = g_ptr_array_index(errarray, i);
844 if(am_has_feature(g_options->features,
845 fe_rep_sendsize_quoted_error)) {
846 errmsg = g_strdup_printf(_("Application '%s': %s"),
847 est->dle->program, line);
848 qerrmsg = quote_string(errmsg);
849 for (level = 0; level < DUMP_LEVELS; level++) {
850 if (est->est[level].needestimate) {
851 g_printf(_("%s %d ERROR %s\n"),
852 est->dle->disk, level, qerrmsg);
853 dbprintf(_("%s %d ERROR %s\n"),
854 est->qamname, level, qerrmsg);
861 if (i == 0) { /* nothing in errarray */
864 errmsg = g_strdup_printf(
865 _("Application '%s': cannon execute support command"),
867 qerrmsg = quote_string(errmsg);
868 for (level = 0; level < DUMP_LEVELS; level++) {
869 if (est->est[level].needestimate) {
870 g_printf(_("%s %d ERROR %s\n"),
871 est->dle->disk, level, qerrmsg);
872 dbprintf(_("%s %d ERROR %s\n"),
873 est->qamname, level, qerrmsg);
879 for (level = 0; level < DUMP_LEVELS; level++) {
880 est->est[level].needestimate = 0;
882 g_ptr_array_free(errarray, TRUE);
885 if (est->dle->data_path == DATA_PATH_AMANDA &&
886 (bsu->data_path_set & DATA_PATH_AMANDA)==0) {
887 g_printf("%s %d ERROR application %s doesn't support amanda data-path\n", est->qamname, 0, est->dle->program);
891 if (est->dle->data_path == DATA_PATH_DIRECTTCP &&
892 (bsu->data_path_set & DATA_PATH_DIRECTTCP)==0) {
893 g_printf("%s %d ERROR application %s doesn't support directtcp data-path\n", est->qamname, 0, est->dle->program);
898 /* find estimate method to use */
899 for (el = est->dle->estimatelist; el != NULL; el = el->next) {
900 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
901 if (estimate == ES_CLIENT)
903 if (estimate == ES_CALCSIZE)
905 if (estimate == ES_SERVER) {
906 if (estimate_method == ES_ES)
907 estimate_method = ES_SERVER;
909 if ((estimate == ES_CLIENT && bsu->client_estimate) ||
910 (estimate == ES_CALCSIZE && bsu->calcsize)) {
911 if (client_method == ES_ES)
912 client_method = estimate;
913 if (estimate_method == ES_ES)
914 estimate_method = estimate;
918 for(level = 0; level < DUMP_LEVELS; level++) {
919 if (est->est[level].needestimate) {
920 if (level > bsu->max_level) {
921 /* planner will not even consider this level */
922 g_printf("%s %d SIZE %lld\n", est->qamname, level,
924 est->est[level].needestimate = 0;
925 dbprintf(_("Application '%s' can't estimate level %d\n"),
926 est->dle->program, level);
927 } else if (estimate_method == ES_ES) {
928 g_printf("%s %d SIZE %lld\n", est->qamname, level,
930 est->est[level].needestimate = 0;
931 if (am_has_feature(g_options->features,
932 fe_rep_sendsize_quoted_error)) {
933 char *errmsg, *qerrmsg;
934 if (has_client && !bsu->client_estimate &&
935 has_calcsize && !bsu->calcsize) {
936 errmsg = vstrallocf(_("Application '%s' can't do CLIENT or CALCSIZE estimate"),
938 } else if (has_client && !bsu->client_estimate) {
939 errmsg = vstrallocf(_("Application '%s' can't do CLIENT estimate"),
941 } else if (has_calcsize && !bsu->calcsize) {
942 errmsg = vstrallocf(_("Application '%s' can't do CALCSIZE estimate"),
945 errmsg = vstrallocf(_("Application '%s' can't do estimate"),
948 qerrmsg = quote_string(errmsg);
949 dbprintf(_("errmsg is %s\n"), errmsg);
950 g_printf("%s %d ERROR %s\n",
951 est->qamname, 0, qerrmsg);
955 } else if (estimate_method == ES_SERVER &&
956 (est->est[level].server || client_method == ES_ES)) {
957 /* planner will consider this level, */
958 /* but use a server-side estimate */
959 g_printf("%s %d SIZE -1\n", est->qamname, level);
960 est->est[level].needestimate = 0;
961 } else if (client_method == ES_CLIENT) {
962 levels[nb_level++] = level;
963 } else if (client_method == ES_CALCSIZE) {
964 levels[nb_level++] = level;
974 if (bsu->multi_estimate) {
975 for (i=0;i<nb_level;i++) {
976 dbprintf(_("getting size via application API for %s %s level %d\n"),
977 est->qamname, est->qamdevice, levels[i]);
979 getsize_application_api(est, nb_level, levels, bsu);
982 for(level = 0; level < DUMP_LEVELS; level++) {
983 if (est->est[level].needestimate) {
985 _("getting size via application API for %s %s level %d\n"),
986 est->qamname, est->qamdevice, level);
988 getsize_application_api(est, 1, levels, bsu);
998 generic_calc_estimates(
999 disk_estimates_t * est)
1001 int pipefd = -1, nullfd = -1;
1005 GPtrArray *argv_ptr = g_ptr_array_new();
1006 char number[NUM_STR_SIZE];
1012 char *file_exclude = NULL;
1013 char *file_include = NULL;
1015 FILE *dumpout = NULL;
1018 amwait_t wait_status;
1019 char *errmsg = NULL, *qerrmsg;
1020 char tmppath[PATH_MAX];
1023 cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
1025 g_ptr_array_add(argv_ptr, stralloc("calcsize"));
1026 if (g_options->config)
1027 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
1029 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
1031 g_ptr_array_add(argv_ptr, stralloc(est->dle->program));
1032 canonicalize_pathname(est->dle->disk, tmppath);
1033 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1034 canonicalize_pathname(est->dirname, tmppath);
1035 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1037 if (est->dle->exclude_file)
1038 nb_exclude += est->dle->exclude_file->nb_element;
1039 if (est->dle->exclude_list)
1040 nb_exclude += est->dle->exclude_list->nb_element;
1041 if (est->dle->include_file)
1042 nb_include += est->dle->include_file->nb_element;
1043 if (est->dle->include_list)
1044 nb_include += est->dle->include_list->nb_element;
1047 file_exclude = build_exclude(est->dle, 0);
1049 file_include = build_include(est->dle, 0);
1052 g_ptr_array_add(argv_ptr, stralloc("-X"));
1053 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1057 g_ptr_array_add(argv_ptr, stralloc("-I"));
1058 g_ptr_array_add(argv_ptr, stralloc(file_include));
1060 start_time = curclock();
1062 command = (char *)g_ptr_array_index(argv_ptr, 0);
1063 cmdline = stralloc(command);
1064 for(i = 1; i < argv_ptr->len-1; i++) {
1065 cmdline = vstrextend(&cmdline, " ",
1066 (char *)g_ptr_array_index(argv_ptr, i), NULL);
1068 dbprintf(_("running: \"%s\"\n"), cmdline);
1071 for(level = 0; level < DUMP_LEVELS; level++) {
1072 if(est->est[level].needestimate) {
1073 g_snprintf(number, SIZEOF(number), "%d", level);
1074 g_ptr_array_add(argv_ptr, stralloc(number));
1075 dbprintf(" %s", number);
1076 g_snprintf(number, SIZEOF(number),
1077 "%ld", (long)est->est[level].dumpsince);
1078 g_ptr_array_add(argv_ptr, stralloc(number));
1079 dbprintf(" %s", number);
1082 g_ptr_array_add(argv_ptr, NULL);
1085 fflush(stderr); fflush(stdout);
1087 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1088 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1090 dbprintf("%s\n", errmsg);
1094 calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
1095 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
1098 dumpout = fdopen(pipefd,"r");
1100 error(_("Can't fdopen: %s"), strerror(errno));
1103 match_expr = vstralloc(" %d SIZE %lld", NULL);
1104 len = strlen(est->qamname);
1105 for(; (line = agets(dumpout)) != NULL; free(line)) {
1106 long long size_ = (long long)0;
1107 if (line[0] == '\0' || (int)strlen(line) <= len)
1109 /* Don't use sscanf for est->qamname because it can have a '%'. */
1110 if (strncmp(line, est->qamname, len) == 0 &&
1111 sscanf(line+len, match_expr, &level, &size_) == 2) {
1112 g_printf("%s\n", line); /* write to amandad */
1113 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1121 dbprintf(_("waiting for %s %s child (pid=%d)\n"),
1122 command, est->qamdevice, (int)calcpid);
1123 waitpid(calcpid, &wait_status, 0);
1124 if (WIFSIGNALED(wait_status)) {
1125 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1126 "calcsize", WTERMSIG(wait_status),
1128 } else if (WIFEXITED(wait_status)) {
1129 if (WEXITSTATUS(wait_status) != 0) {
1130 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1131 "calcsize", WEXITSTATUS(wait_status),
1137 errmsg = vstrallocf(_("%s got bad exit: see %s"),
1138 "calcsize", dbfn());
1140 dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
1141 command, est->qamdevice,
1142 (int)calcpid, WEXITSTATUS(wait_status));
1144 dbprintf(_(".....\n"));
1145 dbprintf(_("estimate time for %s: %s\n"),
1147 walltime_str(timessub(curclock(), start_time)));
1150 if (errmsg && errmsg[0] != '\0') {
1151 if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
1152 qerrmsg = quote_string(errmsg);
1153 dbprintf(_("errmsg is %s\n"), errmsg);
1154 g_printf("%s %d ERROR %s\n",
1155 est->qamname, 0, qerrmsg);
1160 g_ptr_array_free_full(argv_ptr);
1166 dump_calc_estimates(
1167 disk_estimates_t * est)
1171 char *errmsg=NULL, *qerrmsg;
1173 for(level = 0; level < DUMP_LEVELS; level++) {
1174 if(est->est[level].needestimate) {
1175 dbprintf(_("getting size via dump for %s level %d\n"),
1176 est->qamname, level);
1177 size = getsize_dump(est->dle, level, &errmsg);
1181 g_printf(_("%s %d SIZE %lld\n"),
1182 est->qamname, level, (long long)size);
1183 if (errmsg && errmsg[0] != '\0') {
1184 if(am_has_feature(g_options->features,
1185 fe_rep_sendsize_quoted_error)) {
1186 qerrmsg = quote_string(errmsg);
1187 dbprintf(_("errmsg is %s\n"), errmsg);
1188 g_printf("%s %d ERROR %s\n",
1189 est->qamname, level, qerrmsg);
1196 amfunlock(1, "size");
1203 smbtar_calc_estimates(
1204 disk_estimates_t * est)
1208 char *errmsg = NULL, *qerrmsg;
1210 for(level = 0; level < DUMP_LEVELS; level++) {
1211 if(est->est[level].needestimate) {
1212 dbprintf(_("getting size via smbclient for %s level %d\n"),
1213 est->qamname, level);
1214 size = getsize_smbtar(est->dle, level, &errmsg);
1218 g_printf(_("%s %d SIZE %lld\n"),
1219 est->qamname, level, (long long)size);
1220 if (errmsg && errmsg[0] != '\0') {
1221 if(am_has_feature(g_options->features,
1222 fe_rep_sendsize_quoted_error)) {
1223 qerrmsg = quote_string(errmsg);
1224 dbprintf(_("errmsg is %s\n"), errmsg);
1225 g_printf("%s %d ERROR %s\n",
1226 est->qamname, level, qerrmsg);
1233 amfunlock(1, "size");
1241 gnutar_calc_estimates(
1242 disk_estimates_t * est)
1246 char *errmsg = NULL, *qerrmsg;
1248 for(level = 0; level < DUMP_LEVELS; level++) {
1249 if (est->est[level].needestimate) {
1250 dbprintf(_("getting size via gnutar for %s level %d\n"),
1251 est->qamname, level);
1252 size = getsize_gnutar(est->dle, level,
1253 est->est[level].dumpsince,
1258 g_printf(_("%s %d SIZE %lld\n"),
1259 est->qamname, level, (long long)size);
1260 if (errmsg && errmsg[0] != '\0') {
1261 if(am_has_feature(g_options->features,
1262 fe_rep_sendsize_quoted_error)) {
1263 qerrmsg = quote_string(errmsg);
1264 dbprintf(_("errmsg is %s\n"), errmsg);
1265 g_printf(_("%s %d ERROR %s\n"),
1266 est->qamname, level, qerrmsg);
1273 amfunlock(1, "size");
1279 typedef struct regex_scale_s {
1285 regex_scale_t re_size[] = {
1287 {" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1288 {" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1289 {" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
1290 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
1291 {"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
1292 {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1293 {"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
1294 {"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
1295 {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
1296 {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
1297 {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1299 {"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
1300 {"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
1301 {"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
1302 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1304 #ifdef HAVE_DUMP_ESTIMATE
1305 {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1306 /* DU 3.2g dump -E */
1307 {"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
1308 {"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
1313 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
1317 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
1318 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1322 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
1326 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
1330 #if SAMBA_VERSION >= 2
1331 #define SAMBA_DEBUG_LEVEL "0"
1332 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
1334 #define SAMBA_DEBUG_LEVEL "3"
1335 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
1349 int pipefd[2], nullfd, stdoutfd, killctl[2];
1353 char *dumpkeys = NULL;
1354 char *device = NULL;
1355 char *fstype = NULL;
1359 char *rundump_cmd = NULL;
1360 char level_str[NUM_STR_SIZE];
1363 char *qdisk = quote_string(dle->disk);
1366 amwait_t wait_status;
1367 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1372 return -2; /* planner will not even consider this level */
1374 g_snprintf(level_str, SIZEOF(level_str), "%d", level);
1376 device = amname_to_devname(dle->device);
1377 qdevice = quote_string(device);
1378 fstype = amname_to_fstype(dle->device);
1380 dbprintf(_("calculating for device %s with %s\n"),
1383 cmd = vstralloc(amlibexecdir, "/rundump", NULL);
1384 rundump_cmd = stralloc(cmd);
1385 if (g_options->config)
1386 config = g_options->config;
1388 config = "NOCONFIG";
1389 if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1390 *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
1392 dbprintf("%s\n", *errmsg);
1394 amfree(rundump_cmd);
1401 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1402 if (pipe(pipefd) < 0) {
1403 *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
1405 dbprintf("%s\n", *errmsg);
1407 amfree(rundump_cmd);
1415 #ifdef XFSDUMP /* { */
1417 if (strcmp(fstype, "xfs") == 0)
1422 name = stralloc(" (xfsdump)");
1423 dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
1424 cmd, name, level_str, qdevice);
1428 #ifdef VXDUMP /* { */
1430 if (strcmp(fstype, "vxfs") == 0)
1436 name = stralloc(" (vxdump)");
1438 name = stralloc("");
1439 cmd = newstralloc(cmd, VXDUMP);
1440 config = skip_argument;
1443 dumpkeys = vstralloc(level_str, "s", "f", NULL);
1444 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1445 cmd, name, dumpkeys, qdevice);
1449 #ifdef VDUMP /* { */
1451 if (strcmp(fstype, "advfs") == 0)
1456 name = stralloc(" (vdump)");
1457 dumpkeys = vstralloc(level_str, "b", "f", NULL);
1458 dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1459 cmd, name, dumpkeys, qdevice);
1465 # ifdef USE_RUNDUMP /* { */
1466 # ifdef AIX_BACKUP /* { */
1467 name = stralloc(" (backup)");
1469 name = vstralloc(" (", DUMP, ")", NULL);
1472 name = stralloc("");
1473 cmd = newstralloc(cmd, DUMP);
1474 config = skip_argument;
1478 # ifdef AIX_BACKUP /* { */
1479 dumpkeys = vstralloc("-", level_str, "f", NULL);
1480 dbprintf(_("running \"%s%s %s - %s\"\n"),
1481 cmd, name, dumpkeys, qdevice);
1483 # ifdef HAVE_DUMP_ESTIMATE
1484 # define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1486 # define PARAM_DUMP_ESTIMATE ""
1488 # ifdef HAVE_HONOR_NODUMP
1489 # define PARAM_HONOR_NODUMP "h"
1491 # define PARAM_HONOR_NODUMP ""
1493 dumpkeys = vstralloc(level_str,
1494 PARAM_DUMP_ESTIMATE,
1498 # ifdef HAVE_DUMP_ESTIMATE
1499 stdoutfd = pipefd[1];
1502 # ifdef HAVE_HONOR_NODUMP /* { */
1503 dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1504 cmd, name, dumpkeys, qdevice);
1506 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1507 cmd, name, dumpkeys, qdevice);
1514 error(_("no dump program available"));
1518 if (pipe(killctl) < 0) {
1519 dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1520 /* Message will be printed later... */
1521 killctl[0] = killctl[1] = -1;
1524 start_time = curclock();
1525 switch(dumppid = fork()) {
1527 *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1529 dbprintf("%s\n", *errmsg);
1532 amfree(rundump_cmd);
1541 case 0: /* child process */
1544 else if (killctl[0] == -1 || killctl[1] == -1)
1545 dbprintf(_("Trying without killpgrp\n"));
1549 dbprintf(_("fork failed, trying without killpgrp\n"));
1555 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp", NULL);
1556 dbprintf(_("running %s\n"), killpgrp_cmd);
1557 dup2(killctl[0], 0);
1564 if (g_options->config)
1565 config = g_options->config;
1567 config = "NOCONFIG";
1569 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1571 dbprintf(_("cannot execute %s: %s\n"),
1572 killpgrp_cmd, strerror(errno));
1576 case 0: /* child process */
1585 if (killctl[0] != -1)
1587 if (killctl[1] != -1)
1593 if (strcmp(fstype, "xfs") == 0)
1598 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1599 level_str, "-", device, (char *)0, safe_env());
1601 execle(cmd, "xfsdump", "-F", "-J", "-l",
1602 level_str, "-", device, (char *)0, safe_env());
1607 if (strcmp(fstype, "vxfs") == 0)
1612 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1613 "-", device, (char *)0, safe_env());
1615 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1616 device, (char *)0, safe_env());
1621 if (strcmp(fstype, "advfs") == 0)
1626 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1627 device, (char *)0, safe_env());
1629 execle(cmd, "vdump", dumpkeys, "60", "-",
1630 device, (char *)0, safe_env());
1636 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1637 device, (char *)0, safe_env());
1639 execle(cmd, "backup", dumpkeys, "-",
1640 device, (char *)0, safe_env());
1643 execle(cmd, "rundump", config, "dump", dumpkeys,
1644 #ifdef HAVE_HONOR_NODUMP
1647 "1048576", "-", device, (char *)0, safe_env());
1649 execle(cmd, "dump", dumpkeys,
1650 #ifdef HAVE_HONOR_NODUMP
1653 "1048576", "-", device, (char *)0, safe_env());
1658 error(_("exec %s failed or no dump program available: %s"),
1659 cmd, strerror(errno));
1665 amfree(rundump_cmd);
1668 if (killctl[0] != -1)
1670 dumpout = fdopen(pipefd[0],"r");
1672 error(_("Can't fdopen: %s"), strerror(errno));
1676 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1677 if (line[0] == '\0')
1679 dbprintf("%s\n", line);
1680 size = handle_dumpline(line);
1681 if(size > (off_t)-1) {
1683 while ((line = agets(dumpout)) != NULL) {
1684 if (line[0] != '\0')
1689 dbprintf("%s\n", line);
1696 dbprintf(".....\n");
1697 dbprintf(_("estimate time for %s level %d: %s\n"),
1700 walltime_str(timessub(curclock(), start_time)));
1701 if(size == (off_t)-1) {
1702 *errmsg = vstrallocf(_("no size line match in %s%s output"),
1704 dbprintf(_("%s for %s\n"),
1707 dbprintf(".....\n");
1708 dbprintf(_("Run %s%s manually to check for errors\n"),
1710 } else if(size == (off_t)0 && level == 0) {
1711 dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1712 cmd, name, dle->disk);
1713 dbprintf(".....\n");
1715 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1721 if (killctl[1] != -1) {
1722 dbprintf(_("asking killpgrp to terminate\n"));
1724 for(s = 5; s > 0; --s) {
1726 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1732 * First, try to kill the dump process nicely. If it ignores us
1733 * for several seconds, hit it harder.
1735 dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1736 if (kill(-dumppid, SIGTERM) == -1) {
1737 dbprintf(_("kill failed: %s\n"), strerror(errno));
1739 /* Now check whether it dies */
1740 for(s = 5; s > 0; --s) {
1742 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1746 dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1747 if (kill(-dumppid, SIGKILL) == -1) {
1748 dbprintf(_("kill failed: %s\n"), strerror(errno));
1750 for(s = 5; s > 0; --s) {
1752 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1756 dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1757 waitpid(dumppid, &wait_status, 0);
1758 if (WIFSIGNALED(wait_status)) {
1759 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1760 cmd, WTERMSIG(wait_status), dbfn());
1761 } else if (WIFEXITED(wait_status)) {
1762 if (WEXITSTATUS(wait_status) != 0) {
1763 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1764 cmd, WEXITSTATUS(wait_status), dbfn());
1769 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1772 dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1797 int pipefd = -1, nullfd = -1, passwdfd = -1;
1801 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1802 char *share = NULL, *subdir = NULL;
1809 char *error_pn = NULL;
1810 char *qdisk = quote_string(dle->disk);
1811 amwait_t wait_status;
1813 error_pn = stralloc2(get_pname(), "-smbclient");
1816 return -2; /* planner will not even consider this level */
1818 parsesharename(dle->device, &share, &subdir);
1822 set_pname(error_pn);
1824 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1827 if ((subdir) && (SAMBA_VERSION < 2)) {
1830 set_pname(error_pn);
1832 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1835 if ((user_and_password = findpass(share, &domain)) == NULL) {
1838 memset(domain, '\0', strlen(domain));
1841 set_pname(error_pn);
1843 error(_("cannot find password for %s"), dle->disk);
1846 lpass = strlen(user_and_password);
1847 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1848 memset(user_and_password, '\0', (size_t)lpass);
1849 amfree(user_and_password);
1851 memset(domain, '\0', strlen(domain));
1854 set_pname(error_pn);
1856 error(_("password field not \'user%%pass\' for %s"), dle->disk);
1860 pwtext_len = strlen(pwtext);
1861 if ((sharename = makesharename(share, 0)) == NULL) {
1862 memset(user_and_password, '\0', (size_t)lpass);
1863 amfree(user_and_password);
1865 memset(domain, '\0', strlen(domain));
1868 set_pname(error_pn);
1870 error(_("cannot make share name of %s"), share);
1873 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1874 memset(user_and_password, '\0', (size_t)lpass);
1875 amfree(user_and_password);
1877 memset(domain, '\0', strlen(domain));
1880 set_pname(error_pn);
1883 error(_("could not open /dev/null: %s\n"),
1888 #if SAMBA_VERSION >= 2
1890 tarkeys = "archive 0;recurse;du";
1892 tarkeys = "archive 1;recurse;du";
1895 tarkeys = "archive 0;recurse;dir";
1897 tarkeys = "archive 1;recurse;dir";
1900 start_time = curclock();
1902 if (pwtext_len > 0) {
1903 pw_fd_env = "PASSWD_FD";
1905 pw_fd_env = "dummy_PASSWD_FD";
1907 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
1908 &nullfd, &nullfd, &pipefd,
1909 pw_fd_env, &passwdfd,
1912 "-d", SAMBA_DEBUG_LEVEL,
1913 *user_and_password ? "-U" : skip_argument,
1914 *user_and_password ? user_and_password : skip_argument,
1916 domain ? "-W" : skip_argument,
1917 domain ? domain : skip_argument,
1918 #if SAMBA_VERSION >= 2
1919 subdir ? "-D" : skip_argument,
1920 subdir ? subdir : skip_argument,
1925 memset(domain, '\0', strlen(domain));
1929 if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
1930 int save_errno = errno;
1932 memset(user_and_password, '\0', (size_t)lpass);
1933 amfree(user_and_password);
1935 set_pname(error_pn);
1937 error(_("password write failed: %s"), strerror(save_errno));
1940 memset(user_and_password, '\0', (size_t)lpass);
1941 amfree(user_and_password);
1947 dumpout = fdopen(pipefd,"r");
1949 error(_("Can't fdopen: %s"), strerror(errno));
1953 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1954 if (line[0] == '\0')
1956 dbprintf("%s\n", line);
1957 size = handle_dumpline(line);
1960 while ((line = agets(dumpout)) != NULL) {
1961 if (line[0] != '\0')
1966 dbprintf("%s\n", line);
1973 dbprintf(".....\n");
1974 dbprintf(_("estimate time for %s level %d: %s\n"),
1977 walltime_str(timessub(curclock(), start_time)));
1978 if(size == (off_t)-1) {
1979 *errmsg = vstrallocf(_("no size line match in %s output"),
1981 dbprintf(_("%s for %s\n"),
1983 dbprintf(".....\n");
1984 } else if(size == (off_t)0 && level == 0) {
1985 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1986 SAMBA_CLIENT, dle->disk);
1987 dbprintf(".....\n");
1989 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1994 kill(-dumppid, SIGTERM);
1996 dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1997 waitpid(dumppid, &wait_status, 0);
1998 if (WIFSIGNALED(wait_status)) {
1999 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2000 SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
2001 } else if (WIFEXITED(wait_status)) {
2002 if (WEXITSTATUS(wait_status) != 0) {
2003 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2004 SAMBA_CLIENT, WEXITSTATUS(wait_status),
2010 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2011 SAMBA_CLIENT, dbfn());
2013 dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
2033 int pipefd = -1, nullfd = -1;
2035 off_t size = (off_t)-1;
2036 FILE *dumpout = NULL;
2037 char *incrname = NULL;
2038 char *basename = NULL;
2039 char *dirname = NULL;
2040 char *inputname = NULL;
2045 char *command = NULL;
2046 char dumptimestr[80];
2050 GPtrArray *argv_ptr = g_ptr_array_new();
2051 char *file_exclude = NULL;
2052 char *file_include = NULL;
2057 char *qdisk = quote_string(dle->disk);
2058 char *gnutar_list_dir;
2059 amwait_t wait_status;
2060 char tmppath[PATH_MAX];
2063 return -2; /* planner will not even consider this level */
2065 if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
2066 if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
2067 if(dle->include_file) nb_include += dle->include_file->nb_element;
2068 if(dle->include_list) nb_include += dle->include_list->nb_element;
2070 if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
2071 if(nb_include > 0) file_include = build_include(dle, 0);
2073 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
2074 if (strlen(gnutar_list_dir) == 0)
2075 gnutar_list_dir = NULL;
2076 if (gnutar_list_dir) {
2077 char number[NUM_STR_SIZE];
2079 char *sdisk = sanitise_filename(dle->disk);
2081 basename = vstralloc(gnutar_list_dir,
2083 g_options->hostname,
2088 g_snprintf(number, SIZEOF(number), "%d", level);
2089 incrname = vstralloc(basename, "_", number, ".new", NULL);
2093 * Open the listed incremental file from the previous level. Search
2094 * backward until one is found. If none are found (which will also
2095 * be true for a level 0), arrange to read from /dev/null.
2099 while (infd == -1) {
2100 if (--baselevel >= 0) {
2101 g_snprintf(number, SIZEOF(number), "%d", baselevel);
2102 inputname = newvstralloc(inputname,
2103 basename, "_", number, NULL);
2105 inputname = newstralloc(inputname, "/dev/null");
2107 if ((infd = open(inputname, O_RDONLY)) == -1) {
2109 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
2110 inputname, strerror(errno));
2111 dbprintf("%s\n", *errmsg);
2112 if (baselevel < 0) {
2120 * Copy the previous listed incremental file to the new one.
2122 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
2123 *errmsg = vstrallocf(_("opening %s: %s"),
2124 incrname, strerror(errno));
2125 dbprintf("%s\n", *errmsg);
2129 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
2130 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
2131 *errmsg = vstrallocf(_("writing to %s: %s"),
2132 incrname, strerror(errno));
2133 dbprintf("%s\n", *errmsg);
2139 *errmsg = vstrallocf(_("reading from %s: %s"),
2140 inputname, strerror(errno));
2141 dbprintf("%s\n", *errmsg);
2145 if (close(infd) != 0) {
2146 *errmsg = vstrallocf(_("closing %s: %s"),
2147 inputname, strerror(errno));
2148 dbprintf("%s\n", *errmsg);
2151 if (close(outfd) != 0) {
2152 *errmsg = vstrallocf(_("closing %s: %s"),
2153 incrname, strerror(errno));
2154 dbprintf("%s\n", *errmsg);
2162 gmtm = gmtime(&dumpsince);
2163 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2164 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2165 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2166 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2168 dirname = amname_to_dirname(dle->device);
2170 cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
2171 g_ptr_array_add(argv_ptr, stralloc("runtar"));
2172 if (g_options->config)
2173 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2175 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
2178 g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
2180 g_ptr_array_add(argv_ptr, stralloc("tar"));
2182 g_ptr_array_add(argv_ptr, stralloc("--create"));
2183 g_ptr_array_add(argv_ptr, stralloc("--file"));
2184 g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
2185 /* use --numeric-owner for estimates, to reduce the number of user/group
2186 * lookups required */
2187 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
2188 g_ptr_array_add(argv_ptr, stralloc("--directory"));
2189 canonicalize_pathname(dirname, tmppath);
2190 g_ptr_array_add(argv_ptr, stralloc(tmppath));
2191 g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
2192 if (gnutar_list_dir) {
2193 g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
2194 g_ptr_array_add(argv_ptr, stralloc(incrname));
2196 g_ptr_array_add(argv_ptr, stralloc("--incremental"));
2197 g_ptr_array_add(argv_ptr, stralloc("--newer"));
2198 g_ptr_array_add(argv_ptr, stralloc(dumptimestr));
2200 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2201 /* --atime-preserve causes gnutar to call
2202 * utime() after reading files in order to
2203 * adjust their atime. However, utime()
2204 * updates the file's ctime, so incremental
2205 * dumps will think the file has changed. */
2206 g_ptr_array_add(argv_ptr, stralloc("--atime-preserve"));
2208 g_ptr_array_add(argv_ptr, stralloc("--sparse"));
2209 g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
2210 g_ptr_array_add(argv_ptr, stralloc("--totals"));
2213 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
2214 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
2218 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
2219 g_ptr_array_add(argv_ptr, stralloc(file_include));
2222 g_ptr_array_add(argv_ptr, stralloc("."));
2224 g_ptr_array_add(argv_ptr, NULL);
2226 start_time = curclock();
2228 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2229 *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2231 dbprintf("%s\n", *errmsg);
2235 command = (char *)g_ptr_array_index(argv_ptr, 0);
2236 dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
2237 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
2239 dumpout = fdopen(pipefd,"r");
2241 error(_("Can't fdopen: %s"), strerror(errno));
2245 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2246 if (line[0] == '\0')
2248 dbprintf("%s\n", line);
2249 size = handle_dumpline(line);
2250 if(size > (off_t)-1) {
2252 while ((line = agets(dumpout)) != NULL) {
2253 if (line[0] != '\0') {
2259 dbprintf("%s\n", line);
2267 dbprintf(".....\n");
2268 dbprintf(_("estimate time for %s level %d: %s\n"),
2271 walltime_str(timessub(curclock(), start_time)));
2272 if(size == (off_t)-1) {
2273 *errmsg = vstrallocf(_("no size line match in %s output"),
2275 dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2276 dbprintf(".....\n");
2277 } else if(size == (off_t)0 && level == 0) {
2278 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2279 command, dle->disk);
2280 dbprintf(".....\n");
2282 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2287 kill(-dumppid, SIGTERM);
2289 dbprintf(_("waiting for %s \"%s\" child\n"),
2291 waitpid(dumppid, &wait_status, 0);
2292 if (WIFSIGNALED(wait_status)) {
2293 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2294 cmd, WTERMSIG(wait_status), dbfn());
2295 } else if (WIFEXITED(wait_status)) {
2296 if (WEXITSTATUS(wait_status) != 0) {
2297 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2298 cmd, WEXITSTATUS(wait_status), dbfn());
2303 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2306 dbprintf(_("after %s %s wait\n"), command, qdisk);
2317 g_ptr_array_free_full(argv_ptr);
2320 amfree(file_exclude);
2321 amfree(file_include);
2333 getsize_application_api(
2334 disk_estimates_t *est,
2337 backup_support_option_t *bsu)
2339 dle_t *dle = est->dle;
2340 int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
2342 off_t size = (off_t)-1;
2350 GPtrArray *argv_ptr = g_ptr_array_new();
2351 char *newoptstr = NULL;
2354 char *qdisk = quote_string(dle->disk);
2355 char *qamdevice = quote_string(dle->device);
2356 amwait_t wait_status;
2357 char levelstr[NUM_STR_SIZE];
2360 char *errmsg = NULL;
2361 estimate_t estimate;
2364 cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
2366 g_ptr_array_add(argv_ptr, stralloc(dle->program));
2367 g_ptr_array_add(argv_ptr, stralloc("estimate"));
2368 if (bsu->message_line == 1) {
2369 g_ptr_array_add(argv_ptr, stralloc("--message"));
2370 g_ptr_array_add(argv_ptr, stralloc("line"));
2372 if (g_options->config && bsu->config == 1) {
2373 g_ptr_array_add(argv_ptr, stralloc("--config"));
2374 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2376 if (g_options->hostname && bsu->host == 1) {
2377 g_ptr_array_add(argv_ptr, stralloc("--host"));
2378 g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
2380 g_ptr_array_add(argv_ptr, stralloc("--device"));
2381 g_ptr_array_add(argv_ptr, stralloc(dle->device));
2382 if (dle->disk && bsu->disk == 1) {
2383 g_ptr_array_add(argv_ptr, stralloc("--disk"));
2384 g_ptr_array_add(argv_ptr, stralloc(dle->disk));
2386 for (j=0; j < nb_level; j++) {
2387 g_ptr_array_add(argv_ptr, stralloc("--level"));
2388 g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
2389 g_ptr_array_add(argv_ptr, stralloc(levelstr));
2391 /* find the first in ES_CLIENT and ES_CALCSIZE */
2392 estimate = ES_CLIENT;
2393 for (el = dle->estimatelist; el != NULL; el = el->next) {
2394 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
2395 if ((estimate == ES_CLIENT && bsu->client_estimate) ||
2396 (estimate == ES_CALCSIZE && bsu->calcsize))
2398 estimate = ES_CLIENT;
2400 if (estimate == ES_CALCSIZE && bsu->calcsize) {
2401 g_ptr_array_add(argv_ptr, stralloc("--calcsize"));
2404 application_property_add_to_argv(argv_ptr, dle, bsu, g_options->features);
2406 for (scriptlist = dle->scriptlist; scriptlist != NULL;
2407 scriptlist = scriptlist->next) {
2408 script = (script_t *)scriptlist->data;
2409 if (script->result && script->result->proplist) {
2410 property_add_to_argv(argv_ptr, script->result->proplist);
2414 g_ptr_array_add(argv_ptr, NULL);
2416 cmdline = stralloc(cmd);
2417 for(i = 1; i < argv_ptr->len-1; i++)
2418 cmdline = vstrextend(&cmdline, " ",
2419 (char *)g_ptr_array_index(argv_ptr, i), NULL);
2420 dbprintf("running: \"%s\"\n", cmdline);
2423 if (pipe(pipeerrfd) < 0) {
2424 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2429 if (pipe(pipeinfd) < 0) {
2430 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2435 if (pipe(pipeoutfd) < 0) {
2436 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2441 start_time = curclock();
2443 switch(dumppid = fork()) {
2450 dup2(pipeinfd[0], 0);
2451 dup2(pipeoutfd[1], 1);
2452 dup2(pipeerrfd[1], 2);
2453 aclose(pipeinfd[1]);
2454 aclose(pipeoutfd[0]);
2455 aclose(pipeerrfd[0]);
2458 execve(cmd, (char **)argv_ptr->pdata, safe_env());
2459 error(_("exec %s failed: %s"), cmd, strerror(errno));
2464 aclose(pipeinfd[0]);
2465 aclose(pipeoutfd[1]);
2466 aclose(pipeerrfd[1]);
2467 aclose(pipeinfd[1]);
2469 dumpout = fdopen(pipeoutfd[0],"r");
2471 error(_("Can't fdopen: %s"), strerror(errno));
2475 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2476 long long size1_ = (long long)0;
2477 long long size2_ = (long long)0;
2479 if (line[0] == '\0')
2481 dbprintf("%s\n", line);
2482 if (strncmp(line,"ERROR ", 6) == 0) {
2483 char *errmsg, *qerrmsg;
2485 errmsg = stralloc(line+6);
2486 qerrmsg = quote_string(errmsg);
2487 dbprintf(_("errmsg is %s\n"), errmsg);
2488 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2492 i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
2494 i = sscanf(line, "%lld %lld", &size1_, &size2_);
2497 char *errmsg, *qerrmsg;
2499 errmsg = vstrallocf(_("bad line %s"), line);
2500 qerrmsg = quote_string(errmsg);
2501 dbprintf(_("errmsg is %s\n"), errmsg);
2502 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2507 size1 = (off_t)size1_;
2508 size2 = (off_t)size2_;
2509 if (size1 <= 0 || size2 <=0)
2511 else if (size1 * size2 > 0)
2512 size = size1 * size2;
2513 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2517 g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
2521 dumperr = fdopen(pipeerrfd[0],"r");
2523 error(_("Can't fdopen: %s"), strerror(errno));
2527 while ((line = agets(dumperr)) != NULL) {
2528 if (strlen(line) > 0) {
2529 char *err = g_strdup_printf(_("Application '%s': %s"),
2530 dle->program, line);
2531 char *qerr = quote_string(err);
2532 for (j=0; j < nb_level; j++) {
2533 fprintf(stdout, "%s %d ERROR %s\n",
2534 est->qamname, levels[j], qerr);
2536 dbprintf("ERROR %s", qerr);
2543 dbprintf(".....\n");
2544 if (nb_level == 1) {
2545 dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
2546 levels[0], walltime_str(timessub(curclock(), start_time)));
2548 dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
2549 walltime_str(timessub(curclock(), start_time)));
2552 kill(-dumppid, SIGTERM);
2554 dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2555 waitpid(dumppid, &wait_status, 0);
2556 if (WIFSIGNALED(wait_status)) {
2557 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2558 cmd, WTERMSIG(wait_status), dbfn());
2559 } else if (WIFEXITED(wait_status)) {
2560 if (WEXITSTATUS(wait_status) != 0) {
2561 errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2562 WEXITSTATUS(wait_status), dbfn());
2567 errmsg = vstrallocf(_("%s got bad exit: see %s"),
2570 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2578 g_ptr_array_free_full(argv_ptr);
2583 char *qerrmsg = quote_string(errmsg);
2584 dbprintf(_("errmsg is %s\n"), errmsg);
2585 for (j=0; j < nb_level; j++) {
2586 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
2596 * Returns the value of the first integer in a string.
2608 while(ch && !isdigit(ch)) ch = *str++;
2610 while(isdigit(ch) || (ch == '.')) ch = *str++;
2619 * Checks the dump output line against the error and size regex tables.
2629 /* check for size match */
2631 for(rp = re_size; rp->regex != NULL; rp++) {
2632 if(match(rp->regex, str)) {
2633 size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2635 size = 1.0; /* found on NeXT -- sigh */