2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
4 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
28 * $Id: amgtar.c 8888 2007-10-02 13:40:42Z martineau $
30 * send estimated backup sizes using dump
35 * GNUTAR-PATH (default GNUTAR)
36 * GNUTAR-LISTDIR (default CNF_GNUTAR_LIST_DIR)
37 * DIRECTORY (no default, if set, the backup will be from that directory
38 * instead of from the --device)
39 * ONE-FILE-SYSTEM (default YES)
40 * SPARSE (default YES)
41 * ATIME-PRESERVE (default YES)
42 * CHECK-DEVICE (default YES)
43 * NO-UNQUOTE (default NO)
45 * SELINUX (default NO)
58 * EXIT-HANDLING (1=GOOD 2=BAD)
59 * TAR-BLOCKSIZE (default does not add --blocking-factor option,
60 * using tar's default)
66 #include "pipespawn.h"
67 #include "amfeatures.h"
71 #include "client_util.h"
75 int debug_application = 1;
76 #define application_debug(i, ...) do { \
77 if ((i) <= debug_application) { \
78 dbprintf(__VA_ARGS__); \
82 static amregex_t init_re_table[] = {
83 /* tar prints the size in bytes */
84 AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1, 1),
85 AM_NORMAL_RE("^could not open conf file"),
86 AM_NORMAL_RE("^Elapsed time:"),
87 AM_NORMAL_RE("^Throughput"),
88 AM_IGNORE_RE(": Directory is new$"),
89 AM_IGNORE_RE(": Directory has been renamed"),
91 /* GNU tar 1.13.17 will print this warning when (not) backing up a
93 AM_NORMAL_RE(": socket ignored$"),
95 /* GNUTAR produces a few error messages when files are modified or
96 removed while it is running. They may cause data to be lost, but
97 then they may not. We shouldn't consider them NORMAL until
98 further investigation. */
99 AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
100 AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
101 AM_NORMAL_RE(": Error exit delayed from previous errors"),
103 /* catch-all: DMP_STRANGE is returned for all other lines */
106 static amregex_t *re_table;
108 /* local functions */
109 int main(int argc, char **argv);
111 typedef struct application_argument_s {
119 GSList *command_options;
120 char *include_list_glob;
121 char *exclude_list_glob;
127 } application_argument_t;
129 enum { CMD_ESTIMATE, CMD_BACKUP };
131 static void amgtar_support(application_argument_t *argument);
132 static void amgtar_selfcheck(application_argument_t *argument);
133 static void amgtar_estimate(application_argument_t *argument);
134 static void amgtar_backup(application_argument_t *argument);
135 static void amgtar_restore(application_argument_t *argument);
136 static void amgtar_validate(application_argument_t *argument);
137 static void amgtar_build_exinclude(dle_t *dle, int verbose,
138 int *nb_exclude, char **file_exclude,
139 int *nb_include, char **file_include);
140 static char *amgtar_get_incrname(application_argument_t *argument, int level,
141 FILE *mesgstream, int command);
142 static void check_no_check_device(void);
143 static GPtrArray *amgtar_build_argv(application_argument_t *argument,
144 char *incrname, char **file_exclude,
145 char **file_include, int command);
146 static char *gnutar_path;
147 static char *gnutar_listdir;
148 static char *gnutar_directory;
149 static int gnutar_onefilesystem;
150 static int gnutar_atimepreserve;
151 static int gnutar_acls;
152 static int gnutar_selinux;
153 static int gnutar_xattrs;
154 static int gnutar_checkdevice;
155 static int gnutar_no_unquote;
156 static int gnutar_sparse;
157 static GSList *normal_message = NULL;
158 static GSList *ignore_message = NULL;
159 static GSList *strange_message = NULL;
160 static char *exit_handling;
161 static int exit_value[256];
163 static struct option long_options[] = {
164 {"config" , 1, NULL, 1},
165 {"host" , 1, NULL, 2},
166 {"disk" , 1, NULL, 3},
167 {"device" , 1, NULL, 4},
168 {"level" , 1, NULL, 5},
169 {"index" , 1, NULL, 6},
170 {"message" , 1, NULL, 7},
171 {"collection" , 0, NULL, 8},
172 {"record" , 0, NULL, 9},
173 {"gnutar-path" , 1, NULL, 10},
174 {"gnutar-listdir" , 1, NULL, 11},
175 {"one-file-system" , 1, NULL, 12},
176 {"sparse" , 1, NULL, 13},
177 {"atime-preserve" , 1, NULL, 14},
178 {"check-device" , 1, NULL, 15},
179 {"include-file" , 1, NULL, 16},
180 {"include-list" , 1, NULL, 17},
181 {"include-optional", 1, NULL, 18},
182 {"exclude-file" , 1, NULL, 19},
183 {"exclude-list" , 1, NULL, 20},
184 {"exclude-optional", 1, NULL, 21},
185 {"directory" , 1, NULL, 22},
186 {"normal" , 1, NULL, 23},
187 {"ignore" , 1, NULL, 24},
188 {"strange" , 1, NULL, 25},
189 {"exit-handling" , 1, NULL, 26},
190 {"calcsize" , 0, NULL, 27},
191 {"tar-blocksize" , 1, NULL, 28},
192 {"no-unquote" , 1, NULL, 29},
193 {"acls" , 1, NULL, 30},
194 {"selinux" , 1, NULL, 31},
195 {"xattrs" , 1, NULL, 32},
196 {"command-options" , 1, NULL, 33},
197 {"include-list-glob", 1, NULL, 34},
198 {"exclude-list-glob", 1, NULL, 35},
199 {"verbose" , 1, NULL, 36},
200 {"ignore-zeros" , 1, NULL, 37},
209 char *result = malloc(4*strlen(str)+1);
214 for (s = str; *s != '\0'; s++) {
222 } else if (c == '?') {
226 } else if (c == 'a') {
230 } else if (c == 'b') {
234 } else if (c == 'f') {
238 } else if (c == 'n') {
243 } else if (c == 'r') {
248 } else if (c == 't') {
252 } else if (c == 'v') {
256 } else if (c >= '0' && c <= '9') {
260 if (c >= '0' && c <= '9') {
264 if (c >= '0' && c <= '9') {
274 } else if (*s == '?') {
277 } else if (*s == '*' || *s == '[') {
295 application_argument_t argument;
299 gnutar_path = GNUTAR;
303 gnutar_listdir = NULL;
304 gnutar_directory = NULL;
305 gnutar_onefilesystem = 1;
306 gnutar_atimepreserve = 1;
310 gnutar_checkdevice = 1;
312 gnutar_no_unquote = 0;
313 exit_handling = NULL;
318 * Configure program for internationalization:
319 * 1) Only set the message locale for now.
320 * 2) Set textdomain for all amanda related programs to "amanda"
321 * We don't want to be forced to support dozens of message catalogs.
323 setlocale(LC_MESSAGES, "C");
324 textdomain("amanda");
327 printf("ERROR no command given to amgtar\n");
328 error(_("No command given to amgtar"));
331 /* drop root privileges */
332 if (!set_root_privs(0)) {
333 if (strcmp(argv[1], "selfcheck") == 0) {
334 printf("ERROR amgtar must be run setuid root\n");
336 error(_("amgtar must be run setuid root"));
343 /* Don't die when child closes pipe */
344 signal(SIGPIPE, SIG_IGN);
346 #if defined(USE_DBMALLOC)
347 malloc_size_1 = malloc_inuse(&malloc_hist_1);
350 add_amanda_log_handler(amanda_log_stderr);
351 add_amanda_log_handler(amanda_log_syslog);
352 dbopen(DBG_SUBDIR_CLIENT);
354 dbprintf(_("version %s\n"), VERSION);
356 config_init(CONFIG_INIT_CLIENT, NULL);
358 //check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
360 //RUNNING_AS_CLIENT_LOGIN from selfcheck, sendsize, sendbackup
365 argument.config = NULL;
366 argument.host = NULL;
367 argument.message = 0;
368 argument.collection = 0;
369 argument.calcsize = 0;
370 argument.tar_blocksize = NULL;
371 argument.level = NULL;
372 argument.command_options = NULL;
373 argument.include_list_glob = NULL;
374 argument.exclude_list_glob = NULL;
375 argument.verbose = 0;
376 argument.ignore_zeros = 1;
377 init_dle(&argument.dle);
378 argument.dle.record = 0;
381 int option_index = 0;
382 c = getopt_long (argc, argv, "", long_options, &option_index);
387 case 1: argument.config = stralloc(optarg);
389 case 2: argument.host = stralloc(optarg);
391 case 3: argument.dle.disk = stralloc(optarg);
393 case 4: argument.dle.device = stralloc(optarg);
395 case 5: argument.level = g_slist_append(argument.level,
396 GINT_TO_POINTER(atoi(optarg)));
398 case 6: argument.dle.create_index = 1;
400 case 7: argument.message = 1;
402 case 8: argument.collection = 1;
404 case 9: argument.dle.record = 1;
406 case 10: gnutar_path = stralloc(optarg);
408 case 11: gnutar_listdir = stralloc(optarg);
410 case 12: if (optarg && strcasecmp(optarg, "NO") == 0)
411 gnutar_onefilesystem = 0;
412 else if (optarg && strcasecmp(optarg, "YES") == 0)
413 gnutar_onefilesystem = 1;
414 else if (strcasecmp(command, "selfcheck") == 0)
415 printf(_("ERROR [%s: bad ONE-FILE-SYSTEM property value (%s)]\n"), get_pname(), optarg);
417 case 13: if (optarg && strcasecmp(optarg, "NO") == 0)
419 else if (optarg && strcasecmp(optarg, "YES") == 0)
421 else if (strcasecmp(command, "selfcheck") == 0)
422 printf(_("ERROR [%s: bad SPARSE property value (%s)]\n"), get_pname(), optarg);
424 case 14: if (optarg && strcasecmp(optarg, "NO") == 0)
425 gnutar_atimepreserve = 0;
426 else if (optarg && strcasecmp(optarg, "YES") == 0)
427 gnutar_atimepreserve = 1;
428 else if (strcasecmp(command, "selfcheck") == 0)
429 printf(_("ERROR [%s: bad ATIME-PRESERVE property value (%s)]\n"), get_pname(), optarg);
431 case 15: if (optarg && strcasecmp(optarg, "NO") == 0)
432 gnutar_checkdevice = 0;
433 else if (optarg && strcasecmp(optarg, "YES") == 0)
434 gnutar_checkdevice = 1;
435 else if (strcasecmp(command, "selfcheck") == 0)
436 printf(_("ERROR [%s: bad CHECK-DEVICE property value (%s)]\n"), get_pname(), optarg);
439 argument.dle.include_file =
440 append_sl(argument.dle.include_file, optarg);
443 argument.dle.include_list =
444 append_sl(argument.dle.include_list, optarg);
446 case 18: argument.dle.include_optional = 1;
449 argument.dle.exclude_file =
450 append_sl(argument.dle.exclude_file, optarg);
453 argument.dle.exclude_list =
454 append_sl(argument.dle.exclude_list, optarg);
456 case 21: argument.dle.exclude_optional = 1;
458 case 22: gnutar_directory = stralloc(optarg);
462 g_slist_append(normal_message, optarg);
466 g_slist_append(ignore_message, optarg);
470 g_slist_append(strange_message, optarg);
473 exit_handling = stralloc(optarg);
475 case 27: argument.calcsize = 1;
477 case 28: argument.tar_blocksize = stralloc(optarg);
479 case 29: if (optarg && strcasecmp(optarg, "NO") == 0)
480 gnutar_no_unquote = 0;
481 else if (optarg && strcasecmp(optarg, "YES") == 0)
482 gnutar_no_unquote = 1;
483 else if (strcasecmp(command, "selfcheck") == 0)
484 printf(_("ERROR [%s: bad No_UNQUOTE property value (%s)]\n"), get_pname(), optarg);
486 case 30: if (optarg && strcasecmp(optarg, "YES") == 0)
489 case 31: if (optarg && strcasecmp(optarg, "YES") == 0)
492 case 32: if (optarg && strcasecmp(optarg, "YES") == 0)
495 case 33: argument.command_options =
496 g_slist_append(argument.command_options,
500 argument.include_list_glob = stralloc(optarg);
503 argument.exclude_list_glob = stralloc(optarg);
505 case 36: if (optarg && strcasecmp(optarg, "YES") == 0)
506 argument.verbose = 1;
508 case 37: if (strcasecmp(optarg, "YES") != 0)
509 argument.ignore_zeros = 0;
517 if (!argument.dle.disk && argument.dle.device)
518 argument.dle.disk = stralloc(argument.dle.device);
519 if (!argument.dle.device && argument.dle.disk)
520 argument.dle.device = stralloc(argument.dle.disk);
522 argument.argc = argc - optind;
523 argument.argv = argv + optind;
525 if (argument.config) {
526 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
528 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
531 if (config_errors(NULL) >= CFGERR_ERRORS) {
532 g_critical(_("errors processing config file"));
535 if (!gnutar_listdir) {
536 gnutar_listdir = g_strdup(getconf_str(CNF_GNUTAR_LIST_DIR));
539 re_table = build_re_table(init_re_table, normal_message, ignore_message,
543 exit_value[i] = 1; /* BAD */
544 exit_value[0] = 0; /* GOOD */
545 exit_value[1] = 0; /* GOOD */
547 char *s = exit_handling;
549 char *r = strchr(s, '=');
552 if (j >= 0 && j < 256) {
554 if (strncasecmp(r, "GOOD", 4) == 0) {
559 s = strchr(s+1, ' ');
563 if (strlen(gnutar_listdir) == 0)
564 gnutar_listdir = NULL;
567 dbprintf("GNUTAR-PATH %s\n", gnutar_path);
569 dbprintf("GNUTAR-PATH is not set\n");
571 if (gnutar_listdir) {
572 dbprintf("GNUTAR-LISTDIR %s\n", gnutar_listdir);
574 dbprintf("GNUTAR-LISTDIR is not set\n");
576 if (gnutar_directory) {
577 dbprintf("DIRECTORY %s\n", gnutar_directory);
579 dbprintf("ONE-FILE-SYSTEM %s\n", gnutar_onefilesystem? "yes":"no");
580 dbprintf("SPARSE %s\n", gnutar_sparse? "yes":"no");
581 dbprintf("NO-UNQUOTE %s\n", gnutar_no_unquote? "yes":"no");
582 dbprintf("ATIME-PRESERVE %s\n", gnutar_atimepreserve? "yes":"no");
583 dbprintf("ACLS %s\n", gnutar_acls? "yes":"no");
584 dbprintf("SELINUX %s\n", gnutar_selinux? "yes":"no");
585 dbprintf("XATTRS %s\n", gnutar_xattrs? "yes":"no");
586 dbprintf("CHECK-DEVICE %s\n", gnutar_checkdevice? "yes":"no");
589 for (rp = re_table; rp->regex != NULL; rp++) {
591 case DMP_NORMAL : dbprintf("NORMAL %s\n", rp->regex); break;
592 case DMP_IGNORE : dbprintf("IGNORE %s\n", rp->regex); break;
593 case DMP_STRANGE: dbprintf("STRANGE %s\n", rp->regex); break;
594 case DMP_SIZE : dbprintf("SIZE %s\n", rp->regex); break;
595 case DMP_ERROR : dbprintf("ERROR %s\n", rp->regex); break;
600 if (strcmp(command, "support") == 0) {
601 amgtar_support(&argument);
602 } else if (strcmp(command, "selfcheck") == 0) {
603 amgtar_selfcheck(&argument);
604 } else if (strcmp(command, "estimate") == 0) {
605 amgtar_estimate(&argument);
606 } else if (strcmp(command, "backup") == 0) {
607 amgtar_backup(&argument);
608 } else if (strcmp(command, "restore") == 0) {
609 amgtar_restore(&argument);
610 } else if (strcmp(command, "validate") == 0) {
611 amgtar_validate(&argument);
613 dbprintf("Unknown command `%s'.\n", command);
614 fprintf(stderr, "Unknown command `%s'.\n", command);
622 application_argument_t *argument)
625 fprintf(stdout, "CONFIG YES\n");
626 fprintf(stdout, "HOST YES\n");
627 fprintf(stdout, "DISK YES\n");
628 fprintf(stdout, "MAX-LEVEL 399\n");
629 fprintf(stdout, "INDEX-LINE YES\n");
630 fprintf(stdout, "INDEX-XML NO\n");
631 fprintf(stdout, "MESSAGE-LINE YES\n");
632 fprintf(stdout, "MESSAGE-XML NO\n");
633 fprintf(stdout, "RECORD YES\n");
634 fprintf(stdout, "INCLUDE-FILE YES\n");
635 fprintf(stdout, "INCLUDE-LIST YES\n");
636 fprintf(stdout, "INCLUDE-LIST-GLOB YES\n");
637 fprintf(stdout, "INCLUDE-OPTIONAL YES\n");
638 fprintf(stdout, "EXCLUDE-FILE YES\n");
639 fprintf(stdout, "EXCLUDE-LIST YES\n");
640 fprintf(stdout, "EXCLUDE-LIST-GLOB YES\n");
641 fprintf(stdout, "EXCLUDE-OPTIONAL YES\n");
642 fprintf(stdout, "COLLECTION NO\n");
643 fprintf(stdout, "MULTI-ESTIMATE YES\n");
644 fprintf(stdout, "CALCSIZE YES\n");
645 fprintf(stdout, "CLIENT-ESTIMATE YES\n");
650 application_argument_t *argument)
652 if (argument->dle.disk) {
653 char *qdisk = quote_string(argument->dle.disk);
654 fprintf(stdout, "OK disk %s\n", qdisk);
658 printf("OK amgtar version %s\n", VERSION);
659 amgtar_build_exinclude(&argument->dle, 1, NULL, NULL, NULL, NULL);
661 printf("OK amgtar\n");
663 if (check_file(gnutar_path, X_OK)) {
665 GPtrArray *argv_ptr = g_ptr_array_new();
667 g_ptr_array_add(argv_ptr, gnutar_path);
668 g_ptr_array_add(argv_ptr, "--version");
669 g_ptr_array_add(argv_ptr, NULL);
671 gtar_version = get_first_line(argv_ptr);
674 for (gv = gtar_version; *gv && !g_ascii_isdigit(*gv); gv++);
675 printf("OK amgtar gtar-version %s\n", gv);
677 printf(_("ERROR [Can't get %s version]\n"), gnutar_path);
680 g_ptr_array_free(argv_ptr, TRUE);
681 amfree(gtar_version);
684 printf(_("ERROR [GNUTAR program not available]\n"));
688 if (gnutar_listdir && strlen(gnutar_listdir) == 0)
689 gnutar_listdir = NULL;
690 if (gnutar_listdir) {
691 check_dir(gnutar_listdir, R_OK|W_OK);
693 printf(_("ERROR [No GNUTAR-LISTDIR]\n"));
696 if (gnutar_directory) {
697 check_dir(gnutar_directory, R_OK);
698 } else if (argument->dle.device) {
699 check_dir(argument->dle.device, R_OK);
701 if (argument->calcsize) {
702 char *calcsize = vstralloc(amlibexecdir, "/", "calcsize", NULL);
703 check_file(calcsize, X_OK);
704 check_suid(calcsize);
712 application_argument_t *argument)
714 char *incrname = NULL;
719 FILE *dumpout = NULL;
723 char *qerrmsg = NULL;
725 amwait_t wait_status;
734 if (!argument->level) {
735 fprintf(stderr, "ERROR No level argument\n");
736 error(_("No level argument"));
738 if (!argument->dle.disk) {
739 fprintf(stderr, "ERROR No disk argument\n");
740 error(_("No disk argument"));
742 if (!argument->dle.device) {
743 fprintf(stderr, "ERROR No device argument\n");
744 error(_("No device argument"));
747 qdisk = quote_string(argument->dle.disk);
749 if (argument->calcsize) {
754 if (gnutar_directory) {
755 dirname = gnutar_directory;
757 dirname = argument->dle.device;
759 amgtar_build_exinclude(&argument->dle, 1,
760 &nb_exclude, &file_exclude,
761 &nb_include, &file_include);
763 run_calcsize(argument->config, "GNUTAR", argument->dle.disk, dirname,
764 argument->level, file_exclude, file_include);
766 if (argument->verbose == 0) {
768 unlink(file_exclude);
770 unlink(file_include);
776 errmsg = vstrallocf(_("GNUTAR-PATH not defined"));
780 if (!gnutar_listdir) {
781 errmsg = vstrallocf(_("GNUTAR-LISTDIR not defined"));
785 for (levels = argument->level; levels != NULL; levels = levels->next) {
786 level = GPOINTER_TO_INT(levels->data);
787 incrname = amgtar_get_incrname(argument, level, stdout, CMD_ESTIMATE);
788 cmd = stralloc(gnutar_path);
789 argv_ptr = amgtar_build_argv(argument, incrname, &file_exclude,
790 &file_include, CMD_ESTIMATE);
792 start_time = curclock();
794 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
795 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
800 tarpid = pipespawnv(cmd, STDERR_PIPE, 1,
801 &nullfd, &nullfd, &pipefd,
802 (char **)argv_ptr->pdata);
804 dumpout = fdopen(pipefd,"r");
806 error(_("Can't fdopen: %s"), strerror(errno));
811 while (size < 0 && (fgets(line, sizeof(line), dumpout) != NULL)) {
812 if (line[strlen(line)-1] == '\n') /* remove trailling \n */
813 line[strlen(line)-1] = '\0';
816 dbprintf("%s\n", line);
817 /* check for size match */
819 for(rp = re_table; rp->regex != NULL; rp++) {
820 if(match(rp->regex, line)) {
821 if (rp->typ == DMP_SIZE) {
822 size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
824 size = 1.0; /* found on NeXT -- sigh */
832 while (fgets(line, sizeof(line), dumpout) != NULL) {
833 dbprintf("%s", line);
837 dbprintf(_("estimate time for %s level %d: %s\n"),
840 walltime_str(timessub(curclock(), start_time)));
841 if(size == (off_t)-1) {
842 errmsg = vstrallocf(_("no size line match in %s output"), cmd);
843 dbprintf(_("%s for %s\n"), errmsg, qdisk);
845 } else if(size == (off_t)0 && argument->level == 0) {
846 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
847 cmd, argument->dle.disk);
850 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
855 kill(-tarpid, SIGTERM);
857 dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
858 waitpid(tarpid, &wait_status, 0);
859 if (WIFSIGNALED(wait_status)) {
860 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
861 cmd, WTERMSIG(wait_status), dbfn());
862 } else if (WIFEXITED(wait_status)) {
863 if (exit_value[WEXITSTATUS(wait_status)] == 1) {
864 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
865 cmd, WEXITSTATUS(wait_status), dbfn());
870 errmsg = vstrallocf(_("%s got bad exit: see %s"),
873 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
877 dbprintf("%s", errmsg);
878 fprintf(stdout, "ERROR %s\n", errmsg);
885 if (argument->verbose == 0) {
887 unlink(file_exclude);
889 unlink(file_include);
892 g_ptr_array_free_full(argv_ptr);
898 fprintf(stdout, "%d %lld 1\n", level, (long long)size);
904 qerrmsg = quote_string(errmsg);
906 dbprintf("%s", errmsg);
907 fprintf(stdout, "ERROR %s\n", qerrmsg);
915 application_argument_t *argument)
923 off_t dump_size = -1;
931 FILE *indexstream = NULL;
934 amwait_t wait_status;
940 mesgstream = fdopen(mesgf, "w");
942 error(_("error mesgstream(%d): %s\n"), mesgf, strerror(errno));
946 error(_("GNUTAR-PATH not defined"));
948 if (!gnutar_listdir) {
949 error(_("GNUTAR-LISTDIR not defined"));
952 if (!argument->level) {
953 fprintf(mesgstream, "? No level argument\n");
954 error(_("No level argument"));
956 if (!argument->dle.disk) {
957 fprintf(mesgstream, "? No disk argument\n");
958 error(_("No disk argument"));
960 if (!argument->dle.device) {
961 fprintf(mesgstream, "? No device argument\n");
962 error(_("No device argument"));
965 qdisk = quote_string(argument->dle.disk);
967 incrname = amgtar_get_incrname(argument,
968 GPOINTER_TO_INT(argument->level->data),
969 mesgstream, CMD_BACKUP);
970 cmd = stralloc(gnutar_path);
971 argv_ptr = amgtar_build_argv(argument, incrname, &file_exclude,
972 &file_include, CMD_BACKUP);
974 tarpid = pipespawnv(cmd, STDIN_PIPE|STDERR_PIPE, 1,
975 &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
976 /* close the write ends of the pipes */
980 if (argument->dle.create_index) {
981 indexstream = fdopen(indexf, "w");
983 error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
986 outstream = fdopen(outf, "r");
988 error(_("error outstream(%d): %s\n"), outf, strerror(errno));
991 while (fgets(line, sizeof(line), outstream) != NULL) {
992 if (line[strlen(line)-1] == '\n') /* remove trailling \n */
993 line[strlen(line)-1] = '\0';
994 if (*line == '.' && *(line+1) == '/') { /* filename */
995 if (argument->dle.create_index) {
996 fprintf(indexstream, "%s\n", &line[1]); /* remove . */
998 } else { /* message */
999 for(rp = re_table; rp->regex != NULL; rp++) {
1000 if(match(rp->regex, line)) {
1004 if(rp->typ == DMP_SIZE) {
1005 dump_size = (off_t)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
1031 dbprintf("%3d: %7s(%c): %s\n", rp->srcline, type, startchr, line);
1032 fprintf(mesgstream,"%c %s\n", startchr, line);
1036 waitpid(tarpid, &wait_status, 0);
1037 if (WIFSIGNALED(wait_status)) {
1038 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1039 cmd, WTERMSIG(wait_status), dbfn());
1040 } else if (WIFEXITED(wait_status)) {
1041 if (exit_value[WEXITSTATUS(wait_status)] == 1) {
1042 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1043 cmd, WEXITSTATUS(wait_status), dbfn());
1048 errmsg = vstrallocf(_("%s got bad exit: see %s"),
1051 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
1052 dbprintf(_("amgtar: %s: pid %ld\n"), cmd, (long)tarpid);
1054 dbprintf("%s", errmsg);
1055 g_fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
1058 if (!errmsg && incrname && strlen(incrname) > 4) {
1059 if (argument->dle.record) {
1061 nodotnew = stralloc(incrname);
1062 nodotnew[strlen(nodotnew)-4] = '\0';
1063 if (rename(incrname, nodotnew)) {
1064 dbprintf(_("%s: warning [renaming %s to %s: %s]\n"),
1065 get_pname(), incrname, nodotnew, strerror(errno));
1066 g_fprintf(mesgstream, _("? warning [renaming %s to %s: %s]\n"),
1067 incrname, nodotnew, strerror(errno));
1071 if (unlink(incrname) == -1) {
1072 dbprintf(_("%s: warning [unlink %s: %s]\n"),
1073 get_pname(), incrname, strerror(errno));
1074 g_fprintf(mesgstream, _("? warning [unlink %s: %s]\n"),
1075 incrname, strerror(errno));
1080 dbprintf("sendbackup: size %lld\n", (long long)dump_size);
1081 fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
1082 dbprintf("sendbackup: end\n");
1083 fprintf(mesgstream, "sendbackup: end\n");
1085 if (argument->dle.create_index)
1086 fclose(indexstream);
1090 if (argument->verbose == 0) {
1092 unlink(file_exclude);
1094 unlink(file_include);
1100 g_ptr_array_free_full(argv_ptr);
1105 application_argument_t *argument)
1108 GPtrArray *argv_ptr = g_ptr_array_new();
1112 char *include_filename = NULL;
1113 char *exclude_filename = NULL;
1117 error(_("GNUTAR-PATH not defined"));
1120 cmd = stralloc(gnutar_path);
1121 g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1122 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1123 if (gnutar_no_unquote)
1124 g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1126 g_ptr_array_add(argv_ptr, stralloc("--acls"));
1128 g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1130 g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1131 /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1132 if (argument->ignore_zeros) {
1133 g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1135 if (argument->tar_blocksize) {
1136 g_ptr_array_add(argv_ptr, stralloc("--blocking-factor"));
1137 g_ptr_array_add(argv_ptr, stralloc(argument->tar_blocksize));
1139 g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1140 g_ptr_array_add(argv_ptr, stralloc("-"));
1141 if (gnutar_directory) {
1142 struct stat stat_buf;
1143 if(stat(gnutar_directory, &stat_buf) != 0) {
1144 fprintf(stderr,"can not stat directory %s: %s\n", gnutar_directory, strerror(errno));
1147 if (!S_ISDIR(stat_buf.st_mode)) {
1148 fprintf(stderr,"%s is not a directory\n", gnutar_directory);
1151 if (access(gnutar_directory, W_OK) != 0) {
1152 fprintf(stderr, "Can't write to %s: %s\n", gnutar_directory, strerror(errno));
1155 g_ptr_array_add(argv_ptr, stralloc("--directory"));
1156 g_ptr_array_add(argv_ptr, stralloc(gnutar_directory));
1159 g_ptr_array_add(argv_ptr, stralloc("--wildcards"));
1160 if (argument->dle.exclude_list &&
1161 argument->dle.exclude_list->nb_element == 1) {
1165 int entry_in_exclude = 0;
1166 char line[2*PATH_MAX];
1169 if (argument->dle.disk) {
1170 sdisk = sanitise_filename(argument->dle.disk);
1172 sdisk = g_strdup_printf("no_dle-%d", getpid());
1174 exclude_filename= vstralloc(AMANDA_TMPDIR, "/", "exclude-", sdisk, NULL);
1175 exclude_list = fopen(argument->dle.exclude_list->first->name, "r");
1177 exclude = fopen(exclude_filename, "w");
1178 while (fgets(line, 2*PATH_MAX, exclude_list)) {
1180 line[strlen(line)-1] = '\0'; /* remove '\n' */
1181 escaped = escape_tar_glob(line, &in_argv);
1183 g_ptr_array_add(argv_ptr, "--exclude");
1184 g_ptr_array_add(argv_ptr, escaped);
1186 fprintf(exclude,"%s\n", escaped);
1192 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1193 g_ptr_array_add(argv_ptr, exclude_filename);
1196 if (argument->exclude_list_glob) {
1197 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1198 g_ptr_array_add(argv_ptr, stralloc(argument->exclude_list_glob));
1202 GPtrArray *argv_include = g_ptr_array_new();
1207 int entry_in_include = 0;
1209 if (argument->dle.disk) {
1210 sdisk = sanitise_filename(argument->dle.disk);
1212 sdisk = g_strdup_printf("no_dle-%d", getpid());
1214 include_filename = vstralloc(AMANDA_TMPDIR, "/", "include-", sdisk, NULL);
1215 include = fopen(include_filename, "w");
1216 if (argument->dle.include_list &&
1217 argument->dle.include_list->nb_element == 1) {
1218 char line[2*PATH_MAX];
1219 FILE *include_list = fopen(argument->dle.include_list->first->name, "r");
1220 while (fgets(line, 2*PATH_MAX, include_list)) {
1222 line[strlen(line)-1] = '\0'; /* remove '\n' */
1223 escaped = escape_tar_glob(line, &in_argv);
1225 g_ptr_array_add(argv_include, escaped);
1227 fprintf(include,"%s\n", escaped);
1234 for (j=1; j< argument->argc; j++) {
1235 char *escaped = escape_tar_glob(argument->argv[j], &in_argv);
1237 g_ptr_array_add(argv_include, escaped);
1239 fprintf(include,"%s\n", escaped);
1246 if (entry_in_include) {
1247 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1248 g_ptr_array_add(argv_ptr, include_filename);
1251 if (argument->include_list_glob) {
1252 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1253 g_ptr_array_add(argv_ptr, stralloc(argument->include_list_glob));
1256 for (i = 0; i < argv_include->len; i++) {
1257 g_ptr_array_add(argv_ptr, (char *)g_ptr_array_index(argv_include,i));
1260 g_ptr_array_add(argv_ptr, NULL);
1262 debug_executing(argv_ptr);
1266 case -1: error(_("%s: fork returned: %s"), get_pname(), strerror(errno));
1270 execve(cmd, (char **)argv_ptr->pdata, env);
1271 e = strerror(errno);
1272 error(_("error [exec %s: %s]"), cmd, e);
1277 waitpid(tarpid, NULL, 0);
1278 if (argument->verbose == 0) {
1279 if (exclude_filename)
1280 unlink(exclude_filename);
1281 if (include_filename)
1282 unlink(include_filename);
1288 application_argument_t *argument G_GNUC_UNUSED)
1291 GPtrArray *argv_ptr = g_ptr_array_new();
1297 dbprintf("GNUTAR-PATH not set; Piping to /dev/null\n");
1298 fprintf(stderr,"GNUTAR-PATH not set; Piping to /dev/null\n");
1302 cmd = stralloc(gnutar_path);
1303 g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1304 /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1305 g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1306 g_ptr_array_add(argv_ptr, stralloc("-tf"));
1307 g_ptr_array_add(argv_ptr, stralloc("-"));
1308 g_ptr_array_add(argv_ptr, NULL);
1310 debug_executing(argv_ptr);
1312 execve(cmd, (char **)argv_ptr->pdata, env);
1313 e = strerror(errno);
1314 dbprintf("failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1315 fprintf(stderr,"failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1317 while (read(0, buf, 32768) > 0) {
1322 amgtar_build_exinclude(
1326 char **file_exclude,
1328 char **file_include)
1332 char *exclude = NULL;
1333 char *include = NULL;
1335 if (dle->exclude_file) n_exclude += dle->exclude_file->nb_element;
1336 if (dle->exclude_list) n_exclude += dle->exclude_list->nb_element;
1337 if (dle->include_file) n_include += dle->include_file->nb_element;
1338 if (dle->include_list) n_include += dle->include_list->nb_element;
1340 if (n_exclude > 0) exclude = build_exclude(dle, verbose);
1341 if (n_include > 0) include = build_include(dle, verbose);
1344 *nb_exclude = n_exclude;
1346 *file_exclude = exclude;
1351 *nb_include = n_include;
1353 *file_include = include;
1359 amgtar_get_incrname(
1360 application_argument_t *argument,
1365 char *basename = NULL;
1366 char *incrname = NULL;
1369 char *inputname = NULL;
1370 char *errmsg = NULL;
1373 if (gnutar_listdir) {
1374 char number[NUM_STR_SIZE];
1376 char *sdisk = sanitise_filename(argument->dle.disk);
1378 basename = vstralloc(gnutar_listdir,
1385 snprintf(number, SIZEOF(number), "%d", level);
1386 incrname = vstralloc(basename, "_", number, ".new", NULL);
1390 * Open the listed incremental file from the previous level. Search
1391 * backward until one is found. If none are found (which will also
1392 * be true for a level 0), arrange to read from /dev/null.
1396 while (infd == -1) {
1397 if (--baselevel >= 0) {
1398 snprintf(number, SIZEOF(number), "%d", baselevel);
1399 inputname = newvstralloc(inputname,
1400 basename, "_", number, NULL);
1402 inputname = newstralloc(inputname, "/dev/null");
1404 if ((infd = open(inputname, O_RDONLY)) == -1) {
1406 errmsg = vstrallocf(_("amgtar: error opening %s: %s"),
1407 inputname, strerror(errno));
1408 dbprintf("%s\n", errmsg);
1409 if (baselevel < 0) {
1410 if (command == CMD_ESTIMATE) {
1411 fprintf(mesgstream, "ERROR %s\n", errmsg);
1413 fprintf(mesgstream, "? %s\n", errmsg);
1422 * Copy the previous listed incremental file to the new one.
1424 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1425 errmsg = vstrallocf(_("error opening %s: %s"),
1426 incrname, strerror(errno));
1427 dbprintf("%s\n", errmsg);
1428 if (command == CMD_ESTIMATE) {
1429 fprintf(mesgstream, "ERROR %s\n", errmsg);
1431 fprintf(mesgstream, "? %s\n", errmsg);
1436 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1437 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
1438 errmsg = vstrallocf(_("writing to %s: %s"),
1439 incrname, strerror(errno));
1440 dbprintf("%s\n", errmsg);
1446 errmsg = vstrallocf(_("reading from %s: %s"),
1447 inputname, strerror(errno));
1448 dbprintf("%s\n", errmsg);
1452 if (close(infd) != 0) {
1453 errmsg = vstrallocf(_("closing %s: %s"),
1454 inputname, strerror(errno));
1455 dbprintf("%s\n", errmsg);
1458 if (close(outfd) != 0) {
1459 errmsg = vstrallocf(_("closing %s: %s"),
1460 incrname, strerror(errno));
1461 dbprintf("%s\n", errmsg);
1472 check_no_check_device(void)
1474 if (gnutar_checkdevice == 0) {
1475 GPtrArray *argv_ptr = g_ptr_array_new();
1482 g_ptr_array_add(argv_ptr, gnutar_path);
1483 g_ptr_array_add(argv_ptr, "-x");
1484 g_ptr_array_add(argv_ptr, "--no-check-device");
1485 g_ptr_array_add(argv_ptr, "-f");
1486 g_ptr_array_add(argv_ptr, "-");
1487 g_ptr_array_add(argv_ptr, NULL);
1489 pipespawnv(gnutar_path, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
1490 &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
1493 size = read(outf, buf, 32767);
1496 if (strstr(buf, "--no-check-device")) {
1497 g_debug("disabling --no-check-device since '%s' doesn't support it", gnutar_path);
1498 gnutar_checkdevice = 1;
1502 g_ptr_array_free(argv_ptr, TRUE);
1506 GPtrArray *amgtar_build_argv(
1507 application_argument_t *argument,
1509 char **file_exclude,
1510 char **file_include,
1516 char tmppath[PATH_MAX];
1517 GPtrArray *argv_ptr = g_ptr_array_new();
1520 check_no_check_device();
1521 amgtar_build_exinclude(&argument->dle, 1,
1522 &nb_exclude, file_exclude,
1523 &nb_include, file_include);
1525 if (gnutar_directory) {
1526 dirname = gnutar_directory;
1528 dirname = argument->dle.device;
1531 g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1533 g_ptr_array_add(argv_ptr, stralloc("--create"));
1534 if (command == CMD_BACKUP && argument->dle.create_index)
1535 g_ptr_array_add(argv_ptr, stralloc("--verbose"));
1536 g_ptr_array_add(argv_ptr, stralloc("--file"));
1537 if (command == CMD_ESTIMATE) {
1538 g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
1540 g_ptr_array_add(argv_ptr, stralloc("-"));
1542 if (gnutar_no_unquote)
1543 g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1544 g_ptr_array_add(argv_ptr, stralloc("--directory"));
1545 canonicalize_pathname(dirname, tmppath);
1546 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1547 if (gnutar_onefilesystem)
1548 g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
1549 if (gnutar_atimepreserve)
1550 g_ptr_array_add(argv_ptr, stralloc("--atime-preserve=system"));
1551 if (!gnutar_checkdevice)
1552 g_ptr_array_add(argv_ptr, stralloc("--no-check-device"));
1554 g_ptr_array_add(argv_ptr, stralloc("--acls"));
1556 g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1558 g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1559 g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
1560 g_ptr_array_add(argv_ptr, stralloc(incrname));
1562 g_ptr_array_add(argv_ptr, stralloc("--sparse"));
1563 if (argument->tar_blocksize) {
1564 g_ptr_array_add(argv_ptr, stralloc("--blocking-factor"));
1565 g_ptr_array_add(argv_ptr, stralloc(argument->tar_blocksize));
1567 g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
1568 g_ptr_array_add(argv_ptr, stralloc("--totals"));
1570 for (copt = argument->command_options; copt != NULL; copt = copt->next) {
1571 g_ptr_array_add(argv_ptr, stralloc((char *)copt->data));
1574 if (*file_exclude) {
1575 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1576 g_ptr_array_add(argv_ptr, stralloc(*file_exclude));
1579 if (*file_include) {
1580 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1581 g_ptr_array_add(argv_ptr, stralloc(*file_include));
1584 g_ptr_array_add(argv_ptr, stralloc("."));
1586 g_ptr_array_add(argv_ptr, NULL);