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;
128 char *err_extra = NULL;
134 char *qamdevice = NULL;
139 (void)argc; /* Quiet unused parameter warning */
140 (void)argv; /* Quiet unused parameter warning */
145 * Configure program for internationalization:
146 * 1) Only set the message locale for now.
147 * 2) Set textdomain for all amanda related programs to "amanda"
148 * We don't want to be forced to support dozens of message catalogs.
150 setlocale(LC_MESSAGES, "C");
151 textdomain("amanda");
157 set_pname("sendsize");
159 /* Don't die when child closes pipe */
160 signal(SIGPIPE, SIG_IGN);
162 add_amanda_log_handler(amanda_log_stderr);
163 add_amanda_log_handler(amanda_log_syslog);
164 dbopen(DBG_SUBDIR_CLIENT);
166 dbprintf(_("version %s\n"), VERSION);
168 our_features = am_init_feature_set();
169 our_feature_string = am_feature_to_string(our_features);
171 config_init(CONFIG_INIT_CLIENT, NULL);
172 /* (check for config errors comes later) */
174 check_running_as(RUNNING_AS_CLIENT_LOGIN);
176 /* handle all service requests */
178 for(; (line = agets(stdin)) != NULL; free(line)) {
181 if(strncmp_const(line, "OPTIONS ") == 0) {
182 g_options = parse_g_options(line+8, 1);
183 if(!g_options->hostname) {
184 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
185 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
186 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
189 g_printf("OPTIONS ");
190 if(am_has_feature(g_options->features, fe_rep_options_features)) {
191 g_printf("features=%s;", our_feature_string);
193 if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
194 g_printf("maxdumps=%d;", g_options->maxdumps);
196 if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
197 g_printf("hostname=%s;", g_options->hostname);
202 if (g_options->config) {
203 /* overlay this configuration on the existing (nameless) configuration */
204 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
207 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
210 /* check for any config errors now */
211 if (config_errors(&errlist) >= CFGERR_ERRORS) {
212 char *errstr = config_errors_to_error_string(errlist);
213 g_printf("%s\n", errstr);
218 if (am_has_feature(g_options->features, fe_req_xml)) {
228 skip_whitespace(s, ch); /* find the program name */
230 err_extra = stralloc(_("no program name"));
231 goto err; /* no program name */
233 dle->program = s - 1;
234 skip_non_whitespace(s, ch);
237 dle->program_is_application_api=0;
238 if(strncmp_const(dle->program, "CALCSIZE") == 0) {
239 skip_whitespace(s, ch); /* find the program name */
241 err_extra = stralloc(_("no program name"));
244 dle->estimatelist = g_slist_append(dle->estimatelist,
245 GINT_TO_POINTER(ES_CALCSIZE));
246 dle->program = s - 1;
247 skip_non_whitespace(s, ch);
249 if (strcmp(dle->program,"APPLICATION") == 0) {
250 dle->program_is_application_api=1;
251 skip_whitespace(s, ch); /* find dumper name */
253 goto err; /* no program */
255 dle->program = s - 1;
256 skip_non_whitespace(s, ch);
261 dle->estimatelist = g_slist_append(dle->estimatelist,
262 GINT_TO_POINTER(ES_CLIENT));
263 if (strcmp(dle->program,"APPLICATION") == 0) {
264 dle->program_is_application_api=1;
265 skip_whitespace(s, ch); /* find dumper name */
267 goto err; /* no program */
269 dle->program = s - 1;
270 skip_non_whitespace(s, ch);
274 dle->program = stralloc(dle->program);
276 skip_whitespace(s, ch); /* find the disk name */
278 err_extra = stralloc(_("no disk name"));
279 goto err; /* no disk name */
286 skip_quoted_string(s, ch);
287 s[-1] = '\0'; /* terminate the disk name */
288 qdisk = stralloc(fp);
289 dle->disk = unquote_string(qdisk);
291 skip_whitespace(s, ch); /* find the device or level */
293 err_extra = stralloc(_("bad level"));
296 if(!isdigit((int)s[-1])) {
298 skip_quoted_string(s, ch);
300 qamdevice = stralloc(fp);
301 dle->device = unquote_string(qamdevice);
302 skip_whitespace(s, ch); /* find level number */
305 dle->device = stralloc(dle->disk);
306 qamdevice = stralloc(qdisk);
309 /* find the level number */
310 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
311 err_extra = stralloc(_("bad level"));
312 goto err; /* bad level */
314 if (level < 0 || level >= DUMP_LEVELS) {
315 err_extra = stralloc(_("bad level"));
319 alevel = g_new0(level_t, 1);
320 alevel->level = level;
321 dle->levellist = g_slist_append(dle->levellist, alevel);
323 skip_whitespace(s, ch); /* find the dump date */
325 err_extra = stralloc(_("no dumpdate"));
326 goto err; /* no dumpdate */
329 skip_non_whitespace(s, ch);
331 (void)dumpdate; /* XXX: Set but not used */
333 dle->spindle = 0; /* default spindle */
335 skip_whitespace(s, ch); /* find the spindle */
337 if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
338 err_extra = stralloc(_("bad spindle"));
339 goto err; /* bad spindle */
343 skip_whitespace(s, ch); /* find the parameters */
345 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
353 if(strncmp_const(s-1, "exclude-file=") == 0) {
354 qlist = unquote_string(s+12);
356 append_sl(dle->exclude_file, qlist);
358 } else if(strncmp_const(s-1, "exclude-list=") == 0) {
359 qlist = unquote_string(s+12);
361 append_sl(dle->exclude_list, qlist);
363 } else if(strncmp_const(s-1, "include-file=") == 0) {
364 qlist = unquote_string(s+12);
366 append_sl(dle->include_file, qlist);
368 } else if(strncmp_const(s-1, "include-list=") == 0) {
369 qlist = unquote_string(s+12);
371 append_sl(dle->include_list, qlist);
374 err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
375 goto err; /* should have gotten to end */
377 skip_quoted_string(s, ch);
378 skip_whitespace(s, ch); /* find the inclusion list */
386 dle_add_diskest(dle);
389 if (g_options == NULL) {
390 g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
391 error(_("Missing OPTIONS line in sendsize input\n"));
396 if (am_has_feature(g_options->features, fe_req_xml)) {
400 dles = amxml_parse_node_FILE(stdin, &errmsg);
405 for (dle = dles; dle != NULL; dle = dle->next) {
406 dle_add_diskest(dle);
410 if (amandates_started) {
413 amandates_started = FALSE;
416 for(est = est_list; est != NULL; est = est->next) {
417 run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, g_options, est->dle,
427 * See if we need to wait for a child before we can do anything
432 amwait_t child_status;
435 dbprintf(_("waiting for any estimate child: %d running\n"),
437 child_pid = wait(&child_status);
438 if(child_pid == -1) {
439 error(_("wait failed: %s"), strerror(errno));
443 if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
444 char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
445 char *child_status_str = str_exit_status(child_name, child_status);
446 dbprintf("%s\n", child_status_str);
447 amfree(child_status_str);
452 * Find the child and mark it done.
454 for(est = est_list; est != NULL; est = est->next) {
455 if(est->child == child_pid) {
460 dbprintf(_("unexpected child %ld\n"), (long)child_pid);
465 run_client_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
470 * If we are already running the maximum number of children
471 * go back and wait until one of them finishes.
473 if(dumpsrunning >= g_options->maxdumps) {
476 continue; /* have to wait first */
479 * Find a new child to start.
481 for(est = est_list; est != NULL; est = est->next) {
483 done = 0; /* more to do */
485 if(est->child != 0 || est->done) {
486 continue; /* child is running or done */
489 * Make sure there is no spindle conflict.
491 if(est->dle->spindle != -1) {
492 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
493 if(est1->child == 0 || est == est1 || est1->done) {
495 * Ignore anything not yet started, ourself,
496 * and anything completed.
500 if(est1->dle->spindle == est->dle->spindle) {
501 break; /* oops -- they match */
505 continue; /* spindle conflict */
508 break; /* start this estimate */
511 if(dumpsrunning > 0) {
512 need_wait = 1; /* nothing to do but wait */
516 run_client_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
519 if((est->child = fork()) == 0) {
520 calc_estimates(est); /* child does the estimate */
522 } else if(est->child == -1) {
523 error(_("calc_estimates fork failed: %s"), strerror(errno));
526 dumpsrunning++; /* parent */
530 for(est = est_list; est != NULL; est = est->next) {
531 run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, g_options, est->dle,
536 for(est = est_list; est != NULL; est = est->next) {
542 amfree(our_feature_string);
543 am_release_feature_set(our_features);
545 free_g_options(g_options);
551 g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
552 dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
555 g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
556 dbprintf(_("REQ packet is bogus\n"));
559 free_g_options(g_options);
570 disk_estimates_t *newp, *curp;
574 levellist_t levellist;
575 char *amandates_file;
576 gboolean need_amandates = FALSE;
579 if (dle->levellist == NULL) {
580 g_printf(_("ERROR Missing level in request\n"));
584 /* should we use amandates for this? */
585 for (el = dle->estimatelist; el != NULL; el=el->next) {
586 estimate_t estimate = (estimate_t)GPOINTER_TO_INT(el->data);
587 if (estimate == ES_CALCSIZE)
588 need_amandates = TRUE;
591 if (strcmp(dle->program, "GNUTAR") == 0) {
592 /* GNUTAR only needs amandates if gnutar_list_dir is NULL */
593 char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
594 if (!gnutar_list_dir || !*gnutar_list_dir)
595 need_amandates = TRUE;
598 /* start amandates here, before adding this DLE to est_list, in case
599 * we encounter an error. */
600 if (need_amandates) {
601 if (!amandates_started) {
602 amandates_file = getconf_str(CNF_AMANDATES);
603 if(!start_amandates(amandates_file, 0)) {
604 char *errstr = strerror(errno);
605 char *qamname = quote_string(dle->disk);
606 char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
607 char *qerrmsg = quote_string(errmsg);
608 g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
614 amandates_started = TRUE;
618 levellist = dle->levellist;
619 while (levellist != NULL) {
620 level_t *alevel = (level_t *)levellist->data;
621 if (alevel->level < 0)
623 if (alevel->level >= DUMP_LEVELS)
624 alevel->level = DUMP_LEVELS - 1;
625 levellist = g_slist_next(levellist);
628 for(curp = est_list; curp != NULL; curp = curp->next) {
629 if(strcmp(curp->dle->disk, dle->disk) == 0) {
630 /* already have disk info, just note the level request */
631 levellist = dle->levellist;
632 while (levellist != NULL) {
633 level_t *alevel = (level_t *)levellist->data;
634 int level = alevel->level;
635 curp->est[level].needestimate = 1;
636 curp->est[level].server = alevel->server;
637 levellist = g_slist_next(levellist);
644 newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
645 memset(newp, 0, SIZEOF(*newp));
646 newp->next = est_list;
648 newp->qamname = quote_string(dle->disk);
650 newp->qamdevice = quote_string(dle->device);
651 newp->dirname = amname_to_dirname(dle->device);
652 newp->qdirname = quote_string(newp->dirname);
654 newp->qamdevice = stralloc("");
655 newp->dirname = stralloc("");
656 newp->qdirname = stralloc("");
658 levellist = dle->levellist;
659 while (levellist != NULL) {
660 level_t *alevel = (level_t *)levellist->data;
661 newp->est[alevel->level].needestimate = 1;
662 newp->est[alevel->level].server = alevel->server;
663 levellist = g_slist_next(levellist);
667 /* fill in dump-since dates */
668 if (need_amandates) {
669 amdp = amandates_lookup(newp->dle->disk);
671 newp->est[0].dumpsince = EPOCH;
672 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
673 dumpdate = amdp->dates[dumplev];
674 for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
675 if(dumpdate > newp->est[estlev].dumpsince)
676 newp->est[estlev].dumpsince = dumpdate;
680 /* just zero everything out */
681 for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
682 newp->est[dumplev].dumpsince = 0;
690 disk_estimates_t * est)
692 amfree(est->qamname);
693 amfree(est->qamdevice);
694 amfree(est->dirname);
695 amfree(est->qdirname);
702 * ------------------------------------------------------------------------
708 disk_estimates_t * est)
710 dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
711 est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
713 if(est->dle->program_is_application_api == 1)
714 application_api_calc_estimate(est);
719 estimate_t estimate_method = ES_ES;
720 estimate_t client_method = ES_ES;
722 /* find estimate method to use */
723 for (el = est->dle->estimatelist; el != NULL; el = el->next) {
724 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
725 if (estimate == ES_SERVER) {
726 if (estimate_method == ES_ES)
727 estimate_method = ES_SERVER;
729 if (estimate == ES_CLIENT ||
730 (estimate == ES_CALCSIZE &&
731 (est->dle->device[0] != '/' || est->dle->device[1] != '/'))) {
732 if (client_method == ES_ES)
733 client_method = estimate;
734 if (estimate_method == ES_ES)
735 estimate_method = estimate;
739 /* do server estimate */
740 if (estimate_method == ES_SERVER) {
741 for (level = 0; level < DUMP_LEVELS; level++) {
742 if (est->est[level].needestimate) {
743 if (est->est[level].server || client_method == ES_ES) {
744 g_printf(_("%s %d SIZE -1\n"), est->qamname, level);
745 est->est[level].needestimate = 0;
751 if (client_method == ES_ES && estimate_method != ES_SERVER) {
752 g_printf(_("%s %d SIZE -2\n"), est->qamname, 0);
753 dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
754 est->qamname, est->qdirname);
755 } else if (client_method == ES_CALCSIZE) {
756 generic_calc_estimates(est);
757 } else if (client_method == ES_CLIENT) {
758 #ifndef USE_GENERIC_CALCSIZE
759 if (strcmp(est->dle->program, "DUMP") == 0)
760 dump_calc_estimates(est);
764 if (strcmp(est->dle->program, "GNUTAR") == 0 &&
765 est->dle->device[0] == '/' && est->dle->device[1] == '/')
766 smbtar_calc_estimates(est);
770 if (strcmp(est->dle->program, "GNUTAR") == 0)
771 gnutar_calc_estimates(est);
774 dbprintf(_("Invalid program: %s %s %s\n"),
775 est->qamname, est->qdirname, est->dle->program);
779 dbprintf(_("done with amname %s dirname %s spindle %d\n"),
780 est->qamname, est->qdirname, est->dle->spindle);
784 * ------------------------------------------------------------------------
788 /* local functions */
789 off_t getsize_dump(dle_t *dle, int level, char **errmsg);
790 off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
791 off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
792 off_t getsize_application_api(disk_estimates_t *est, int nb_level,
793 int *levels, backup_support_option_t *bsu);
794 off_t handle_dumpline(char *str);
795 double first_num(char *str);
798 application_api_calc_estimate(
799 disk_estimates_t * est)
803 int levels[DUMP_LEVELS];
805 backup_support_option_t *bsu;
809 estimate_t estimate_method = ES_ES;
810 estimate_t client_method = ES_ES;
811 int has_calcsize = 0;
814 bsu = backup_support_option(est->dle->program, g_options, est->dle->disk,
815 est->dle->device, &errarray);
818 for (i=0; i < errarray->len; i++) {
822 line = g_ptr_array_index(errarray, i);
823 if(am_has_feature(g_options->features,
824 fe_rep_sendsize_quoted_error)) {
825 errmsg = g_strdup_printf(_("Application '%s': %s"),
826 est->dle->program, line);
827 qerrmsg = quote_string(errmsg);
828 for (level = 0; level < DUMP_LEVELS; level++) {
829 if (est->est[level].needestimate) {
830 g_printf(_("%s %d ERROR %s\n"),
831 est->dle->disk, level, qerrmsg);
832 dbprintf(_("%s %d ERROR %s\n"),
833 est->qamname, level, qerrmsg);
840 if (i == 0) { /* nothing in errarray */
843 errmsg = g_strdup_printf(
844 _("Application '%s': cannon execute support command"),
846 qerrmsg = quote_string(errmsg);
847 for (level = 0; level < DUMP_LEVELS; level++) {
848 if (est->est[level].needestimate) {
849 g_printf(_("%s %d ERROR %s\n"),
850 est->dle->disk, level, qerrmsg);
851 dbprintf(_("%s %d ERROR %s\n"),
852 est->qamname, level, qerrmsg);
858 for (level = 0; level < DUMP_LEVELS; level++) {
859 est->est[level].needestimate = 0;
861 g_ptr_array_free(errarray, TRUE);
864 if (est->dle->data_path == DATA_PATH_AMANDA &&
865 (bsu->data_path_set & DATA_PATH_AMANDA)==0) {
866 g_printf("%s %d ERROR application %s doesn't support amanda data-path\n", est->qamname, 0, est->dle->program);
870 if (est->dle->data_path == DATA_PATH_DIRECTTCP &&
871 (bsu->data_path_set & DATA_PATH_DIRECTTCP)==0) {
872 g_printf("%s %d ERROR application %s doesn't support directtcp data-path\n", est->qamname, 0, est->dle->program);
877 /* find estimate method to use */
878 for (el = est->dle->estimatelist; el != NULL; el = el->next) {
879 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
880 if (estimate == ES_CLIENT)
882 if (estimate == ES_CALCSIZE)
884 if (estimate == ES_SERVER) {
885 if (estimate_method == ES_ES)
886 estimate_method = ES_SERVER;
888 if ((estimate == ES_CLIENT && bsu->client_estimate) ||
889 (estimate == ES_CALCSIZE && bsu->calcsize)) {
890 if (client_method == ES_ES)
891 client_method = estimate;
892 if (estimate_method == ES_ES)
893 estimate_method = estimate;
897 for(level = 0; level < DUMP_LEVELS; level++) {
898 if (est->est[level].needestimate) {
899 if (level > bsu->max_level) {
900 /* planner will not even consider this level */
901 g_printf("%s %d SIZE %lld\n", est->qamname, level,
903 est->est[level].needestimate = 0;
904 dbprintf(_("Application '%s' can't estimate level %d\n"),
905 est->dle->program, level);
906 } else if (estimate_method == ES_ES) {
907 g_printf("%s %d SIZE %lld\n", est->qamname, level,
909 est->est[level].needestimate = 0;
910 if (am_has_feature(g_options->features,
911 fe_rep_sendsize_quoted_error)) {
912 char *errmsg, *qerrmsg;
913 if (has_client && !bsu->client_estimate &&
914 has_calcsize && !bsu->calcsize) {
915 errmsg = vstrallocf(_("Application '%s' can't do CLIENT or CALCSIZE estimate"),
917 } else if (has_client && !bsu->client_estimate) {
918 errmsg = vstrallocf(_("Application '%s' can't do CLIENT estimate"),
920 } else if (has_calcsize && !bsu->calcsize) {
921 errmsg = vstrallocf(_("Application '%s' can't do CALCSIZE estimate"),
924 errmsg = vstrallocf(_("Application '%s' can't do estimate"),
927 qerrmsg = quote_string(errmsg);
928 dbprintf(_("errmsg is %s\n"), errmsg);
929 g_printf("%s %d ERROR %s\n",
930 est->qamname, 0, qerrmsg);
934 } else if (estimate_method == ES_SERVER &&
935 (est->est[level].server || client_method == ES_ES)) {
936 /* planner will consider this level, */
937 /* but use a server-side estimate */
938 g_printf("%s %d SIZE -1\n", est->qamname, level);
939 est->est[level].needestimate = 0;
940 } else if (client_method == ES_CLIENT) {
941 levels[nb_level++] = level;
942 } else if (client_method == ES_CALCSIZE) {
943 levels[nb_level++] = level;
953 if (bsu->multi_estimate) {
954 for (i=0;i<nb_level;i++) {
955 dbprintf(_("getting size via application API for %s %s level %d\n"),
956 est->qamname, est->qamdevice, levels[i]);
958 getsize_application_api(est, nb_level, levels, bsu);
961 for(level = 0; level < DUMP_LEVELS; level++) {
962 if (est->est[level].needestimate) {
964 _("getting size via application API for %s %s level %d\n"),
965 est->qamname, est->qamdevice, level);
967 getsize_application_api(est, 1, levels, bsu);
977 generic_calc_estimates(
978 disk_estimates_t * est)
980 int pipefd = -1, nullfd = -1;
984 GPtrArray *argv_ptr = g_ptr_array_new();
985 char number[NUM_STR_SIZE];
991 char *file_exclude = NULL;
992 char *file_include = NULL;
994 FILE *dumpout = NULL;
995 off_t size = (off_t)1;
998 amwait_t wait_status;
999 char *errmsg = NULL, *qerrmsg;
1000 char tmppath[PATH_MAX];
1003 cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
1005 g_ptr_array_add(argv_ptr, stralloc("calcsize"));
1006 if (g_options->config)
1007 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
1009 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
1011 g_ptr_array_add(argv_ptr, stralloc(est->dle->program));
1012 canonicalize_pathname(est->dle->disk, tmppath);
1013 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1014 canonicalize_pathname(est->dirname, tmppath);
1015 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1017 if (est->dle->exclude_file)
1018 nb_exclude += est->dle->exclude_file->nb_element;
1019 if (est->dle->exclude_list)
1020 nb_exclude += est->dle->exclude_list->nb_element;
1021 if (est->dle->include_file)
1022 nb_include += est->dle->include_file->nb_element;
1023 if (est->dle->include_list)
1024 nb_include += est->dle->include_list->nb_element;
1027 file_exclude = build_exclude(est->dle, 0);
1029 file_include = build_include(est->dle, 0);
1032 g_ptr_array_add(argv_ptr, stralloc("-X"));
1033 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1037 g_ptr_array_add(argv_ptr, stralloc("-I"));
1038 g_ptr_array_add(argv_ptr, stralloc(file_include));
1040 start_time = curclock();
1042 command = (char *)g_ptr_array_index(argv_ptr, 0);
1043 cmdline = stralloc(command);
1044 for(i = 1; i < argv_ptr->len-1; i++) {
1045 cmdline = vstrextend(&cmdline, " ",
1046 (char *)g_ptr_array_index(argv_ptr, i), NULL);
1048 dbprintf(_("running: \"%s\"\n"), cmdline);
1051 for(level = 0; level < DUMP_LEVELS; level++) {
1052 if(est->est[level].needestimate) {
1053 g_snprintf(number, SIZEOF(number), "%d", level);
1054 g_ptr_array_add(argv_ptr, stralloc(number));
1055 dbprintf(" %s", number);
1056 g_snprintf(number, SIZEOF(number),
1057 "%ld", (long)est->est[level].dumpsince);
1058 g_ptr_array_add(argv_ptr, stralloc(number));
1059 dbprintf(" %s", number);
1062 g_ptr_array_add(argv_ptr, NULL);
1065 fflush(stderr); fflush(stdout);
1067 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1068 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1070 dbprintf("%s\n", errmsg);
1074 calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
1075 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
1078 dumpout = fdopen(pipefd,"r");
1080 error(_("Can't fdopen: %s"), strerror(errno));
1083 match_expr = vstralloc(" %d SIZE %lld", NULL);
1084 len = strlen(est->qamname);
1085 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1086 long long size_ = (long long)0;
1087 if (line[0] == '\0' || (int)strlen(line) <= len)
1089 /* Don't use sscanf for est->qamname because it can have a '%'. */
1090 if (strncmp(line, est->qamname, len) == 0 &&
1091 sscanf(line+len, match_expr, &level, &size_) == 2) {
1092 g_printf("%s\n", line); /* write to amandad */
1093 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1098 size = (off_t)size_;
1102 dbprintf(_("waiting for %s %s child (pid=%d)\n"),
1103 command, est->qamdevice, (int)calcpid);
1104 waitpid(calcpid, &wait_status, 0);
1105 if (WIFSIGNALED(wait_status)) {
1106 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1107 "calcsize", WTERMSIG(wait_status),
1109 } else if (WIFEXITED(wait_status)) {
1110 if (WEXITSTATUS(wait_status) != 0) {
1111 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1112 "calcsize", WEXITSTATUS(wait_status),
1118 errmsg = vstrallocf(_("%s got bad exit: see %s"),
1119 "calcsize", dbfn());
1121 dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
1122 command, est->qamdevice,
1123 (int)calcpid, WEXITSTATUS(wait_status));
1125 dbprintf(_(".....\n"));
1126 dbprintf(_("estimate time for %s: %s\n"),
1128 walltime_str(timessub(curclock(), start_time)));
1131 if (errmsg && errmsg[0] != '\0') {
1132 if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
1133 qerrmsg = quote_string(errmsg);
1134 dbprintf(_("errmsg is %s\n"), errmsg);
1135 g_printf("%s %d ERROR %s\n",
1136 est->qamname, 0, qerrmsg);
1141 g_ptr_array_free_full(argv_ptr);
1147 dump_calc_estimates(
1148 disk_estimates_t * est)
1152 char *errmsg=NULL, *qerrmsg;
1154 for(level = 0; level < DUMP_LEVELS; level++) {
1155 if(est->est[level].needestimate) {
1156 dbprintf(_("getting size via dump for %s level %d\n"),
1157 est->qamname, level);
1158 size = getsize_dump(est->dle, level, &errmsg);
1162 g_printf(_("%s %d SIZE %lld\n"),
1163 est->qamname, level, (long long)size);
1164 if (errmsg && errmsg[0] != '\0') {
1165 if(am_has_feature(g_options->features,
1166 fe_rep_sendsize_quoted_error)) {
1167 qerrmsg = quote_string(errmsg);
1168 dbprintf(_("errmsg is %s\n"), errmsg);
1169 g_printf("%s %d ERROR %s\n",
1170 est->qamname, level, qerrmsg);
1177 amfunlock(1, "size");
1184 smbtar_calc_estimates(
1185 disk_estimates_t * est)
1189 char *errmsg = NULL, *qerrmsg;
1191 for(level = 0; level < DUMP_LEVELS; level++) {
1192 if(est->est[level].needestimate) {
1193 dbprintf(_("getting size via smbclient for %s level %d\n"),
1194 est->qamname, level);
1195 size = getsize_smbtar(est->dle, level, &errmsg);
1199 g_printf(_("%s %d SIZE %lld\n"),
1200 est->qamname, level, (long long)size);
1201 if (errmsg && errmsg[0] != '\0') {
1202 if(am_has_feature(g_options->features,
1203 fe_rep_sendsize_quoted_error)) {
1204 qerrmsg = quote_string(errmsg);
1205 dbprintf(_("errmsg is %s\n"), errmsg);
1206 g_printf("%s %d ERROR %s\n",
1207 est->qamname, level, qerrmsg);
1214 amfunlock(1, "size");
1222 gnutar_calc_estimates(
1223 disk_estimates_t * est)
1227 char *errmsg = NULL, *qerrmsg;
1229 for(level = 0; level < DUMP_LEVELS; level++) {
1230 if (est->est[level].needestimate) {
1231 dbprintf(_("getting size via gnutar for %s level %d\n"),
1232 est->qamname, level);
1233 size = getsize_gnutar(est->dle, level,
1234 est->est[level].dumpsince,
1239 g_printf(_("%s %d SIZE %lld\n"),
1240 est->qamname, level, (long long)size);
1241 if (errmsg && errmsg[0] != '\0') {
1242 if(am_has_feature(g_options->features,
1243 fe_rep_sendsize_quoted_error)) {
1244 qerrmsg = quote_string(errmsg);
1245 dbprintf(_("errmsg is %s\n"), errmsg);
1246 g_printf(_("%s %d ERROR %s\n"),
1247 est->qamname, level, qerrmsg);
1254 amfunlock(1, "size");
1260 typedef struct regex_scale_s {
1266 regex_scale_t re_size[] = {
1268 {" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1269 {" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1270 {" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
1271 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
1272 {"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
1273 {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1274 {"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
1275 {"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
1276 {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
1277 {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
1278 {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1280 {"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
1281 {"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
1282 {"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
1283 {" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1285 #ifdef HAVE_DUMP_ESTIMATE
1286 {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1287 /* DU 3.2g dump -E */
1288 {"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
1289 {"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
1294 {"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
1298 {"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
1299 {" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
1303 {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
1307 {"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
1311 #if SAMBA_VERSION >= 2
1312 #define SAMBA_DEBUG_LEVEL "0"
1313 {"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
1315 #define SAMBA_DEBUG_LEVEL "3"
1316 {"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
1330 int pipefd[2], nullfd, stdoutfd, killctl[2];
1334 char *dumpkeys = NULL;
1335 char *device = NULL;
1336 char *fstype = NULL;
1340 char *rundump_cmd = NULL;
1341 char level_str[NUM_STR_SIZE];
1344 char *qdisk = quote_string(dle->disk);
1347 amwait_t wait_status;
1348 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1353 return -2; /* planner will not even consider this level */
1355 g_snprintf(level_str, SIZEOF(level_str), "%d", level);
1357 device = amname_to_devname(dle->device);
1358 qdevice = quote_string(device);
1359 fstype = amname_to_fstype(dle->device);
1361 dbprintf(_("calculating for device %s with %s\n"),
1364 cmd = vstralloc(amlibexecdir, "/rundump", NULL);
1365 rundump_cmd = stralloc(cmd);
1366 if (g_options->config)
1367 config = g_options->config;
1369 config = "NOCONFIG";
1370 if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1371 *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
1373 dbprintf("%s\n", *errmsg);
1375 amfree(rundump_cmd);
1382 pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1383 if (pipe(pipefd) < 0) {
1384 *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
1386 dbprintf("%s\n", *errmsg);
1388 amfree(rundump_cmd);
1396 #ifdef XFSDUMP /* { */
1398 if (strcmp(fstype, "xfs") == 0)
1403 name = stralloc(" (xfsdump)");
1404 dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
1405 cmd, name, level_str, qdevice);
1409 #ifdef VXDUMP /* { */
1411 if (strcmp(fstype, "vxfs") == 0)
1417 name = stralloc(" (vxdump)");
1419 name = stralloc("");
1420 cmd = newstralloc(cmd, VXDUMP);
1421 config = skip_argument;
1424 dumpkeys = vstralloc(level_str, "s", "f", NULL);
1425 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1426 cmd, name, dumpkeys, qdevice);
1430 #ifdef VDUMP /* { */
1432 if (strcmp(fstype, "advfs") == 0)
1437 name = stralloc(" (vdump)");
1438 dumpkeys = vstralloc(level_str, "b", "f", NULL);
1439 dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1440 cmd, name, dumpkeys, qdevice);
1446 # ifdef USE_RUNDUMP /* { */
1447 # ifdef AIX_BACKUP /* { */
1448 name = stralloc(" (backup)");
1450 name = vstralloc(" (", DUMP, ")", NULL);
1453 name = stralloc("");
1454 cmd = newstralloc(cmd, DUMP);
1455 config = skip_argument;
1459 # ifdef AIX_BACKUP /* { */
1460 dumpkeys = vstralloc("-", level_str, "f", NULL);
1461 dbprintf(_("running \"%s%s %s - %s\"\n"),
1462 cmd, name, dumpkeys, qdevice);
1464 # ifdef HAVE_DUMP_ESTIMATE
1465 # define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1467 # define PARAM_DUMP_ESTIMATE ""
1469 # ifdef HAVE_HONOR_NODUMP
1470 # define PARAM_HONOR_NODUMP "h"
1472 # define PARAM_HONOR_NODUMP ""
1474 dumpkeys = vstralloc(level_str,
1475 PARAM_DUMP_ESTIMATE,
1479 # ifdef HAVE_DUMP_ESTIMATE
1480 stdoutfd = pipefd[1];
1483 # ifdef HAVE_HONOR_NODUMP /* { */
1484 dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1485 cmd, name, dumpkeys, qdevice);
1487 dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1488 cmd, name, dumpkeys, qdevice);
1495 error(_("no dump program available"));
1499 if (pipe(killctl) < 0) {
1500 dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1501 /* Message will be printed later... */
1502 killctl[0] = killctl[1] = -1;
1505 start_time = curclock();
1506 switch(dumppid = fork()) {
1508 *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1510 dbprintf("%s\n", *errmsg);
1513 amfree(rundump_cmd);
1522 case 0: /* child process */
1525 else if (killctl[0] == -1 || killctl[1] == -1)
1526 dbprintf(_("Trying without killpgrp\n"));
1530 dbprintf(_("fork failed, trying without killpgrp\n"));
1536 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp", NULL);
1537 dbprintf(_("running %s\n"), killpgrp_cmd);
1538 dup2(killctl[0], 0);
1545 if (g_options->config)
1546 config = g_options->config;
1548 config = "NOCONFIG";
1550 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1552 dbprintf(_("cannot execute %s: %s\n"),
1553 killpgrp_cmd, strerror(errno));
1557 case 0: /* child process */
1566 if (killctl[0] != -1)
1568 if (killctl[1] != -1)
1574 if (strcmp(fstype, "xfs") == 0)
1579 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1580 level_str, "-", device, (char *)0, safe_env());
1582 execle(cmd, "xfsdump", "-F", "-J", "-l",
1583 level_str, "-", device, (char *)0, safe_env());
1588 if (strcmp(fstype, "vxfs") == 0)
1593 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1594 "-", device, (char *)0, safe_env());
1596 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1597 device, (char *)0, safe_env());
1602 if (strcmp(fstype, "advfs") == 0)
1607 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1608 device, (char *)0, safe_env());
1610 execle(cmd, "vdump", dumpkeys, "60", "-",
1611 device, (char *)0, safe_env());
1617 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1618 device, (char *)0, safe_env());
1620 execle(cmd, "backup", dumpkeys, "-",
1621 device, (char *)0, safe_env());
1624 execle(cmd, "rundump", config, "dump", dumpkeys,
1625 #ifdef HAVE_HONOR_NODUMP
1628 "1048576", "-", device, (char *)0, safe_env());
1630 execle(cmd, "dump", dumpkeys,
1631 #ifdef HAVE_HONOR_NODUMP
1634 "1048576", "-", device, (char *)0, safe_env());
1639 error(_("exec %s failed or no dump program available: %s"),
1640 cmd, strerror(errno));
1646 amfree(rundump_cmd);
1649 if (killctl[0] != -1)
1651 dumpout = fdopen(pipefd[0],"r");
1653 error(_("Can't fdopen: %s"), strerror(errno));
1657 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1658 if (line[0] == '\0')
1660 dbprintf("%s\n", line);
1661 size = handle_dumpline(line);
1662 if(size > (off_t)-1) {
1664 while ((line = agets(dumpout)) != NULL) {
1665 if (line[0] != '\0')
1670 dbprintf("%s\n", line);
1677 dbprintf(".....\n");
1678 dbprintf(_("estimate time for %s level %d: %s\n"),
1681 walltime_str(timessub(curclock(), start_time)));
1682 if(size == (off_t)-1) {
1683 *errmsg = vstrallocf(_("no size line match in %s%s output"),
1685 dbprintf(_("%s for %s\n"),
1688 dbprintf(".....\n");
1689 dbprintf(_("Run %s%s manually to check for errors\n"),
1691 } else if(size == (off_t)0 && level == 0) {
1692 dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1693 cmd, name, dle->disk);
1694 dbprintf(".....\n");
1696 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1702 if (killctl[1] != -1) {
1703 dbprintf(_("asking killpgrp to terminate\n"));
1705 for(s = 5; s > 0; --s) {
1707 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1713 * First, try to kill the dump process nicely. If it ignores us
1714 * for several seconds, hit it harder.
1716 dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1717 if (kill(-dumppid, SIGTERM) == -1) {
1718 dbprintf(_("kill failed: %s\n"), strerror(errno));
1720 /* Now check whether it dies */
1721 for(s = 5; s > 0; --s) {
1723 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1727 dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1728 if (kill(-dumppid, SIGKILL) == -1) {
1729 dbprintf(_("kill failed: %s\n"), strerror(errno));
1731 for(s = 5; s > 0; --s) {
1733 if (waitpid(dumppid, NULL, WNOHANG) != -1)
1737 dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1738 waitpid(dumppid, &wait_status, 0);
1739 if (WIFSIGNALED(wait_status)) {
1740 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1741 cmd, WTERMSIG(wait_status), dbfn());
1742 } else if (WIFEXITED(wait_status)) {
1743 if (WEXITSTATUS(wait_status) != 0) {
1744 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1745 cmd, WEXITSTATUS(wait_status), dbfn());
1750 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1753 dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1778 int pipefd = -1, nullfd = -1, passwdfd = -1;
1782 char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1783 char *share = NULL, *subdir = NULL;
1790 char *error_pn = NULL;
1791 char *qdisk = quote_string(dle->disk);
1792 amwait_t wait_status;
1794 error_pn = stralloc2(get_pname(), "-smbclient");
1797 return -2; /* planner will not even consider this level */
1799 parsesharename(dle->device, &share, &subdir);
1803 set_pname(error_pn);
1805 error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1808 if ((subdir) && (SAMBA_VERSION < 2)) {
1811 set_pname(error_pn);
1813 error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1816 if ((user_and_password = findpass(share, &domain)) == NULL) {
1819 memset(domain, '\0', strlen(domain));
1822 set_pname(error_pn);
1824 error(_("cannot find password for %s"), dle->disk);
1827 lpass = strlen(user_and_password);
1828 if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1829 memset(user_and_password, '\0', (size_t)lpass);
1830 amfree(user_and_password);
1832 memset(domain, '\0', strlen(domain));
1835 set_pname(error_pn);
1837 error(_("password field not \'user%%pass\' for %s"), dle->disk);
1841 pwtext_len = strlen(pwtext);
1842 if ((sharename = makesharename(share, 0)) == NULL) {
1843 memset(user_and_password, '\0', (size_t)lpass);
1844 amfree(user_and_password);
1846 memset(domain, '\0', strlen(domain));
1849 set_pname(error_pn);
1851 error(_("cannot make share name of %s"), share);
1854 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1855 memset(user_and_password, '\0', (size_t)lpass);
1856 amfree(user_and_password);
1858 memset(domain, '\0', strlen(domain));
1861 set_pname(error_pn);
1864 error(_("could not open /dev/null: %s\n"),
1869 #if SAMBA_VERSION >= 2
1871 tarkeys = "archive 0;recurse;du";
1873 tarkeys = "archive 1;recurse;du";
1876 tarkeys = "archive 0;recurse;dir";
1878 tarkeys = "archive 1;recurse;dir";
1881 start_time = curclock();
1883 if (pwtext_len > 0) {
1884 pw_fd_env = "PASSWD_FD";
1886 pw_fd_env = "dummy_PASSWD_FD";
1888 dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
1889 &nullfd, &nullfd, &pipefd,
1890 pw_fd_env, &passwdfd,
1893 "-d", SAMBA_DEBUG_LEVEL,
1894 *user_and_password ? "-U" : skip_argument,
1895 *user_and_password ? user_and_password : skip_argument,
1897 domain ? "-W" : skip_argument,
1898 domain ? domain : skip_argument,
1899 #if SAMBA_VERSION >= 2
1900 subdir ? "-D" : skip_argument,
1901 subdir ? subdir : skip_argument,
1906 memset(domain, '\0', strlen(domain));
1910 if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
1911 int save_errno = errno;
1913 memset(user_and_password, '\0', (size_t)lpass);
1914 amfree(user_and_password);
1916 set_pname(error_pn);
1918 error(_("password write failed: %s"), strerror(save_errno));
1921 memset(user_and_password, '\0', (size_t)lpass);
1922 amfree(user_and_password);
1928 dumpout = fdopen(pipefd,"r");
1930 error(_("Can't fdopen: %s"), strerror(errno));
1934 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1935 if (line[0] == '\0')
1937 dbprintf("%s\n", line);
1938 size = handle_dumpline(line);
1941 while ((line = agets(dumpout)) != NULL) {
1942 if (line[0] != '\0')
1947 dbprintf("%s\n", line);
1954 dbprintf(".....\n");
1955 dbprintf(_("estimate time for %s level %d: %s\n"),
1958 walltime_str(timessub(curclock(), start_time)));
1959 if(size == (off_t)-1) {
1960 *errmsg = vstrallocf(_("no size line match in %s output"),
1962 dbprintf(_("%s for %s\n"),
1964 dbprintf(".....\n");
1965 } else if(size == (off_t)0 && level == 0) {
1966 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1967 SAMBA_CLIENT, dle->disk);
1968 dbprintf(".....\n");
1970 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1975 kill(-dumppid, SIGTERM);
1977 dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1978 waitpid(dumppid, &wait_status, 0);
1979 if (WIFSIGNALED(wait_status)) {
1980 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1981 SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
1982 } else if (WIFEXITED(wait_status)) {
1983 if (WEXITSTATUS(wait_status) != 0) {
1984 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1985 SAMBA_CLIENT, WEXITSTATUS(wait_status),
1991 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1992 SAMBA_CLIENT, dbfn());
1994 dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
2014 int pipefd = -1, nullfd = -1;
2016 off_t size = (off_t)-1;
2017 FILE *dumpout = NULL;
2018 char *incrname = NULL;
2019 char *basename = NULL;
2020 char *dirname = NULL;
2021 char *inputname = NULL;
2026 char *command = NULL;
2027 char dumptimestr[80];
2031 GPtrArray *argv_ptr = g_ptr_array_new();
2032 char *file_exclude = NULL;
2033 char *file_include = NULL;
2038 char *qdisk = quote_string(dle->disk);
2039 char *gnutar_list_dir;
2040 amwait_t wait_status;
2041 char tmppath[PATH_MAX];
2044 return -2; /* planner will not even consider this level */
2046 if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
2047 if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
2048 if(dle->include_file) nb_include += dle->include_file->nb_element;
2049 if(dle->include_list) nb_include += dle->include_list->nb_element;
2051 if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
2052 if(nb_include > 0) file_include = build_include(dle, 0);
2054 gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
2055 if (strlen(gnutar_list_dir) == 0)
2056 gnutar_list_dir = NULL;
2057 if (gnutar_list_dir) {
2058 char number[NUM_STR_SIZE];
2060 char *sdisk = sanitise_filename(dle->disk);
2062 basename = vstralloc(gnutar_list_dir,
2064 g_options->hostname,
2069 g_snprintf(number, SIZEOF(number), "%d", level);
2070 incrname = vstralloc(basename, "_", number, ".new", NULL);
2074 * Open the listed incremental file from the previous level. Search
2075 * backward until one is found. If none are found (which will also
2076 * be true for a level 0), arrange to read from /dev/null.
2080 while (infd == -1) {
2081 if (--baselevel >= 0) {
2082 g_snprintf(number, SIZEOF(number), "%d", baselevel);
2083 inputname = newvstralloc(inputname,
2084 basename, "_", number, NULL);
2086 inputname = newstralloc(inputname, "/dev/null");
2088 if ((infd = open(inputname, O_RDONLY)) == -1) {
2090 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
2091 inputname, strerror(errno));
2092 dbprintf("%s\n", *errmsg);
2093 if (baselevel < 0) {
2101 * Copy the previous listed incremental file to the new one.
2103 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
2104 *errmsg = vstrallocf(_("opening %s: %s"),
2105 incrname, strerror(errno));
2106 dbprintf("%s\n", *errmsg);
2110 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
2111 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
2112 *errmsg = vstrallocf(_("writing to %s: %s"),
2113 incrname, strerror(errno));
2114 dbprintf("%s\n", *errmsg);
2120 *errmsg = vstrallocf(_("reading from %s: %s"),
2121 inputname, strerror(errno));
2122 dbprintf("%s\n", *errmsg);
2126 if (close(infd) != 0) {
2127 *errmsg = vstrallocf(_("closing %s: %s"),
2128 inputname, strerror(errno));
2129 dbprintf("%s\n", *errmsg);
2132 if (close(outfd) != 0) {
2133 *errmsg = vstrallocf(_("closing %s: %s"),
2134 incrname, strerror(errno));
2135 dbprintf("%s\n", *errmsg);
2143 gmtm = gmtime(&dumpsince);
2144 g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2145 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2146 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2147 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2149 dirname = amname_to_dirname(dle->device);
2151 cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
2152 g_ptr_array_add(argv_ptr, stralloc("runtar"));
2153 if (g_options->config)
2154 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2156 g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
2159 g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
2161 g_ptr_array_add(argv_ptr, stralloc("tar"));
2163 g_ptr_array_add(argv_ptr, stralloc("--create"));
2164 g_ptr_array_add(argv_ptr, stralloc("--file"));
2165 g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
2166 /* use --numeric-owner for estimates, to reduce the number of user/group
2167 * lookups required */
2168 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
2169 g_ptr_array_add(argv_ptr, stralloc("--directory"));
2170 canonicalize_pathname(dirname, tmppath);
2171 g_ptr_array_add(argv_ptr, stralloc(tmppath));
2172 g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
2173 if (gnutar_list_dir) {
2174 g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
2175 g_ptr_array_add(argv_ptr, stralloc(incrname));
2177 g_ptr_array_add(argv_ptr, stralloc("--incremental"));
2178 g_ptr_array_add(argv_ptr, stralloc("--newer"));
2179 g_ptr_array_add(argv_ptr, stralloc(dumptimestr));
2181 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2182 /* --atime-preserve causes gnutar to call
2183 * utime() after reading files in order to
2184 * adjust their atime. However, utime()
2185 * updates the file's ctime, so incremental
2186 * dumps will think the file has changed. */
2187 g_ptr_array_add(argv_ptr, stralloc("--atime-preserve"));
2189 g_ptr_array_add(argv_ptr, stralloc("--sparse"));
2190 g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
2191 g_ptr_array_add(argv_ptr, stralloc("--totals"));
2194 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
2195 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
2199 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
2200 g_ptr_array_add(argv_ptr, stralloc(file_include));
2203 g_ptr_array_add(argv_ptr, stralloc("."));
2205 g_ptr_array_add(argv_ptr, NULL);
2207 start_time = curclock();
2209 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2210 *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2212 dbprintf("%s\n", *errmsg);
2216 command = (char *)g_ptr_array_index(argv_ptr, 0);
2217 dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
2218 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
2220 dumpout = fdopen(pipefd,"r");
2222 error(_("Can't fdopen: %s"), strerror(errno));
2226 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2227 if (line[0] == '\0')
2229 dbprintf("%s\n", line);
2230 size = handle_dumpline(line);
2231 if(size > (off_t)-1) {
2233 while ((line = agets(dumpout)) != NULL) {
2234 if (line[0] != '\0') {
2240 dbprintf("%s\n", line);
2248 dbprintf(".....\n");
2249 dbprintf(_("estimate time for %s level %d: %s\n"),
2252 walltime_str(timessub(curclock(), start_time)));
2253 if(size == (off_t)-1) {
2254 *errmsg = vstrallocf(_("no size line match in %s output"),
2256 dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2257 dbprintf(".....\n");
2258 } else if(size == (off_t)0 && level == 0) {
2259 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2260 command, dle->disk);
2261 dbprintf(".....\n");
2263 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2268 kill(-dumppid, SIGTERM);
2270 dbprintf(_("waiting for %s \"%s\" child\n"),
2272 waitpid(dumppid, &wait_status, 0);
2273 if (WIFSIGNALED(wait_status)) {
2274 *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2275 cmd, WTERMSIG(wait_status), dbfn());
2276 } else if (WIFEXITED(wait_status)) {
2277 if (WEXITSTATUS(wait_status) != 0) {
2278 *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2279 cmd, WEXITSTATUS(wait_status), dbfn());
2284 *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2287 dbprintf(_("after %s %s wait\n"), command, qdisk);
2298 g_ptr_array_free_full(argv_ptr);
2301 amfree(file_exclude);
2302 amfree(file_include);
2314 getsize_application_api(
2315 disk_estimates_t *est,
2318 backup_support_option_t *bsu)
2320 dle_t *dle = est->dle;
2321 int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
2323 off_t size = (off_t)-1;
2331 GPtrArray *argv_ptr = g_ptr_array_new();
2332 char *newoptstr = NULL;
2335 char *qdisk = quote_string(dle->disk);
2336 char *qamdevice = quote_string(dle->device);
2337 amwait_t wait_status;
2338 char levelstr[NUM_STR_SIZE];
2341 char *errmsg = NULL;
2342 estimate_t estimate;
2345 cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
2347 g_ptr_array_add(argv_ptr, stralloc(dle->program));
2348 g_ptr_array_add(argv_ptr, stralloc("estimate"));
2349 if (bsu->message_line == 1) {
2350 g_ptr_array_add(argv_ptr, stralloc("--message"));
2351 g_ptr_array_add(argv_ptr, stralloc("line"));
2353 if (g_options->config && bsu->config == 1) {
2354 g_ptr_array_add(argv_ptr, stralloc("--config"));
2355 g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2357 if (g_options->hostname && bsu->host == 1) {
2358 g_ptr_array_add(argv_ptr, stralloc("--host"));
2359 g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
2361 g_ptr_array_add(argv_ptr, stralloc("--device"));
2362 g_ptr_array_add(argv_ptr, stralloc(dle->device));
2363 if (dle->disk && bsu->disk == 1) {
2364 g_ptr_array_add(argv_ptr, stralloc("--disk"));
2365 g_ptr_array_add(argv_ptr, stralloc(dle->disk));
2367 for (j=0; j < nb_level; j++) {
2368 g_ptr_array_add(argv_ptr, stralloc("--level"));
2369 g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
2370 g_ptr_array_add(argv_ptr, stralloc(levelstr));
2372 /* find the first in ES_CLIENT and ES_CALCSIZE */
2373 estimate = ES_CLIENT;
2374 for (el = dle->estimatelist; el != NULL; el = el->next) {
2375 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
2376 if ((estimate == ES_CLIENT && bsu->client_estimate) ||
2377 (estimate == ES_CALCSIZE && bsu->calcsize))
2379 estimate = ES_CLIENT;
2381 if (estimate == ES_CALCSIZE && bsu->calcsize) {
2382 g_ptr_array_add(argv_ptr, stralloc("--calcsize"));
2385 application_property_add_to_argv(argv_ptr, dle, bsu, g_options->features);
2387 for (scriptlist = dle->scriptlist; scriptlist != NULL;
2388 scriptlist = scriptlist->next) {
2389 script = (script_t *)scriptlist->data;
2390 if (script->result && script->result->proplist) {
2391 property_add_to_argv(argv_ptr, script->result->proplist);
2395 g_ptr_array_add(argv_ptr, NULL);
2397 cmdline = stralloc(cmd);
2398 for(i = 1; i < argv_ptr->len-1; i++)
2399 cmdline = vstrextend(&cmdline, " ",
2400 (char *)g_ptr_array_index(argv_ptr, i), NULL);
2401 dbprintf("running: \"%s\"\n", cmdline);
2404 if (pipe(pipeerrfd) < 0) {
2405 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2410 if (pipe(pipeinfd) < 0) {
2411 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2416 if (pipe(pipeoutfd) < 0) {
2417 errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2422 start_time = curclock();
2424 switch(dumppid = fork()) {
2431 dup2(pipeinfd[0], 0);
2432 dup2(pipeoutfd[1], 1);
2433 dup2(pipeerrfd[1], 2);
2434 aclose(pipeinfd[1]);
2435 aclose(pipeoutfd[0]);
2436 aclose(pipeerrfd[0]);
2439 execve(cmd, (char **)argv_ptr->pdata, safe_env());
2440 error(_("exec %s failed: %s"), cmd, strerror(errno));
2445 aclose(pipeinfd[0]);
2446 aclose(pipeoutfd[1]);
2447 aclose(pipeerrfd[1]);
2448 aclose(pipeinfd[1]);
2450 dumpout = fdopen(pipeoutfd[0],"r");
2452 error(_("Can't fdopen: %s"), strerror(errno));
2456 for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2457 long long size1_ = (long long)0;
2458 long long size2_ = (long long)0;
2460 if (line[0] == '\0')
2462 dbprintf("%s\n", line);
2463 if (strncmp(line,"ERROR ", 6) == 0) {
2464 char *errmsg, *qerrmsg;
2466 errmsg = stralloc(line+6);
2467 qerrmsg = quote_string(errmsg);
2468 dbprintf(_("errmsg is %s\n"), errmsg);
2469 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2473 i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
2475 i = sscanf(line, "%lld %lld", &size1_, &size2_);
2478 char *errmsg, *qerrmsg;
2480 errmsg = vstrallocf(_("bad line %s"), line);
2481 qerrmsg = quote_string(errmsg);
2482 dbprintf(_("errmsg is %s\n"), errmsg);
2483 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2488 size1 = (off_t)size1_;
2489 size2 = (off_t)size2_;
2490 if (size1 <= 0 || size2 <=0)
2492 else if (size1 * size2 > 0)
2493 size = size1 * size2;
2494 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2498 g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
2502 dumperr = fdopen(pipeerrfd[0],"r");
2504 error(_("Can't fdopen: %s"), strerror(errno));
2508 while ((line = agets(dumperr)) != NULL) {
2509 if (strlen(line) > 0) {
2510 char *err = g_strdup_printf(_("Application '%s': %s"),
2511 dle->program, line);
2512 char *qerr = quote_string(err);
2513 for (j=0; j < nb_level; j++) {
2514 fprintf(stdout, "%s %d ERROR %s\n",
2515 est->qamname, levels[j], qerr);
2517 dbprintf("ERROR %s", qerr);
2524 dbprintf(".....\n");
2525 if (nb_level == 1) {
2526 dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
2527 levels[0], walltime_str(timessub(curclock(), start_time)));
2529 dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
2530 walltime_str(timessub(curclock(), start_time)));
2533 kill(-dumppid, SIGTERM);
2535 dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2536 waitpid(dumppid, &wait_status, 0);
2537 if (WIFSIGNALED(wait_status)) {
2538 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2539 cmd, WTERMSIG(wait_status), dbfn());
2540 } else if (WIFEXITED(wait_status)) {
2541 if (WEXITSTATUS(wait_status) != 0) {
2542 errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2543 WEXITSTATUS(wait_status), dbfn());
2548 errmsg = vstrallocf(_("%s got bad exit: see %s"),
2551 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2559 g_ptr_array_free_full(argv_ptr);
2564 char *qerrmsg = quote_string(errmsg);
2565 dbprintf(_("errmsg is %s\n"), errmsg);
2566 for (j=0; j < nb_level; j++) {
2567 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
2577 * Returns the value of the first integer in a string.
2589 while(ch && !isdigit(ch)) ch = *str++;
2591 while(isdigit(ch) || (ch == '.')) ch = *str++;
2600 * Checks the dump output line against the error and size regex tables.
2610 /* check for size match */
2612 for(rp = re_size; rp->regex != NULL; rp++) {
2613 if(match(rp->regex, str)) {
2614 size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2616 size = 1.0; /* found on NeXT -- sigh */