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
34 #include "pipespawn.h"
35 #include "amfeatures.h"
36 #include "amandates.h"
40 #include "client_util.h"
47 #define sendsize_debug(i, ...) do { \
48 if ((i) <= debug_sebdsize) { \
49 dbprintf(__VA_ARGS__); \
54 # define SETPGRP setpgid(getpid(), getpid())
55 # define SETPGRP_FAILED() do { \
56 dbprintf(_("setpgid(%ld,%ld) failed: %s\n"), \
57 (long)getpid(), (long)getpid(), strerror(errno)); \
61 #if defined(SETPGRP_VOID)
62 # define SETPGRP setpgrp()
63 # define SETPGRP_FAILED() do { \
64 dbprintf(_("setpgrp() failed: %s\n"), strerror(errno)); \
68 # define SETPGRP setpgrp(0, getpid())
69 # define SETPGRP_FAILED() do { \
70 dbprintf(_("setpgrp(0,%ld) failed: %s\n"), \
71 (long)getpid(), strerror(errno)); \
77 typedef struct level_estimates_s {
81 int server; /* server can do estimate */
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;
125 disk_estimates_t *est_next;
129 char *err_extra = NULL;
135 char *qamdevice = NULL;
140 (void)argc; /* Quiet unused parameter warning */
141 (void)argv; /* Quiet unused parameter warning */
143 if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
144 printf("sendsize-%s\n", VERSION);
151 * Configure program for internationalization:
152 * 1) Only set the message locale for now.
153 * 2) Set textdomain for all amanda related programs to "amanda"
154 * We don't want to be forced to support dozens of message catalogs.
156 setlocale(LC_MESSAGES, "C");
157 textdomain("amanda");
163 set_pname("sendsize");
165 /* Don't die when child closes pipe */
166 signal(SIGPIPE, SIG_IGN);
168 add_amanda_log_handler(amanda_log_stderr);
169 add_amanda_log_handler(amanda_log_syslog);
170 dbopen(DBG_SUBDIR_CLIENT);
172 dbprintf(_("version %s\n"), VERSION);
174 our_features = am_init_feature_set();
175 our_feature_string = am_feature_to_string(our_features);
177 config_init(CONFIG_INIT_CLIENT, NULL);
178 /* (check for config errors comes later) */
180 check_running_as(RUNNING_AS_CLIENT_LOGIN);
182 /* handle all service requests */
184 for(; (line = agets(stdin)) != NULL; free(line)) {
187 if(strncmp_const(line, "OPTIONS ") == 0) {
188 g_options = parse_g_options(line+8, 1);
189 if(!g_options->hostname) {
190 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
191 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
192 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
195 g_printf("OPTIONS ");
196 if(am_has_feature(g_options->features, fe_rep_options_features)) {
197 g_printf("features=%s;", our_feature_string);
199 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
200 g_printf("maxdumps=%d;", g_options->maxdumps);
202 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
203 g_printf("hostname=%s;", g_options->hostname);
208 if (g_options->config) {
209 /* overlay this configuration on the existing (nameless) configuration */
210 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
213 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
216 /* check for any config errors now */
217 if (config_errors(&errlist) >= CFGERR_ERRORS) {
218 char *errstr = config_errors_to_error_string(errlist);
219 g_printf("%s\n", errstr);
224 if (am_has_feature(g_options->features, fe_req_xml)) {
234 skip_whitespace(s, ch); /* find the program name */
236 err_extra = stralloc(_("no program name"));
237 goto err; /* no program name */
239 dle->program = s - 1;
240 skip_non_whitespace(s, ch);
243 dle->program_is_application_api=0;
244 if(strncmp_const(dle->program, "CALCSIZE") == 0) {
245 skip_whitespace(s, ch); /* find the program name */
247 err_extra = stralloc(_("no program name"));
250 dle->estimatelist = g_slist_append(dle->estimatelist,
251 GINT_TO_POINTER(ES_CALCSIZE));
252 dle->program = s - 1;
253 skip_non_whitespace(s, ch);
255 if (strcmp(dle->program,"APPLICATION") == 0) {
256 dle->program_is_application_api=1;
257 skip_whitespace(s, ch); /* find dumper name */
259 goto err; /* no program */
261 dle->program = s - 1;
262 skip_non_whitespace(s, ch);
267 dle->estimatelist = g_slist_append(dle->estimatelist,
268 GINT_TO_POINTER(ES_CLIENT));
269 if (strcmp(dle->program,"APPLICATION") == 0) {
270 dle->program_is_application_api=1;
271 skip_whitespace(s, ch); /* find dumper name */
273 goto err; /* no program */
275 dle->program = s - 1;
276 skip_non_whitespace(s, ch);
280 dle->program = stralloc(dle->program);
282 skip_whitespace(s, ch); /* find the disk name */
284 err_extra = stralloc(_("no disk name"));
285 goto err; /* no disk name */
292 skip_quoted_string(s, ch);
293 s[-1] = '\0'; /* terminate the disk name */
294 qdisk = stralloc(fp);
295 dle->disk = unquote_string(qdisk);
297 skip_whitespace(s, ch); /* find the device or level */
299 err_extra = stralloc(_("bad level"));
302 if(!isdigit((int)s[-1])) {
304 skip_quoted_string(s, ch);
306 qamdevice = stralloc(fp);
307 dle->device = unquote_string(qamdevice);
308 skip_whitespace(s, ch); /* find level number */
311 dle->device = stralloc(dle->disk);
312 qamdevice = stralloc(qdisk);
315 /* find the level number */
316 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
317 err_extra = stralloc(_("bad level"));
318 goto err; /* bad level */
320 if (level < 0 || level >= DUMP_LEVELS) {
321 err_extra = stralloc(_("bad level"));
325 alevel = g_new0(am_level_t, 1);
326 alevel->level = level;
327 dle->levellist = g_slist_append(dle->levellist, alevel);
329 skip_whitespace(s, ch); /* find the dump date */
331 err_extra = stralloc(_("no dumpdate"));
332 goto err; /* no dumpdate */
335 skip_non_whitespace(s, ch);
337 (void)dumpdate; /* XXX: Set but not used */
339 dle->spindle = 0; /* default spindle */
341 skip_whitespace(s, ch); /* find the spindle */
343 if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
344 err_extra = stralloc(_("bad spindle"));
345 goto err; /* bad spindle */
349 skip_whitespace(s, ch); /* find the parameters */
351 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
359 if(strncmp_const(s-1, "exclude-file=") == 0) {
360 qlist = unquote_string(s+12);
362 append_sl(dle->exclude_file, qlist);
364 } else if(strncmp_const(s-1, "exclude-list=") == 0) {
365 qlist = unquote_string(s+12);
367 append_sl(dle->exclude_list, qlist);
369 } else if(strncmp_const(s-1, "include-file=") == 0) {
370 qlist = unquote_string(s+12);
372 append_sl(dle->include_file, qlist);
374 } else if(strncmp_const(s-1, "include-list=") == 0) {
375 qlist = unquote_string(s+12);
377 append_sl(dle->include_list, qlist);
380 err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
381 goto err; /* should have gotten to end */
383 skip_quoted_string(s, ch);
384 skip_whitespace(s, ch); /* find the inclusion list */
392 dle_add_diskest(dle);
395 if (g_options == NULL) {
396 g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
397 error(_("Missing OPTIONS line in sendsize input\n"));
402 if (am_has_feature(g_options->features, fe_req_xml)) {
406 dles = amxml_parse_node_FILE(stdin, &errmsg);
411 for (dle = dles; dle != NULL; dle = dle->next) {
412 dle_add_diskest(dle);
416 if (amandates_started) {
419 amandates_started = FALSE;
423 for(est = est_list; est != NULL; est = est_next) {
424 int good = merge_dles_properties(est->dle, 0);
425 est_next = est->next;
427 if (est == est_list) {
430 est_prev->next = est_next;
436 for(est = est_list; est != NULL; est = est->next) {
437 run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, g_options, est->dle,
447 * See if we need to wait for a child before we can do anything
452 amwait_t child_status;
455 dbprintf(_("waiting for any estimate child: %d running\n"),
457 child_pid = wait(&child_status);
458 if(child_pid == -1) {
459 error(_("wait failed: %s"), strerror(errno));
463 if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
464 char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
465 char *child_status_str = str_exit_status(child_name, child_status);
466 dbprintf("%s\n", child_status_str);
467 amfree(child_status_str);
472 * Find the child and mark it done.
474 for(est = est_list; est != NULL; est = est->next) {
475 if(est->child == child_pid) {
480 dbprintf(_("unexpected child %ld\n"), (long)child_pid);
485 run_client_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
490 * If we are already running the maximum number of children
491 * go back and wait until one of them finishes.
493 if(dumpsrunning >= g_options->maxdumps) {
496 continue; /* have to wait first */
499 * Find a new child to start.
501 for(est = est_list; est != NULL; est = est->next) {
503 done = 0; /* more to do */
505 if(est->child != 0 || est->done) {
506 continue; /* child is running or done */
509 * Make sure there is no spindle conflict.
511 if(est->dle->spindle != -1) {
512 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
513 if(est1->child == 0 || est == est1 || est1->done) {
515 * Ignore anything not yet started, ourself,
516 * and anything completed.
520 if(est1->dle->spindle == est->dle->spindle) {
521 break; /* oops -- they match */
525 continue; /* spindle conflict */
528 break; /* start this estimate */
531 if(dumpsrunning > 0) {
532 need_wait = 1; /* nothing to do but wait */
536 run_client_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
539 if((est->child = fork()) == 0) {
540 calc_estimates(est); /* child does the estimate */
542 } else if(est->child == -1) {
543 error(_("calc_estimates fork failed: %s"), strerror(errno));
546 dumpsrunning++; /* parent */
550 for(est = est_list; est != NULL; est = est->next) {
551 run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, g_options, est->dle,
556 for(est = est_list; est != NULL; est = est->next) {
562 amfree(our_feature_string);
563 am_release_feature_set(our_features);
565 free_g_options(g_options);
571 g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
572 dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
575 g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
576 dbprintf(_("REQ packet is bogus\n"));
579 free_g_options(g_options);
590 disk_estimates_t *newp, *curp;
594 levellist_t levellist;
595 char *amandates_file;
596 gboolean need_amandates = FALSE;
599 if (dle->levellist == NULL) {
600 g_printf(_("ERROR Missing level in request\n"));
604 /* should we use amandates for this? */
605 for (el = dle->estimatelist; el != NULL; el=el->next) {
606 estimate_t estimate = (estimate_t)GPOINTER_TO_INT(el->data);
607 if (estimate == ES_CALCSIZE)
608 need_amandates = TRUE;
611 if (strcmp(dle->program, "GNUTAR") == 0) {
612 /* GNUTAR only needs amandates if gnutar_list_dir is NULL */
613 char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
614 if (!gnutar_list_dir || !*gnutar_list_dir)
615 need_amandates = TRUE;
618 /* start amandates here, before adding this DLE to est_list, in case
619 * we encounter an error. */
620 if (need_amandates) {
621 if (!amandates_started) {
622 amandates_file = getconf_str(CNF_AMANDATES);
623 if(!start_amandates(amandates_file, 0)) {
624 char *errstr = strerror(errno);
625 char *qamname = quote_string(dle->disk);
626 char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
627 char *qerrmsg = quote_string(errmsg);
628 g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
634 amandates_started = TRUE;
638 levellist = dle->levellist;
639 while (levellist != NULL) {
640 am_level_t *alevel = (am_level_t *)levellist->data;
641 if (alevel->level < 0)
643 if (alevel->level >= DUMP_LEVELS)
644 alevel->level = DUMP_LEVELS - 1;
645 levellist = g_slist_next(levellist);
648 for(curp = est_list; curp != NULL; curp = curp->next) {
649 if(strcmp(curp->dle->disk, dle->disk) == 0) {
650 /* already have disk info, just note the level request */
651 levellist = dle->levellist;
652 while (levellist != NULL) {
653 am_level_t *alevel = (am_level_t *)levellist->data;
654 int level = alevel->level;
655 curp->est[level].needestimate = 1;
656 curp->est[level].server = alevel->server;
657 levellist = g_slist_next(levellist);
664 newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
665 memset(newp, 0, SIZEOF(*newp));
666 newp->next = est_list;
668 newp->qamname = quote_string(dle->disk);
670 newp->qamdevice = quote_string(dle->device);
671 newp->dirname = amname_to_dirname(dle->device);
672 newp->qdirname = quote_string(newp->dirname);
674 newp->qamdevice = stralloc("");
675 newp->dirname = stralloc("");
676 newp->qdirname = stralloc("");
678 levellist = dle->levellist;
679 while (levellist != NULL) {
680 am_level_t *alevel = (am_level_t *)levellist->data;
681 newp->est[alevel->level].needestimate = 1;
682 newp->est[alevel->level].server = alevel->server;
683 levellist = g_slist_next(levellist);
687 /* fill in dump-since dates */
688 if (need_amandates) {
689 amdp = amandates_lookup(newp->dle->disk);
691 newp->est[0].dumpsince = EPOCH;
692 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
693 dumpdate = amdp->dates[dumplev];
694 for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
695 if(dumpdate > newp->est[estlev].dumpsince)
696 newp->est[estlev].dumpsince = dumpdate;
700 /* just zero everything out */
701 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
702 newp->est[dumplev].dumpsince = 0;
710 disk_estimates_t * est)
712 amfree(est->qamname);
713 amfree(est->qamdevice);
714 amfree(est->dirname);
715 amfree(est->qdirname);
722 * ------------------------------------------------------------------------
728 disk_estimates_t * est)
730 dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
731 est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
733 if(est->dle->program_is_application_api == 1)
734 application_api_calc_estimate(est);
739 estimate_t estimate_method = ES_ES;
740 estimate_t client_method = ES_ES;
742 /* find estimate method to use */
743 for (el = est->dle->estimatelist; el != NULL; el = el->next) {
744 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
745 if (estimate == ES_SERVER) {
746 if (estimate_method == ES_ES)
747 estimate_method = ES_SERVER;
749 if (estimate == ES_CLIENT ||
750 (estimate == ES_CALCSIZE &&
751 (est->dle->device[0] != '/' || est->dle->device[1] != '/'))) {
752 if (client_method == ES_ES)
753 client_method = estimate;
754 if (estimate_method == ES_ES)
755 estimate_method = estimate;
759 /* do server estimate */
760 if (estimate_method == ES_SERVER) {
761 for (level = 0; level < DUMP_LEVELS; level++) {
762 if (est->est[level].needestimate) {
763 if (est->est[level].server || client_method == ES_ES) {
764 g_printf(_("%s %d SIZE -1\n"), est->qamname, level);
765 est->est[level].needestimate = 0;
771 if (client_method == ES_ES && estimate_method != ES_SERVER) {
772 g_printf(_("%s %d SIZE -2\n"), est->qamname, 0);
773 dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
774 est->qamname, est->qdirname);
775 } else if (client_method == ES_CALCSIZE) {
776 generic_calc_estimates(est);
777 } else if (client_method == ES_CLIENT) {
778 #ifndef USE_GENERIC_CALCSIZE
779 if (strcmp(est->dle->program, "DUMP") == 0)
780 dump_calc_estimates(est);
784 if (strcmp(est->dle->program, "GNUTAR") == 0 &&
785 est->dle->device[0] == '/' && est->dle->device[1] == '/')
786 smbtar_calc_estimates(est);
790 if (strcmp(est->dle->program, "GNUTAR") == 0)
791 gnutar_calc_estimates(est);
794 dbprintf(_("Invalid program: %s %s %s\n"),
795 est->qamname, est->qdirname, est->dle->program);
799 dbprintf(_("done with amname %s dirname %s spindle %d\n"),
800 est->qamname, est->qdirname, est->dle->spindle);
804 * ------------------------------------------------------------------------
808 /* local functions */
809 off_t getsize_dump(dle_t *dle, int level, char **errmsg);
810 off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
811 off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
812 off_t getsize_application_api(disk_estimates_t *est, int nb_level,
813 int *levels, backup_support_option_t *bsu);
814 off_t handle_dumpline(char *str);
815 double first_num(char *str);
818 application_api_calc_estimate(
819 disk_estimates_t * est)
823 int levels[DUMP_LEVELS];
825 backup_support_option_t *bsu;
829 estimate_t estimate_method = ES_ES;
830 estimate_t client_method = ES_ES;
831 int has_calcsize = 0;
834 bsu = backup_support_option(est->dle->program, g_options, est->dle->disk,
835 est->dle->device, &errarray);
838 for (i=0; i < errarray->len; i++) {
842 line = g_ptr_array_index(errarray, i);
843 if(am_has_feature(g_options->features,
844 fe_rep_sendsize_quoted_error)) {
845 errmsg = g_strdup_printf(_("Application '%s': %s"),
846 est->dle->program, line);
847 qerrmsg = quote_string(errmsg);
848 for (level = 0; level < DUMP_LEVELS; level++) {
849 if (est->est[level].needestimate) {
850 g_printf(_("%s %d ERROR %s\n"),
851 est->dle->disk, level, qerrmsg);
852 dbprintf(_("%s %d ERROR %s\n"),
853 est->qamname, level, qerrmsg);
860 if (i == 0) { /* nothing in errarray */
863 errmsg = g_strdup_printf(
864 _("Application '%s': cannon execute support command"),
866 qerrmsg = quote_string(errmsg);
867 for (level = 0; level < DUMP_LEVELS; level++) {
868 if (est->est[level].needestimate) {
869 g_printf(_("%s %d ERROR %s\n"),
870 est->dle->disk, level, qerrmsg);
871 dbprintf(_("%s %d ERROR %s\n"),
872 est->qamname, level, qerrmsg);
878 for (level = 0; level < DUMP_LEVELS; level++) {
879 est->est[level].needestimate = 0;
881 g_ptr_array_free(errarray, TRUE);
884 if (est->dle->data_path == DATA_PATH_AMANDA &&
885 (bsu->data_path_set & DATA_PATH_AMANDA)==0) {
886 g_printf("%s %d ERROR application %s doesn't support amanda data-path\n", est->qamname, 0, est->dle->program);
890 if (est->dle->data_path == DATA_PATH_DIRECTTCP &&
891 (bsu->data_path_set & DATA_PATH_DIRECTTCP)==0) {
892 g_printf("%s %d ERROR application %s doesn't support directtcp data-path\n", est->qamname, 0, est->dle->program);
897 /* find estimate method to use */
898 for (el = est->dle->estimatelist; el != NULL; el = el->next) {
899 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
900 if (estimate == ES_CLIENT)
902 if (estimate == ES_CALCSIZE)
904 if (estimate == ES_SERVER) {
905 if (estimate_method == ES_ES)
906 estimate_method = ES_SERVER;
908 if ((estimate == ES_CLIENT && bsu->client_estimate) ||
909 (estimate == ES_CALCSIZE && bsu->calcsize)) {
910 if (client_method == ES_ES)
911 client_method = estimate;
912 if (estimate_method == ES_ES)
913 estimate_method = estimate;
917 for(level = 0; level < DUMP_LEVELS; level++) {
918 if (est->est[level].needestimate) {
919 if (level > bsu->max_level) {
920 /* planner will not even consider this level */
921 g_printf("%s %d SIZE %lld\n", est->qamname, level,
923 est->est[level].needestimate = 0;
924 dbprintf(_("Application '%s' can't estimate level %d\n"),
925 est->dle->program, level);
926 } else if (estimate_method == ES_ES) {
927 g_printf("%s %d SIZE %lld\n", est->qamname, level,
929 est->est[level].needestimate = 0;
930 if (am_has_feature(g_options->features,
931 fe_rep_sendsize_quoted_error)) {
932 char *errmsg, *qerrmsg;
933 if (has_client && !bsu->client_estimate &&
934 has_calcsize && !bsu->calcsize) {
935 errmsg = vstrallocf(_("Application '%s' can't do CLIENT or CALCSIZE estimate"),
937 } else if (has_client && !bsu->client_estimate) {
938 errmsg = vstrallocf(_("Application '%s' can't do CLIENT estimate"),
940 } else if (has_calcsize && !bsu->calcsize) {
941 errmsg = vstrallocf(_("Application '%s' can't do CALCSIZE estimate"),
944 errmsg = vstrallocf(_("Application '%s' can't do estimate"),
947 qerrmsg = quote_string(errmsg);
948 dbprintf(_("errmsg is %s\n"), errmsg);
949 g_printf("%s %d ERROR %s\n",
950 est->qamname, 0, qerrmsg);
954 } else if (estimate_method == ES_SERVER &&
955 (est->est[level].server || client_method == ES_ES)) {
956 /* planner will consider this level, */
957 /* but use a server-side estimate */
958 g_printf("%s %d SIZE -1\n", est->qamname, level);
959 est->est[level].needestimate = 0;
960 } else if (client_method == ES_CLIENT) {
961 levels[nb_level++] = level;
962 } else if (client_method == ES_CALCSIZE) {
963 levels[nb_level++] = level;
973 if (bsu->multi_estimate) {
974 for (i=0;i<nb_level;i++) {
975 dbprintf(_("getting size via application API for %s %s level %d\n"),
976 est->qamname, est->qamdevice, levels[i]);
978 getsize_application_api(est, nb_level, levels, bsu);
981 for(level = 0; level < DUMP_LEVELS; level++) {
982 if (est->est[level].needestimate) {
984 _("getting size via application API for %s %s level %d\n"),
985 est->qamname, est->qamdevice, level);
987 getsize_application_api(est, 1, levels, bsu);
997 generic_calc_estimates(
998 disk_estimates_t * est)
1000 int pipefd = -1, nullfd = -1;
1004 GPtrArray *argv_ptr = g_ptr_array_new();
1005 char number[NUM_STR_SIZE];
1011 char *file_exclude = NULL;
1012 char *file_include = NULL;
1014 FILE *dumpout = NULL;
1017 amwait_t wait_status;
1018 char *errmsg = NULL, *qerrmsg;
1019 char tmppath[PATH_MAX];
1022 cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
1024 g_ptr_array_add(argv_ptr, stralloc("calcsize"));
1025 if (g_options->config)
1026 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
1028 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
1030 g_ptr_array_add(argv_ptr, stralloc(est->dle->program));
1031 canonicalize_pathname(est->dle->disk, tmppath);
1032 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1033 canonicalize_pathname(est->dirname, tmppath);
1034 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1036 if (est->dle->exclude_file)
1037 nb_exclude += est->dle->exclude_file->nb_element;
1038 if (est->dle->exclude_list)
1039 nb_exclude += est->dle->exclude_list->nb_element;
1040 if (est->dle->include_file)
1041 nb_include += est->dle->include_file->nb_element;
1042 if (est->dle->include_list)
1043 nb_include += est->dle->include_list->nb_element;
1046 file_exclude = build_exclude(est->dle, 0);
1048 file_include = build_include(est->dle, 0);
1051 g_ptr_array_add(argv_ptr, stralloc("-X"));
1052 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1056 g_ptr_array_add(argv_ptr, stralloc("-I"));
1057 g_ptr_array_add(argv_ptr, stralloc(file_include));
1059 start_time = curclock();
1061 command = (char *)g_ptr_array_index(argv_ptr, 0);
1062 cmdline = stralloc(command);
1063 for(i = 1; i < argv_ptr->len-1; i++) {
1064 cmdline = vstrextend(&cmdline, " ",
1065 (char *)g_ptr_array_index(argv_ptr, i), NULL);
1067 dbprintf(_("running: \"%s\"\n"), cmdline);
1070 for(level = 0; level < DUMP_LEVELS; level++) {
1071 if(est->est[level].needestimate) {
1072 g_snprintf(number, SIZEOF(number), "%d", level);
1073 g_ptr_array_add(argv_ptr, stralloc(number));
1074 dbprintf(" %s", number);
1075 g_snprintf(number, SIZEOF(number),
1076 "%ld", (long)est->est[level].dumpsince);
1077 g_ptr_array_add(argv_ptr, stralloc(number));
1078 dbprintf(" %s", number);
1081 g_ptr_array_add(argv_ptr, NULL);
1084 fflush(stderr); fflush(stdout);
1086 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1087 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1089 dbprintf("%s\n", errmsg);
1093 calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
1094 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
1097 dumpout = fdopen(pipefd,"r");
1099 error(_("Can't fdopen: %s"), strerror(errno));
1102 match_expr = vstralloc(" %d SIZE %lld", NULL);
1103 len = strlen(est->qamname);
1104 for(; (line = agets(dumpout)) != NULL; free(line)) {
1105 long long size_ = (long long)0;
1106 if (line[0] == '\0' || (int)strlen(line) <= len)
1108 /* Don't use sscanf for est->qamname because it can have a '%'. */
1109 if (strncmp(line, est->qamname, len) == 0 &&
1110 sscanf(line+len, match_expr, &level, &size_) == 2) {
1111 g_printf("%s\n", line); /* write to amandad */
1112 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1120 dbprintf(_("waiting for %s %s child (pid=%d)\n"),
1121 command, est->qamdevice, (int)calcpid);
1122 waitpid(calcpid, &wait_status, 0);
1123 if (WIFSIGNALED(wait_status)) {
1124 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1125 "calcsize", WTERMSIG(wait_status),
1127 } else if (WIFEXITED(wait_status)) {
1128 if (WEXITSTATUS(wait_status) != 0) {
1129 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1130 "calcsize", WEXITSTATUS(wait_status),
1136 errmsg = vstrallocf(_("%s got bad exit: see %s"),
1137 "calcsize", dbfn());
1139 dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
1140 command, est->qamdevice,
1141 (int)calcpid, WEXITSTATUS(wait_status));
1143 dbprintf(_(".....\n"));
1144 dbprintf(_("estimate time for %s: %s\n"),
1146 walltime_str(timessub(curclock(), start_time)));
1149 if (errmsg && errmsg[0] != '\0') {
1150 if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
1151 qerrmsg = quote_string(errmsg);
1152 dbprintf(_("errmsg is %s\n"), errmsg);
1153 g_printf("%s %d ERROR %s\n",
1154 est->qamname, 0, qerrmsg);
1159 g_ptr_array_free_full(argv_ptr);
1165 dump_calc_estimates(
1166 disk_estimates_t * est)
1170 char *errmsg=NULL, *qerrmsg;
1172 for(level = 0; level < DUMP_LEVELS; level++) {
1173 if(est->est[level].needestimate) {
1174 dbprintf(_("getting size via dump for %s level %d\n"),
1175 est->qamname, level);
1176 size = getsize_dump(est->dle, level, &errmsg);
1180 g_printf(_("%s %d SIZE %lld\n"),
1181 est->qamname, level, (long long)size);
1182 if (errmsg && errmsg[0] != '\0') {
1183 if(am_has_feature(g_options->features,
1184 fe_rep_sendsize_quoted_error)) {
1185 qerrmsg = quote_string(errmsg);
1186 dbprintf(_("errmsg is %s\n"), errmsg);
1187 g_printf("%s %d ERROR %s\n",
1188 est->qamname, level, qerrmsg);
1195 amfunlock(1, "size");
1202 smbtar_calc_estimates(
1203 disk_estimates_t * est)
1207 char *errmsg = NULL, *qerrmsg;
1209 for(level = 0; level < DUMP_LEVELS; level++) {
1210 if(est->est[level].needestimate) {
1211 dbprintf(_("getting size via smbclient for %s level %d\n"),
1212 est->qamname, level);
1213 size = getsize_smbtar(est->dle, level, &errmsg);
1217 g_printf(_("%s %d SIZE %lld\n"),
1218 est->qamname, level, (long long)size);
1219 if (errmsg && errmsg[0] != '\0') {
1220 if(am_has_feature(g_options->features,
1221 fe_rep_sendsize_quoted_error)) {
1222 qerrmsg = quote_string(errmsg);
1223 dbprintf(_("errmsg is %s\n"), errmsg);
1224 g_printf("%s %d ERROR %s\n",
1225 est->qamname, level, qerrmsg);
1232 amfunlock(1, "size");
1240 gnutar_calc_estimates(
1241 disk_estimates_t * est)
1245 char *errmsg = NULL, *qerrmsg;
1247 for(level = 0; level < DUMP_LEVELS; level++) {
1248 if (est->est[level].needestimate) {
1249 dbprintf(_("getting size via gnutar for %s level %d\n"),
1250 est->qamname, level);
1251 size = getsize_gnutar(est->dle, level,
1252 est->est[level].dumpsince,
1257 g_printf(_("%s %d SIZE %lld\n"),
1258 est->qamname, level, (long long)size);
1259 if (errmsg && errmsg[0] != '\0') {
1260 if(am_has_feature(g_options->features,
1261 fe_rep_sendsize_quoted_error)) {
1262 qerrmsg = quote_string(errmsg);
1263 dbprintf(_("errmsg is %s\n"), errmsg);
1264 g_printf(_("%s %d ERROR %s\n"),
1265 est->qamname, level, qerrmsg);
1272 amfunlock(1, "size");
1278 typedef struct regex_scale_s {
1284 regex_scale_t re_size[] = {
1286 {" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1287 {" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1288 {" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
1289 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
1290 {"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
1291 {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1292 {"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
1293 {"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
1294 {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
1295 {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
1296 {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1298 {"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
1299 {"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
1300 {"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
1301 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1303 #ifdef HAVE_DUMP_ESTIMATE
1304 {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1305 /* DU 3.2g dump -E */
1306 {"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
1307 {"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
1312 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
1316 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
1317 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1321 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
1325 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
1329 #if SAMBA_VERSION >= 2
1330 #define SAMBA_DEBUG_LEVEL "0"
1331 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
1333 #define SAMBA_DEBUG_LEVEL "3"
1334 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
1348 int pipefd[2], nullfd, stdoutfd, killctl[2];
1352 char *dumpkeys = NULL;
1353 char *device = NULL;
1354 char *fstype = NULL;
1358 char *rundump_cmd = NULL;
1359 char level_str[NUM_STR_SIZE];
1362 char *qdisk = quote_string(dle->disk);
1365 amwait_t wait_status;
1366 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1371 return -2; /* planner will not even consider this level */
1373 g_snprintf(level_str, SIZEOF(level_str), "%d", level);
1375 device = amname_to_devname(dle->device);
1376 qdevice = quote_string(device);
1377 fstype = amname_to_fstype(dle->device);
1379 dbprintf(_("calculating for device %s with %s\n"),
1382 cmd = vstralloc(amlibexecdir, "/rundump", NULL);
1383 rundump_cmd = stralloc(cmd);
1384 if (g_options->config)
1385 config = g_options->config;
1387 config = "NOCONFIG";
1388 if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1389 *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
1391 dbprintf("%s\n", *errmsg);
1393 amfree(rundump_cmd);
1400 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1401 if (pipe(pipefd) < 0) {
1402 *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
1404 dbprintf("%s\n", *errmsg);
1406 amfree(rundump_cmd);
1414 #ifdef XFSDUMP /* { */
1416 if (strcmp(fstype, "xfs") == 0)
1421 name = stralloc(" (xfsdump)");
1422 dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
1423 cmd, name, level_str, qdevice);
1427 #ifdef VXDUMP /* { */
1429 if (strcmp(fstype, "vxfs") == 0)
1435 name = stralloc(" (vxdump)");
1437 name = stralloc("");
1438 cmd = newstralloc(cmd, VXDUMP);
1439 config = skip_argument;
1442 dumpkeys = vstralloc(level_str, "s", "f", NULL);
1443 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1444 cmd, name, dumpkeys, qdevice);
1448 #ifdef VDUMP /* { */
1450 if (strcmp(fstype, "advfs") == 0)
1455 name = stralloc(" (vdump)");
1456 dumpkeys = vstralloc(level_str, "b", "f", NULL);
1457 dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1458 cmd, name, dumpkeys, qdevice);
1464 # ifdef USE_RUNDUMP /* { */
1465 # ifdef AIX_BACKUP /* { */
1466 name = stralloc(" (backup)");
1468 name = vstralloc(" (", DUMP, ")", NULL);
1471 name = stralloc("");
1472 cmd = newstralloc(cmd, DUMP);
1473 config = skip_argument;
1477 # ifdef AIX_BACKUP /* { */
1478 dumpkeys = vstralloc("-", level_str, "f", NULL);
1479 dbprintf(_("running \"%s%s %s - %s\"\n"),
1480 cmd, name, dumpkeys, qdevice);
1482 # ifdef HAVE_DUMP_ESTIMATE
1483 # define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1485 # define PARAM_DUMP_ESTIMATE ""
1487 # ifdef HAVE_HONOR_NODUMP
1488 # define PARAM_HONOR_NODUMP "h"
1490 # define PARAM_HONOR_NODUMP ""
1492 dumpkeys = vstralloc(level_str,
1493 PARAM_DUMP_ESTIMATE,
1497 # ifdef HAVE_DUMP_ESTIMATE
1498 stdoutfd = pipefd[1];
1501 # ifdef HAVE_HONOR_NODUMP /* { */
1502 dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1503 cmd, name, dumpkeys, qdevice);
1505 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1506 cmd, name, dumpkeys, qdevice);
1513 error(_("no dump program available"));
1517 if (pipe(killctl) < 0) {
1518 dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1519 /* Message will be printed later... */
1520 killctl[0] = killctl[1] = -1;
1523 start_time = curclock();
1524 switch(dumppid = fork()) {
1526 *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1528 dbprintf("%s\n", *errmsg);
1531 amfree(rundump_cmd);
1540 case 0: /* child process */
1543 else if (killctl[0] == -1 || killctl[1] == -1)
1544 dbprintf(_("Trying without killpgrp\n"));
1548 dbprintf(_("fork failed, trying without killpgrp\n"));
1554 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp", NULL);
1555 dbprintf(_("running %s\n"), killpgrp_cmd);
1556 dup2(killctl[0], 0);
1563 if (g_options->config)
1564 config = g_options->config;
1566 config = "NOCONFIG";
1568 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1570 dbprintf(_("cannot execute %s: %s\n"),
1571 killpgrp_cmd, strerror(errno));
1575 case 0: /* child process */
1584 if (killctl[0] != -1)
1586 if (killctl[1] != -1)
1592 if (strcmp(fstype, "xfs") == 0)
1597 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1598 level_str, "-", device, (char *)0, safe_env());
1600 execle(cmd, "xfsdump", "-F", "-J", "-l",
1601 level_str, "-", device, (char *)0, safe_env());
1606 if (strcmp(fstype, "vxfs") == 0)
1611 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1612 "-", device, (char *)0, safe_env());
1614 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1615 device, (char *)0, safe_env());
1620 if (strcmp(fstype, "advfs") == 0)
1625 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1626 device, (char *)0, safe_env());
1628 execle(cmd, "vdump", dumpkeys, "60", "-",
1629 device, (char *)0, safe_env());
1635 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1636 device, (char *)0, safe_env());
1638 execle(cmd, "backup", dumpkeys, "-",
1639 device, (char *)0, safe_env());
1642 execle(cmd, "rundump", config, "dump", dumpkeys,
1643 #ifdef HAVE_HONOR_NODUMP
1646 "1048576", "-", device, (char *)0, safe_env());
1648 execle(cmd, "dump", dumpkeys,
1649 #ifdef HAVE_HONOR_NODUMP
1652 "1048576", "-", device, (char *)0, safe_env());
1657 error(_("exec %s failed or no dump program available: %s"),
1658 cmd, strerror(errno));
1664 amfree(rundump_cmd);
1667 if (killctl[0] != -1)
1669 dumpout = fdopen(pipefd[0],"r");
1671 error(_("Can't fdopen: %s"), strerror(errno));
1675 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1676 if (line[0] == '\0')
1678 dbprintf("%s\n", line);
1679 size = handle_dumpline(line);
1680 if(size > (off_t)-1) {
1682 while ((line = agets(dumpout)) != NULL) {
1683 if (line[0] != '\0')
1688 dbprintf("%s\n", line);
1695 dbprintf(".....\n");
1696 dbprintf(_("estimate time for %s level %d: %s\n"),
1699 walltime_str(timessub(curclock(), start_time)));
1700 if(size == (off_t)-1) {
1701 *errmsg = vstrallocf(_("no size line match in %s%s output"),
1703 dbprintf(_("%s for %s\n"),
1706 dbprintf(".....\n");
1707 dbprintf(_("Run %s%s manually to check for errors\n"),
1709 } else if(size == (off_t)0 && level == 0) {
1710 dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1711 cmd, name, dle->disk);
1712 dbprintf(".....\n");
1714 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1720 if (killctl[1] != -1) {
1721 dbprintf(_("asking killpgrp to terminate\n"));
1723 for(s = 5; s > 0; --s) {
1725 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1731 * First, try to kill the dump process nicely. If it ignores us
1732 * for several seconds, hit it harder.
1734 dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1735 if (kill(-dumppid, SIGTERM) == -1) {
1736 dbprintf(_("kill failed: %s\n"), strerror(errno));
1738 /* Now check whether it dies */
1739 for(s = 5; s > 0; --s) {
1741 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1745 dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1746 if (kill(-dumppid, SIGKILL) == -1) {
1747 dbprintf(_("kill failed: %s\n"), strerror(errno));
1749 for(s = 5; s > 0; --s) {
1751 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1755 dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1756 waitpid(dumppid, &wait_status, 0);
1757 if (WIFSIGNALED(wait_status)) {
1758 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1759 cmd, WTERMSIG(wait_status), dbfn());
1760 } else if (WIFEXITED(wait_status)) {
1761 if (WEXITSTATUS(wait_status) != 0) {
1762 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1763 cmd, WEXITSTATUS(wait_status), dbfn());
1768 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1771 dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1796 int pipefd = -1, nullfd = -1, passwdfd = -1;
1800 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1801 char *share = NULL, *subdir = NULL;
1808 char *error_pn = NULL;
1809 char *qdisk = quote_string(dle->disk);
1810 amwait_t wait_status;
1812 error_pn = stralloc2(get_pname(), "-smbclient");
1815 return -2; /* planner will not even consider this level */
1817 parsesharename(dle->device, &share, &subdir);
1821 set_pname(error_pn);
1823 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1826 if ((subdir) && (SAMBA_VERSION < 2)) {
1829 set_pname(error_pn);
1831 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1834 if ((user_and_password = findpass(share, &domain)) == NULL) {
1837 memset(domain, '\0', strlen(domain));
1840 set_pname(error_pn);
1842 error(_("cannot find password for %s"), dle->disk);
1845 lpass = strlen(user_and_password);
1846 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1847 memset(user_and_password, '\0', (size_t)lpass);
1848 amfree(user_and_password);
1850 memset(domain, '\0', strlen(domain));
1853 set_pname(error_pn);
1855 error(_("password field not \'user%%pass\' for %s"), dle->disk);
1859 pwtext_len = strlen(pwtext);
1860 if ((sharename = makesharename(share, 0)) == NULL) {
1861 memset(user_and_password, '\0', (size_t)lpass);
1862 amfree(user_and_password);
1864 memset(domain, '\0', strlen(domain));
1867 set_pname(error_pn);
1869 error(_("cannot make share name of %s"), share);
1872 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1873 memset(user_and_password, '\0', (size_t)lpass);
1874 amfree(user_and_password);
1876 memset(domain, '\0', strlen(domain));
1879 set_pname(error_pn);
1882 error(_("could not open /dev/null: %s\n"),
1887 #if SAMBA_VERSION >= 2
1889 tarkeys = "archive 0;recurse;du";
1891 tarkeys = "archive 1;recurse;du";
1894 tarkeys = "archive 0;recurse;dir";
1896 tarkeys = "archive 1;recurse;dir";
1899 start_time = curclock();
1901 if (pwtext_len > 0) {
1902 pw_fd_env = "PASSWD_FD";
1904 pw_fd_env = "dummy_PASSWD_FD";
1906 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
1907 &nullfd, &nullfd, &pipefd,
1908 pw_fd_env, &passwdfd,
1911 "-d", SAMBA_DEBUG_LEVEL,
1912 *user_and_password ? "-U" : skip_argument,
1913 *user_and_password ? user_and_password : skip_argument,
1915 domain ? "-W" : skip_argument,
1916 domain ? domain : skip_argument,
1917 #if SAMBA_VERSION >= 2
1918 subdir ? "-D" : skip_argument,
1919 subdir ? subdir : skip_argument,
1924 memset(domain, '\0', strlen(domain));
1928 if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
1929 int save_errno = errno;
1931 memset(user_and_password, '\0', (size_t)lpass);
1932 amfree(user_and_password);
1934 set_pname(error_pn);
1936 error(_("password write failed: %s"), strerror(save_errno));
1939 memset(user_and_password, '\0', (size_t)lpass);
1940 amfree(user_and_password);
1946 dumpout = fdopen(pipefd,"r");
1948 error(_("Can't fdopen: %s"), strerror(errno));
1952 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1953 if (line[0] == '\0')
1955 dbprintf("%s\n", line);
1956 size = handle_dumpline(line);
1959 while ((line = agets(dumpout)) != NULL) {
1960 if (line[0] != '\0')
1965 dbprintf("%s\n", line);
1972 dbprintf(".....\n");
1973 dbprintf(_("estimate time for %s level %d: %s\n"),
1976 walltime_str(timessub(curclock(), start_time)));
1977 if(size == (off_t)-1) {
1978 *errmsg = vstrallocf(_("no size line match in %s output"),
1980 dbprintf(_("%s for %s\n"),
1982 dbprintf(".....\n");
1983 } else if(size == (off_t)0 && level == 0) {
1984 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1985 SAMBA_CLIENT, dle->disk);
1986 dbprintf(".....\n");
1988 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1993 kill(-dumppid, SIGTERM);
1995 dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1996 waitpid(dumppid, &wait_status, 0);
1997 if (WIFSIGNALED(wait_status)) {
1998 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1999 SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
2000 } else if (WIFEXITED(wait_status)) {
2001 if (WEXITSTATUS(wait_status) != 0) {
2002 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2003 SAMBA_CLIENT, WEXITSTATUS(wait_status),
2009 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2010 SAMBA_CLIENT, dbfn());
2012 dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
2032 int pipefd = -1, nullfd = -1;
2034 off_t size = (off_t)-1;
2035 FILE *dumpout = NULL;
2036 char *incrname = NULL;
2037 char *basename = NULL;
2038 char *dirname = NULL;
2039 char *inputname = NULL;
2044 char *command = NULL;
2045 char dumptimestr[80];
2049 GPtrArray *argv_ptr = g_ptr_array_new();
2050 char *file_exclude = NULL;
2051 char *file_include = NULL;
2056 char *qdisk = quote_string(dle->disk);
2057 char *gnutar_list_dir;
2058 amwait_t wait_status;
2059 char tmppath[PATH_MAX];
2062 return -2; /* planner will not even consider this level */
2064 if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
2065 if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
2066 if(dle->include_file) nb_include += dle->include_file->nb_element;
2067 if(dle->include_list) nb_include += dle->include_list->nb_element;
2069 if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
2070 if(nb_include > 0) file_include = build_include(dle, 0);
2072 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
2073 if (strlen(gnutar_list_dir) == 0)
2074 gnutar_list_dir = NULL;
2075 if (gnutar_list_dir) {
2076 char number[NUM_STR_SIZE];
2078 char *sdisk = sanitise_filename(dle->disk);
2080 basename = vstralloc(gnutar_list_dir,
2082 g_options->hostname,
2087 g_snprintf(number, SIZEOF(number), "%d", level);
2088 incrname = vstralloc(basename, "_", number, ".new", NULL);
2092 * Open the listed incremental file from the previous level. Search
2093 * backward until one is found. If none are found (which will also
2094 * be true for a level 0), arrange to read from /dev/null.
2098 while (infd == -1) {
2099 if (--baselevel >= 0) {
2100 g_snprintf(number, SIZEOF(number), "%d", baselevel);
2101 inputname = newvstralloc(inputname,
2102 basename, "_", number, NULL);
2104 inputname = newstralloc(inputname, "/dev/null");
2106 if ((infd = open(inputname, O_RDONLY)) == -1) {
2108 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
2109 inputname, strerror(errno));
2110 dbprintf("%s\n", *errmsg);
2111 if (baselevel < 0) {
2119 * Copy the previous listed incremental file to the new one.
2121 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
2122 *errmsg = vstrallocf(_("opening %s: %s"),
2123 incrname, strerror(errno));
2124 dbprintf("%s\n", *errmsg);
2128 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
2129 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
2130 *errmsg = vstrallocf(_("writing to %s: %s"),
2131 incrname, strerror(errno));
2132 dbprintf("%s\n", *errmsg);
2138 *errmsg = vstrallocf(_("reading from %s: %s"),
2139 inputname, strerror(errno));
2140 dbprintf("%s\n", *errmsg);
2144 if (close(infd) != 0) {
2145 *errmsg = vstrallocf(_("closing %s: %s"),
2146 inputname, strerror(errno));
2147 dbprintf("%s\n", *errmsg);
2150 if (close(outfd) != 0) {
2151 *errmsg = vstrallocf(_("closing %s: %s"),
2152 incrname, strerror(errno));
2153 dbprintf("%s\n", *errmsg);
2161 gmtm = gmtime(&dumpsince);
2162 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2163 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2164 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2165 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2167 dirname = amname_to_dirname(dle->device);
2169 cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
2170 g_ptr_array_add(argv_ptr, stralloc("runtar"));
2171 if (g_options->config)
2172 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2174 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
2177 g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
2179 g_ptr_array_add(argv_ptr, stralloc("tar"));
2181 g_ptr_array_add(argv_ptr, stralloc("--create"));
2182 g_ptr_array_add(argv_ptr, stralloc("--file"));
2183 g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
2184 /* use --numeric-owner for estimates, to reduce the number of user/group
2185 * lookups required */
2186 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
2187 g_ptr_array_add(argv_ptr, stralloc("--directory"));
2188 canonicalize_pathname(dirname, tmppath);
2189 g_ptr_array_add(argv_ptr, stralloc(tmppath));
2190 g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
2191 if (gnutar_list_dir) {
2192 g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
2193 g_ptr_array_add(argv_ptr, stralloc(incrname));
2195 g_ptr_array_add(argv_ptr, stralloc("--incremental"));
2196 g_ptr_array_add(argv_ptr, stralloc("--newer"));
2197 g_ptr_array_add(argv_ptr, stralloc(dumptimestr));
2199 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2200 /* --atime-preserve causes gnutar to call
2201 * utime() after reading files in order to
2202 * adjust their atime. However, utime()
2203 * updates the file's ctime, so incremental
2204 * dumps will think the file has changed. */
2205 g_ptr_array_add(argv_ptr, stralloc("--atime-preserve"));
2207 g_ptr_array_add(argv_ptr, stralloc("--sparse"));
2208 g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
2209 g_ptr_array_add(argv_ptr, stralloc("--totals"));
2212 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
2213 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
2217 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
2218 g_ptr_array_add(argv_ptr, stralloc(file_include));
2221 g_ptr_array_add(argv_ptr, stralloc("."));
2223 g_ptr_array_add(argv_ptr, NULL);
2225 start_time = curclock();
2227 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2228 *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2230 dbprintf("%s\n", *errmsg);
2234 command = (char *)g_ptr_array_index(argv_ptr, 0);
2235 dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
2236 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
2238 dumpout = fdopen(pipefd,"r");
2240 error(_("Can't fdopen: %s"), strerror(errno));
2244 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2245 if (line[0] == '\0')
2247 dbprintf("%s\n", line);
2248 size = handle_dumpline(line);
2249 if(size > (off_t)-1) {
2251 while ((line = agets(dumpout)) != NULL) {
2252 if (line[0] != '\0') {
2258 dbprintf("%s\n", line);
2266 dbprintf(".....\n");
2267 dbprintf(_("estimate time for %s level %d: %s\n"),
2270 walltime_str(timessub(curclock(), start_time)));
2271 if(size == (off_t)-1) {
2272 *errmsg = vstrallocf(_("no size line match in %s output"),
2274 dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2275 dbprintf(".....\n");
2276 } else if(size == (off_t)0 && level == 0) {
2277 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2278 command, dle->disk);
2279 dbprintf(".....\n");
2281 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2286 kill(-dumppid, SIGTERM);
2288 dbprintf(_("waiting for %s \"%s\" child\n"),
2290 waitpid(dumppid, &wait_status, 0);
2291 if (WIFSIGNALED(wait_status)) {
2292 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2293 cmd, WTERMSIG(wait_status), dbfn());
2294 } else if (WIFEXITED(wait_status)) {
2295 if (WEXITSTATUS(wait_status) != 0) {
2296 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2297 cmd, WEXITSTATUS(wait_status), dbfn());
2302 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2305 dbprintf(_("after %s %s wait\n"), command, qdisk);
2316 g_ptr_array_free_full(argv_ptr);
2319 amfree(file_exclude);
2320 amfree(file_include);
2332 getsize_application_api(
2333 disk_estimates_t *est,
2336 backup_support_option_t *bsu)
2338 dle_t *dle = est->dle;
2339 int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
2341 off_t size = (off_t)-1;
2349 GPtrArray *argv_ptr = g_ptr_array_new();
2350 char *newoptstr = NULL;
2353 char *qdisk = quote_string(dle->disk);
2354 char *qamdevice = quote_string(dle->device);
2355 amwait_t wait_status;
2356 char levelstr[NUM_STR_SIZE];
2359 char *errmsg = NULL;
2360 estimate_t estimate;
2363 cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
2365 g_ptr_array_add(argv_ptr, stralloc(dle->program));
2366 g_ptr_array_add(argv_ptr, stralloc("estimate"));
2367 if (bsu->message_line == 1) {
2368 g_ptr_array_add(argv_ptr, stralloc("--message"));
2369 g_ptr_array_add(argv_ptr, stralloc("line"));
2371 if (g_options->config && bsu->config == 1) {
2372 g_ptr_array_add(argv_ptr, stralloc("--config"));
2373 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2375 if (g_options->hostname && bsu->host == 1) {
2376 g_ptr_array_add(argv_ptr, stralloc("--host"));
2377 g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
2379 g_ptr_array_add(argv_ptr, stralloc("--device"));
2380 g_ptr_array_add(argv_ptr, stralloc(dle->device));
2381 if (dle->disk && bsu->disk == 1) {
2382 g_ptr_array_add(argv_ptr, stralloc("--disk"));
2383 g_ptr_array_add(argv_ptr, stralloc(dle->disk));
2385 for (j=0; j < nb_level; j++) {
2386 g_ptr_array_add(argv_ptr, stralloc("--level"));
2387 g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
2388 g_ptr_array_add(argv_ptr, stralloc(levelstr));
2390 /* find the first in ES_CLIENT and ES_CALCSIZE */
2391 estimate = ES_CLIENT;
2392 for (el = dle->estimatelist; el != NULL; el = el->next) {
2393 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
2394 if ((estimate == ES_CLIENT && bsu->client_estimate) ||
2395 (estimate == ES_CALCSIZE && bsu->calcsize))
2397 estimate = ES_CLIENT;
2399 if (estimate == ES_CALCSIZE && bsu->calcsize) {
2400 g_ptr_array_add(argv_ptr, stralloc("--calcsize"));
2403 application_property_add_to_argv(argv_ptr, dle, bsu, g_options->features);
2405 for (scriptlist = dle->scriptlist; scriptlist != NULL;
2406 scriptlist = scriptlist->next) {
2407 script = (script_t *)scriptlist->data;
2408 if (script->result && script->result->proplist) {
2409 property_add_to_argv(argv_ptr, script->result->proplist);
2413 g_ptr_array_add(argv_ptr, NULL);
2415 cmdline = stralloc(cmd);
2416 for(i = 1; i < argv_ptr->len-1; i++)
2417 cmdline = vstrextend(&cmdline, " ",
2418 (char *)g_ptr_array_index(argv_ptr, i), NULL);
2419 dbprintf("running: \"%s\"\n", cmdline);
2422 if (pipe(pipeerrfd) < 0) {
2423 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2428 if (pipe(pipeinfd) < 0) {
2429 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2434 if (pipe(pipeoutfd) < 0) {
2435 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2440 start_time = curclock();
2442 switch(dumppid = fork()) {
2449 dup2(pipeinfd[0], 0);
2450 dup2(pipeoutfd[1], 1);
2451 dup2(pipeerrfd[1], 2);
2452 aclose(pipeinfd[1]);
2453 aclose(pipeoutfd[0]);
2454 aclose(pipeerrfd[0]);
2457 execve(cmd, (char **)argv_ptr->pdata, safe_env());
2458 error(_("exec %s failed: %s"), cmd, strerror(errno));
2463 aclose(pipeinfd[0]);
2464 aclose(pipeoutfd[1]);
2465 aclose(pipeerrfd[1]);
2466 aclose(pipeinfd[1]);
2468 dumpout = fdopen(pipeoutfd[0],"r");
2470 error(_("Can't fdopen: %s"), strerror(errno));
2474 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2475 long long size1_ = (long long)0;
2476 long long size2_ = (long long)0;
2478 if (line[0] == '\0')
2480 dbprintf("%s\n", line);
2481 if (strncmp(line,"ERROR ", 6) == 0) {
2482 char *errmsg, *qerrmsg;
2484 errmsg = stralloc(line+6);
2485 qerrmsg = quote_string(errmsg);
2486 dbprintf(_("errmsg is %s\n"), errmsg);
2487 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2491 i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
2493 i = sscanf(line, "%lld %lld", &size1_, &size2_);
2496 char *errmsg, *qerrmsg;
2498 errmsg = vstrallocf(_("bad line %s"), line);
2499 qerrmsg = quote_string(errmsg);
2500 dbprintf(_("errmsg is %s\n"), errmsg);
2501 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2506 size1 = (off_t)size1_;
2507 size2 = (off_t)size2_;
2508 if (size1 <= 0 || size2 <=0)
2510 else if (size1 * size2 > 0)
2511 size = size1 * size2;
2512 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2516 g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
2520 dumperr = fdopen(pipeerrfd[0],"r");
2522 error(_("Can't fdopen: %s"), strerror(errno));
2526 while ((line = agets(dumperr)) != NULL) {
2527 if (strlen(line) > 0) {
2528 char *err = g_strdup_printf(_("Application '%s': %s"),
2529 dle->program, line);
2530 char *qerr = quote_string(err);
2531 for (j=0; j < nb_level; j++) {
2532 fprintf(stdout, "%s %d ERROR %s\n",
2533 est->qamname, levels[j], qerr);
2535 dbprintf("ERROR %s", qerr);
2542 dbprintf(".....\n");
2543 if (nb_level == 1) {
2544 dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
2545 levels[0], walltime_str(timessub(curclock(), start_time)));
2547 dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
2548 walltime_str(timessub(curclock(), start_time)));
2551 kill(-dumppid, SIGTERM);
2553 dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2554 waitpid(dumppid, &wait_status, 0);
2555 if (WIFSIGNALED(wait_status)) {
2556 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2557 cmd, WTERMSIG(wait_status), dbfn());
2558 } else if (WIFEXITED(wait_status)) {
2559 if (WEXITSTATUS(wait_status) != 0) {
2560 errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2561 WEXITSTATUS(wait_status), dbfn());
2566 errmsg = vstrallocf(_("%s got bad exit: see %s"),
2569 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2577 g_ptr_array_free_full(argv_ptr);
2582 char *qerrmsg = quote_string(errmsg);
2583 dbprintf(_("errmsg is %s\n"), errmsg);
2584 for (j=0; j < nb_level; j++) {
2585 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
2595 * Returns the value of the first integer in a string.
2607 while(ch && !isdigit(ch)) ch = *str++;
2609 while(isdigit(ch) || (ch == '.')) ch = *str++;
2618 * Checks the dump output line against the error and size regex tables.
2628 /* check for size match */
2630 for(rp = re_size; rp->regex != NULL; rp++) {
2631 if(match(rp->regex, str)) {
2632 size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2634 size = 1.0; /* found on NeXT -- sigh */