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: amgtar.c 8888 2007-10-02 13:40:42Z martineau $
29 * send estimated backup sizes using dump
34 * GNUTAR-PATH (default GNUTAR)
35 * GNUTAR-LISTDIR (default CNF_GNUTAR_LIST_DIR)
36 * DIRECTORY (no default, if set, the backup will be from that directory
37 * instead of from the --device)
38 * ONE-FILE-SYSTEM (default YES)
39 * SPARSE (default YES)
40 * ATIME-PRESERVE (default YES)
41 * CHECK-DEVICE (default YES)
42 * NO-UNQUOTE (default NO)
44 * SELINUX (default NO)
57 * EXIT-HANDLING (1=GOOD 2=BAD)
58 * TAR-BLOCKSIZE (default does not add --blocking-factor option,
59 * using tar's default)
64 #include "pipespawn.h"
65 #include "amfeatures.h"
69 #include "client_util.h"
73 int debug_application = 1;
74 #define application_debug(i, ...) do { \
75 if ((i) <= debug_application) { \
76 dbprintf(__VA_ARGS__); \
80 static amregex_t init_re_table[] = {
81 /* tar prints the size in bytes */
82 AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1, 1),
83 AM_NORMAL_RE("^could not open conf file"),
84 AM_NORMAL_RE("^Elapsed time:"),
85 AM_NORMAL_RE("^Throughput"),
86 AM_IGNORE_RE(": Directory is new$"),
87 AM_IGNORE_RE(": Directory has been renamed"),
89 /* GNU tar 1.13.17 will print this warning when (not) backing up a
91 AM_NORMAL_RE(": socket ignored$"),
93 /* GNUTAR produces a few error messages when files are modified or
94 removed while it is running. They may cause data to be lost, but
95 then they may not. We shouldn't consider them NORMAL until
96 further investigation. */
97 AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
98 AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
99 AM_NORMAL_RE(": Error exit delayed from previous errors"),
101 /* catch-all: DMP_STRANGE is returned for all other lines */
104 static amregex_t *re_table;
106 /* local functions */
107 int main(int argc, char **argv);
109 typedef struct application_argument_s {
117 GSList *command_options;
118 char *include_list_glob;
119 char *exclude_list_glob;
123 } application_argument_t;
125 enum { CMD_ESTIMATE, CMD_BACKUP };
127 static void amgtar_support(application_argument_t *argument);
128 static void amgtar_selfcheck(application_argument_t *argument);
129 static void amgtar_estimate(application_argument_t *argument);
130 static void amgtar_backup(application_argument_t *argument);
131 static void amgtar_restore(application_argument_t *argument);
132 static void amgtar_validate(application_argument_t *argument);
133 static void amgtar_build_exinclude(dle_t *dle, int verbose,
134 int *nb_exclude, char **file_exclude,
135 int *nb_include, char **file_include);
136 static char *amgtar_get_incrname(application_argument_t *argument, int level,
137 FILE *mesgstream, int command);
138 static GPtrArray *amgtar_build_argv(application_argument_t *argument,
139 char *incrname, int command);
140 static char *gnutar_path;
141 static char *gnutar_listdir;
142 static char *gnutar_directory;
143 static int gnutar_onefilesystem;
144 static int gnutar_atimepreserve;
145 static int gnutar_acls;
146 static int gnutar_selinux;
147 static int gnutar_xattrs;
148 static int gnutar_checkdevice;
149 static int gnutar_no_unquote;
150 static int gnutar_sparse;
151 static GSList *normal_message = NULL;
152 static GSList *ignore_message = NULL;
153 static GSList *strange_message = NULL;
154 static char *exit_handling;
155 static int exit_value[256];
157 static struct option long_options[] = {
158 {"config" , 1, NULL, 1},
159 {"host" , 1, NULL, 2},
160 {"disk" , 1, NULL, 3},
161 {"device" , 1, NULL, 4},
162 {"level" , 1, NULL, 5},
163 {"index" , 1, NULL, 6},
164 {"message" , 1, NULL, 7},
165 {"collection" , 0, NULL, 8},
166 {"record" , 0, NULL, 9},
167 {"gnutar-path" , 1, NULL, 10},
168 {"gnutar-listdir" , 1, NULL, 11},
169 {"one-file-system" , 1, NULL, 12},
170 {"sparse" , 1, NULL, 13},
171 {"atime-preserve" , 1, NULL, 14},
172 {"check-device" , 1, NULL, 15},
173 {"include-file" , 1, NULL, 16},
174 {"include-list" , 1, NULL, 17},
175 {"include-optional", 1, NULL, 18},
176 {"exclude-file" , 1, NULL, 19},
177 {"exclude-list" , 1, NULL, 20},
178 {"exclude-optional", 1, NULL, 21},
179 {"directory" , 1, NULL, 22},
180 {"normal" , 1, NULL, 23},
181 {"ignore" , 1, NULL, 24},
182 {"strange" , 1, NULL, 25},
183 {"exit-handling" , 1, NULL, 26},
184 {"calcsize" , 0, NULL, 27},
185 {"tar-blocksize" , 1, NULL, 28},
186 {"no-unquote" , 1, NULL, 29},
187 {"acls" , 1, NULL, 30},
188 {"selinux" , 1, NULL, 31},
189 {"xattrs" , 1, NULL, 32},
190 {"command-options" , 1, NULL, 33},
191 {"include-list-glob", 1, NULL, 34},
192 {"exclude-list-glob", 1, NULL, 35},
201 char *result = malloc(4*strlen(str)+1);
206 for (s = str; *s != '\0'; s++) {
214 } else if (c == '?') {
218 } else if (c == 'a') {
222 } else if (c == 'b') {
226 } else if (c == 'f') {
230 } else if (c == 'n') {
235 } else if (c == 'r') {
240 } else if (c == 't') {
244 } else if (c == 'v') {
248 } else if (c >= '0' && c <= '9') {
252 if (c >= '0' && c <= '9') {
256 if (c >= '0' && c <= '9') {
266 } else if (*s == '?') {
269 } else if (*s == '*' || *s == '[') {
287 application_argument_t argument;
291 gnutar_path = GNUTAR;
295 gnutar_directory = NULL;
296 gnutar_onefilesystem = 1;
297 gnutar_atimepreserve = 1;
301 gnutar_checkdevice = 1;
303 gnutar_no_unquote = 0;
304 exit_handling = NULL;
309 * Configure program for internationalization:
310 * 1) Only set the message locale for now.
311 * 2) Set textdomain for all amanda related programs to "amanda"
312 * We don't want to be forced to support dozens of message catalogs.
314 setlocale(LC_MESSAGES, "C");
315 textdomain("amanda");
318 printf("ERROR no command given to amgtar\n");
319 error(_("No command given to amgtar"));
322 /* drop root privileges */
323 if (!set_root_privs(0)) {
324 if (strcmp(argv[1], "selfcheck") == 0) {
325 printf("ERROR amgtar must be run setuid root\n");
327 error(_("amgtar must be run setuid root"));
334 /* Don't die when child closes pipe */
335 signal(SIGPIPE, SIG_IGN);
337 #if defined(USE_DBMALLOC)
338 malloc_size_1 = malloc_inuse(&malloc_hist_1);
341 add_amanda_log_handler(amanda_log_stderr);
342 add_amanda_log_handler(amanda_log_syslog);
343 dbopen(DBG_SUBDIR_CLIENT);
345 dbprintf(_("version %s\n"), VERSION);
347 config_init(CONFIG_INIT_CLIENT, NULL);
349 //check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
351 //RUNNING_AS_CLIENT_LOGIN from selfcheck, sendsize, sendbackup
356 argument.config = NULL;
357 argument.host = NULL;
358 argument.message = 0;
359 argument.collection = 0;
360 argument.calcsize = 0;
361 argument.tar_blocksize = NULL;
362 argument.level = NULL;
363 argument.command_options = NULL;
364 argument.include_list_glob = NULL;
365 argument.exclude_list_glob = NULL;
366 init_dle(&argument.dle);
369 int option_index = 0;
370 c = getopt_long (argc, argv, "", long_options, &option_index);
375 case 1: argument.config = stralloc(optarg);
377 case 2: argument.host = stralloc(optarg);
379 case 3: argument.dle.disk = stralloc(optarg);
381 case 4: argument.dle.device = stralloc(optarg);
383 case 5: argument.level = g_slist_append(argument.level,
384 GINT_TO_POINTER(atoi(optarg)));
386 case 6: argument.dle.create_index = 1;
388 case 7: argument.message = 1;
390 case 8: argument.collection = 1;
392 case 9: argument.dle.record = 1;
394 case 10: gnutar_path = stralloc(optarg);
396 case 11: gnutar_listdir = stralloc(optarg);
398 case 12: if (optarg && strcasecmp(optarg, "NO") == 0)
399 gnutar_onefilesystem = 0;
400 else if (optarg && strcasecmp(optarg, "YES") == 0)
401 gnutar_onefilesystem = 1;
402 else if (strcasecmp(command, "selfcheck") == 0)
403 printf(_("ERROR [%s: bad ONE-FILE-SYSTEM property value (%s)]\n"), get_pname(), optarg);
405 case 13: if (optarg && strcasecmp(optarg, "NO") == 0)
407 else if (optarg && strcasecmp(optarg, "YES") == 0)
409 else if (strcasecmp(command, "selfcheck") == 0)
410 printf(_("ERROR [%s: bad SPARSE property value (%s)]\n"), get_pname(), optarg);
412 case 14: if (optarg && strcasecmp(optarg, "NO") == 0)
413 gnutar_atimepreserve = 0;
414 else if (optarg && strcasecmp(optarg, "YES") == 0)
415 gnutar_atimepreserve = 1;
416 else if (strcasecmp(command, "selfcheck") == 0)
417 printf(_("ERROR [%s: bad ATIME-PRESERVE property value (%s)]\n"), get_pname(), optarg);
419 case 15: if (optarg && strcasecmp(optarg, "NO") == 0)
420 gnutar_checkdevice = 0;
421 else if (optarg && strcasecmp(optarg, "YES") == 0)
422 gnutar_checkdevice = 1;
423 else if (strcasecmp(command, "selfcheck") == 0)
424 printf(_("ERROR [%s: bad CHECK-DEVICE property value (%s)]\n"), get_pname(), optarg);
427 argument.dle.include_file =
428 append_sl(argument.dle.include_file, optarg);
431 argument.dle.include_list =
432 append_sl(argument.dle.include_list, optarg);
434 case 18: argument.dle.include_optional = 1;
437 argument.dle.exclude_file =
438 append_sl(argument.dle.exclude_file, optarg);
441 argument.dle.exclude_list =
442 append_sl(argument.dle.exclude_list, optarg);
444 case 21: argument.dle.exclude_optional = 1;
446 case 22: gnutar_directory = stralloc(optarg);
450 g_slist_append(normal_message, optarg);
454 g_slist_append(ignore_message, optarg);
458 g_slist_append(strange_message, optarg);
461 exit_handling = stralloc(optarg);
463 case 27: argument.calcsize = 1;
465 case 28: argument.tar_blocksize = stralloc(optarg);
467 case 29: if (optarg && strcasecmp(optarg, "NO") == 0)
468 gnutar_no_unquote = 0;
469 else if (optarg && strcasecmp(optarg, "YES") == 0)
470 gnutar_no_unquote = 1;
471 else if (strcasecmp(command, "selfcheck") == 0)
472 printf(_("ERROR [%s: bad No_UNQUOTE property value (%s)]\n"), get_pname(), optarg);
474 case 30: if (optarg && strcasecmp(optarg, "YES") == 0)
477 case 31: if (optarg && strcasecmp(optarg, "YES") == 0)
480 case 32: if (optarg && strcasecmp(optarg, "YES") == 0)
483 case 33: argument.command_options =
484 g_slist_append(argument.command_options,
488 argument.include_list_glob = stralloc(optarg);
491 argument.exclude_list_glob = stralloc(optarg);
499 if (!argument.dle.disk && argument.dle.device)
500 argument.dle.disk = stralloc(argument.dle.device);
501 if (!argument.dle.device && argument.dle.disk)
502 argument.dle.device = stralloc(argument.dle.disk);
504 argument.argc = argc - optind;
505 argument.argv = argv + optind;
507 if (argument.config) {
508 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
510 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
513 if (config_errors(NULL) >= CFGERR_ERRORS) {
514 g_critical(_("errors processing config file"));
517 re_table = build_re_table(init_re_table, normal_message, ignore_message,
521 exit_value[i] = 1; /* BAD */
522 exit_value[0] = 0; /* GOOD */
523 exit_value[1] = 0; /* GOOD */
525 char *s = exit_handling;
527 char *r = strchr(s, '=');
530 if (j >= 0 && j < 256) {
532 if (strncasecmp(r, "GOOD", 4) == 0) {
537 s = strchr(s+1, ' ');
541 gnutar_listdir = getconf_str(CNF_GNUTAR_LIST_DIR);
542 if (strlen(gnutar_listdir) == 0)
543 gnutar_listdir = NULL;
546 dbprintf("GNUTAR-PATH %s\n", gnutar_path);
548 dbprintf("GNUTAR-PATH is not set\n");
550 if (gnutar_listdir) {
551 dbprintf("GNUTAR-LISTDIR %s\n", gnutar_listdir);
553 dbprintf("GNUTAR-LISTDIR is not set\n");
555 if (gnutar_directory) {
556 dbprintf("DIRECTORY %s\n", gnutar_directory);
558 dbprintf("ONE-FILE-SYSTEM %s\n", gnutar_onefilesystem? "yes":"no");
559 dbprintf("SPARSE %s\n", gnutar_sparse? "yes":"no");
560 dbprintf("NO-UNQUOTE %s\n", gnutar_no_unquote? "yes":"no");
561 dbprintf("ATIME-PRESERVE %s\n", gnutar_atimepreserve? "yes":"no");
562 dbprintf("ACLS %s\n", gnutar_acls? "yes":"no");
563 dbprintf("SELINUX %s\n", gnutar_selinux? "yes":"no");
564 dbprintf("XATTRS %s\n", gnutar_xattrs? "yes":"no");
565 dbprintf("CHECK-DEVICE %s\n", gnutar_checkdevice? "yes":"no");
568 for (rp = re_table; rp->regex != NULL; rp++) {
570 case DMP_NORMAL : dbprintf("NORMAL %s\n", rp->regex); break;
571 case DMP_IGNORE : dbprintf("IGNORE %s\n", rp->regex); break;
572 case DMP_STRANGE: dbprintf("STRANGE %s\n", rp->regex); break;
573 case DMP_SIZE : dbprintf("SIZE %s\n", rp->regex); break;
574 case DMP_ERROR : dbprintf("ERROR %s\n", rp->regex); break;
579 if (strcmp(command, "support") == 0) {
580 amgtar_support(&argument);
581 } else if (strcmp(command, "selfcheck") == 0) {
582 amgtar_selfcheck(&argument);
583 } else if (strcmp(command, "estimate") == 0) {
584 amgtar_estimate(&argument);
585 } else if (strcmp(command, "backup") == 0) {
586 amgtar_backup(&argument);
587 } else if (strcmp(command, "restore") == 0) {
588 amgtar_restore(&argument);
589 } else if (strcmp(command, "validate") == 0) {
590 amgtar_validate(&argument);
592 dbprintf("Unknown command `%s'.\n", command);
593 fprintf(stderr, "Unknown command `%s'.\n", command);
601 application_argument_t *argument)
604 fprintf(stdout, "CONFIG YES\n");
605 fprintf(stdout, "HOST YES\n");
606 fprintf(stdout, "DISK YES\n");
607 fprintf(stdout, "MAX-LEVEL 399\n");
608 fprintf(stdout, "INDEX-LINE YES\n");
609 fprintf(stdout, "INDEX-XML NO\n");
610 fprintf(stdout, "MESSAGE-LINE YES\n");
611 fprintf(stdout, "MESSAGE-XML NO\n");
612 fprintf(stdout, "RECORD YES\n");
613 fprintf(stdout, "INCLUDE-FILE YES\n");
614 fprintf(stdout, "INCLUDE-LIST YES\n");
615 fprintf(stdout, "INCLUDE-LIST-GLOB YES\n");
616 fprintf(stdout, "INCLUDE-OPTIONAL YES\n");
617 fprintf(stdout, "EXCLUDE-FILE YES\n");
618 fprintf(stdout, "EXCLUDE-LIST YES\n");
619 fprintf(stdout, "EXCLUDE-LIST-GLOB YES\n");
620 fprintf(stdout, "EXCLUDE-OPTIONAL YES\n");
621 fprintf(stdout, "COLLECTION NO\n");
622 fprintf(stdout, "MULTI-ESTIMATE YES\n");
623 fprintf(stdout, "CALCSIZE YES\n");
624 fprintf(stdout, "CLIENT-ESTIMATE YES\n");
629 application_argument_t *argument)
631 amgtar_build_exinclude(&argument->dle, 1, NULL, NULL, NULL, NULL);
633 printf("OK amgtar\n");
635 check_file(gnutar_path, X_OK);
637 printf(_("ERROR [GNUTAR program not available]\n"));
641 if (gnutar_listdir && strlen(gnutar_listdir) == 0)
642 gnutar_listdir = NULL;
643 if (gnutar_listdir) {
644 check_dir(gnutar_listdir, R_OK|W_OK);
646 printf(_("ERROR [No GNUTAR-LISTDIR]\n"));
649 if (argument->dle.disk) {
650 char *qdisk = quote_string(argument->dle.disk);
651 fprintf(stdout, "OK %s\n", qdisk);
654 if (gnutar_directory) {
655 check_dir(gnutar_directory, R_OK);
656 } else if (argument->dle.device) {
657 check_dir(argument->dle.device, R_OK);
659 if (argument->calcsize) {
660 char *calcsize = vstralloc(amlibexecdir, "/", "calcsize", NULL);
661 check_file(calcsize, X_OK);
662 check_suid(calcsize);
670 application_argument_t *argument)
672 char *incrname = NULL;
677 FILE *dumpout = NULL;
681 char *qerrmsg = NULL;
683 amwait_t wait_status;
690 if (!argument->level) {
691 fprintf(stderr, "ERROR No level argument\n");
692 error(_("No level argument"));
694 if (!argument->dle.disk) {
695 fprintf(stderr, "ERROR No disk argument\n");
696 error(_("No disk argument"));
698 if (!argument->dle.device) {
699 fprintf(stderr, "ERROR No device argument\n");
700 error(_("No device argument"));
703 qdisk = quote_string(argument->dle.disk);
705 if (argument->calcsize) {
712 if (gnutar_directory) {
713 dirname = gnutar_directory;
715 dirname = amname_to_dirname(argument->dle.device);
717 amgtar_build_exinclude(&argument->dle, 1,
718 &nb_exclude, &file_exclude,
719 &nb_include, &file_include);
721 run_calcsize(argument->config, "GNUTAR", argument->dle.disk, dirname,
722 argument->level, file_exclude, file_include);
727 errmsg = vstrallocf(_("GNUTAR-PATH not defined"));
731 if (!gnutar_listdir) {
732 errmsg = vstrallocf(_("GNUTAR-LISTDIR not defined"));
736 for (levels = argument->level; levels != NULL; levels = levels->next) {
737 level = GPOINTER_TO_INT(levels->data);
738 incrname = amgtar_get_incrname(argument, level, stdout, CMD_ESTIMATE);
739 cmd = stralloc(gnutar_path);
740 argv_ptr = amgtar_build_argv(argument, incrname, CMD_ESTIMATE);
742 start_time = curclock();
744 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
745 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
750 tarpid = pipespawnv(cmd, STDERR_PIPE, 1,
751 &nullfd, &nullfd, &pipefd,
752 (char **)argv_ptr->pdata);
754 dumpout = fdopen(pipefd,"r");
756 error(_("Can't fdopen: %s"), strerror(errno));
761 while (size < 0 && (fgets(line, sizeof(line), dumpout) != NULL)) {
762 if (line[strlen(line)-1] == '\n') /* remove trailling \n */
763 line[strlen(line)-1] = '\0';
766 dbprintf("%s\n", line);
767 /* check for size match */
769 for(rp = re_table; rp->regex != NULL; rp++) {
770 if(match(rp->regex, line)) {
771 if (rp->typ == DMP_SIZE) {
772 size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
774 size = 1.0; /* found on NeXT -- sigh */
782 while (fgets(line, sizeof(line), dumpout) != NULL) {
783 dbprintf("%s", line);
787 dbprintf(_("estimate time for %s level %d: %s\n"),
790 walltime_str(timessub(curclock(), start_time)));
791 if(size == (off_t)-1) {
792 errmsg = vstrallocf(_("no size line match in %s output"), cmd);
793 dbprintf(_("%s for %s\n"), errmsg, qdisk);
795 } else if(size == (off_t)0 && argument->level == 0) {
796 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
797 cmd, argument->dle.disk);
800 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
805 kill(-tarpid, SIGTERM);
807 dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
808 waitpid(tarpid, &wait_status, 0);
809 if (WIFSIGNALED(wait_status)) {
810 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
811 cmd, WTERMSIG(wait_status), dbfn());
812 } else if (WIFEXITED(wait_status)) {
813 if (exit_value[WEXITSTATUS(wait_status)] == 1) {
814 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
815 cmd, WEXITSTATUS(wait_status), dbfn());
820 errmsg = vstrallocf(_("%s got bad exit: see %s"),
823 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
827 dbprintf("%s", errmsg);
828 fprintf(stdout, "ERROR %s\n", errmsg);
834 g_ptr_array_free_full(argv_ptr);
840 fprintf(stdout, "%d %lld 1\n", level, (long long)size);
846 qerrmsg = quote_string(errmsg);
848 dbprintf("%s", errmsg);
849 fprintf(stdout, "ERROR %s\n", qerrmsg);
857 application_argument_t *argument)
865 off_t dump_size = -1;
873 FILE *indexstream = NULL;
876 amwait_t wait_status;
880 mesgstream = fdopen(mesgf, "w");
882 error(_("error mesgstream(%d): %s\n"), mesgf, strerror(errno));
886 error(_("GNUTAR-PATH not defined"));
888 if (!gnutar_listdir) {
889 error(_("GNUTAR-LISTDIR not defined"));
892 if (!argument->level) {
893 fprintf(mesgstream, "? No level argument\n");
894 error(_("No level argument"));
896 if (!argument->dle.disk) {
897 fprintf(mesgstream, "? No disk argument\n");
898 error(_("No disk argument"));
900 if (!argument->dle.device) {
901 fprintf(mesgstream, "? No device argument\n");
902 error(_("No device argument"));
905 qdisk = quote_string(argument->dle.disk);
907 incrname = amgtar_get_incrname(argument,
908 GPOINTER_TO_INT(argument->level->data),
909 mesgstream, CMD_BACKUP);
910 cmd = stralloc(gnutar_path);
911 argv_ptr = amgtar_build_argv(argument, incrname, CMD_BACKUP);
913 tarpid = pipespawnv(cmd, STDIN_PIPE|STDERR_PIPE, 1,
914 &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
915 /* close the write ends of the pipes */
919 if (argument->dle.create_index) {
920 indexstream = fdopen(indexf, "w");
922 error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
925 outstream = fdopen(outf, "r");
927 error(_("error outstream(%d): %s\n"), outf, strerror(errno));
930 while (fgets(line, sizeof(line), outstream) != NULL) {
931 if (line[strlen(line)-1] == '\n') /* remove trailling \n */
932 line[strlen(line)-1] = '\0';
933 if (*line == '.' && *(line+1) == '/') { /* filename */
934 if (argument->dle.create_index) {
935 fprintf(indexstream, "%s\n", &line[1]); /* remove . */
937 } else { /* message */
938 for(rp = re_table; rp->regex != NULL; rp++) {
939 if(match(rp->regex, line)) {
943 if(rp->typ == DMP_SIZE) {
944 dump_size = (long)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
970 dbprintf("%3d: %7s(%c): %s\n", rp->srcline, type, startchr, line);
971 fprintf(mesgstream,"%c %s\n", startchr, line);
975 waitpid(tarpid, &wait_status, 0);
976 if (WIFSIGNALED(wait_status)) {
977 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
978 cmd, WTERMSIG(wait_status), dbfn());
979 } else if (WIFEXITED(wait_status)) {
980 if (exit_value[WEXITSTATUS(wait_status)] == 1) {
981 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
982 cmd, WEXITSTATUS(wait_status), dbfn());
987 errmsg = vstrallocf(_("%s got bad exit: see %s"),
990 dbprintf(_("after %s %s wait\n"), cmd, qdisk);
991 dbprintf(_("amgtar: %s: pid %ld\n"), cmd, (long)tarpid);
993 dbprintf("%s", errmsg);
994 g_fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
997 if (!errmsg && incrname && strlen(incrname) > 4) {
999 nodotnew = stralloc(incrname);
1000 nodotnew[strlen(nodotnew)-4] = '\0';
1001 if (rename(incrname, nodotnew)) {
1002 dbprintf(_("%s: warning [renaming %s to %s: %s]\n"),
1003 get_pname(), incrname, nodotnew, strerror(errno));
1004 g_fprintf(mesgstream, _("? warning [renaming %s to %s: %s]\n"),
1005 incrname, nodotnew, strerror(errno));
1010 dbprintf("sendbackup: size %lld\n", (long long)dump_size);
1011 fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
1012 dbprintf("sendbackup: end\n");
1013 fprintf(mesgstream, "sendbackup: end\n");
1015 if (argument->dle.create_index)
1016 fclose(indexstream);
1023 g_ptr_array_free_full(argv_ptr);
1028 application_argument_t *argument)
1031 GPtrArray *argv_ptr = g_ptr_array_new();
1037 error(_("GNUTAR-PATH not defined"));
1040 cmd = stralloc(gnutar_path);
1041 g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1042 g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1043 if (gnutar_no_unquote)
1044 g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1046 g_ptr_array_add(argv_ptr, stralloc("--acls"));
1048 g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1050 g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1051 g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1052 g_ptr_array_add(argv_ptr, stralloc("-"));
1053 if (gnutar_directory) {
1054 struct stat stat_buf;
1055 if(stat(gnutar_directory, &stat_buf) != 0) {
1056 fprintf(stderr,"can not stat directory %s: %s\n", gnutar_directory, strerror(errno));
1059 if (!S_ISDIR(stat_buf.st_mode)) {
1060 fprintf(stderr,"%s is not a directory\n", gnutar_directory);
1063 if (access(gnutar_directory, W_OK) != 0) {
1064 fprintf(stderr, "Can't write to %s: %s\n", gnutar_directory, strerror(errno));
1067 g_ptr_array_add(argv_ptr, stralloc("--directory"));
1068 g_ptr_array_add(argv_ptr, stralloc(gnutar_directory));
1071 g_ptr_array_add(argv_ptr, stralloc("--wildcards"));
1072 if (argument->dle.exclude_list &&
1073 argument->dle.exclude_list->nb_element == 1) {
1078 int entry_in_exclude = 0;
1079 char line[2*PATH_MAX];
1082 if (argument->dle.disk) {
1083 sdisk = sanitise_filename(argument->dle.disk);
1085 sdisk = g_strdup_printf("installcheck-exclude-%d", getpid());
1087 filename = vstralloc(AMANDA_TMPDIR, "/", "exclude-", sdisk, NULL);
1088 exclude_list = fopen(argument->dle.exclude_list->first->name, "r");
1090 exclude = fopen(filename, "w");
1091 while (fgets(line, 2*PATH_MAX, exclude_list)) {
1093 line[strlen(line)-1] = '\0'; /* remove '\n' */
1094 escaped = escape_tar_glob(line, &in_argv);
1096 g_ptr_array_add(argv_ptr, "--exclude");
1097 g_ptr_array_add(argv_ptr, escaped);
1099 fprintf(exclude,"%s\n", escaped);
1105 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1106 g_ptr_array_add(argv_ptr, filename);
1109 if (argument->exclude_list_glob) {
1110 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1111 g_ptr_array_add(argv_ptr, stralloc(argument->exclude_list_glob));
1115 GPtrArray *argv_include = g_ptr_array_new();
1121 int entry_in_include = 0;
1123 if (argument->dle.disk) {
1124 sdisk = sanitise_filename(argument->dle.disk);
1126 sdisk = g_strdup_printf("installcheck-include-%d", getpid());
1128 filename = vstralloc(AMANDA_TMPDIR, "/", "include-", sdisk, NULL);
1129 include = fopen(filename, "w");
1130 if (argument->dle.include_list &&
1131 argument->dle.include_list->nb_element == 1) {
1132 char line[2*PATH_MAX];
1133 FILE *include_list = fopen(argument->dle.include_list->first->name, "r");
1134 while (fgets(line, 2*PATH_MAX, include_list)) {
1136 line[strlen(line)-1] = '\0'; /* remove '\n' */
1137 escaped = escape_tar_glob(line, &in_argv);
1139 g_ptr_array_add(argv_include, escaped);
1141 fprintf(include,"%s\n", escaped);
1148 for (j=1; j< argument->argc; j++) {
1149 char *escaped = escape_tar_glob(argument->argv[j], &in_argv);
1151 g_ptr_array_add(argv_include, escaped);
1153 fprintf(include,"%s\n", escaped);
1160 if (entry_in_include) {
1161 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1162 g_ptr_array_add(argv_ptr, filename);
1165 if (argument->include_list_glob) {
1166 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1167 g_ptr_array_add(argv_ptr, stralloc(argument->include_list_glob));
1170 for (i = 0; i < argv_include->len; i++) {
1171 g_ptr_array_add(argv_ptr, (char *)g_ptr_array_index(argv_include,i));
1174 g_ptr_array_add(argv_ptr, NULL);
1176 debug_executing(argv_ptr);
1179 execve(cmd, (char **)argv_ptr->pdata, env);
1180 e = strerror(errno);
1181 error(_("error [exec %s: %s]"), cmd, e);
1186 application_argument_t *argument G_GNUC_UNUSED)
1189 GPtrArray *argv_ptr = g_ptr_array_new();
1195 dbprintf("GNUTAR-PATH not set; Piping to /dev/null\n");
1196 fprintf(stderr,"GNUTAR-PATH not set; Piping to /dev/null\n");
1200 cmd = stralloc(gnutar_path);
1201 g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1202 g_ptr_array_add(argv_ptr, stralloc("-tf"));
1203 g_ptr_array_add(argv_ptr, stralloc("-"));
1204 g_ptr_array_add(argv_ptr, NULL);
1206 debug_executing(argv_ptr);
1208 execve(cmd, (char **)argv_ptr->pdata, env);
1209 e = strerror(errno);
1210 dbprintf("failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1211 fprintf(stderr,"failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1213 while (read(0, buf, 32768) > 0) {
1218 amgtar_build_exinclude(
1222 char **file_exclude,
1224 char **file_include)
1228 char *exclude = NULL;
1229 char *include = NULL;
1231 if (dle->exclude_file) n_exclude += dle->exclude_file->nb_element;
1232 if (dle->exclude_list) n_exclude += dle->exclude_list->nb_element;
1233 if (dle->include_file) n_include += dle->include_file->nb_element;
1234 if (dle->include_list) n_include += dle->include_list->nb_element;
1236 if (n_exclude > 0) exclude = build_exclude(dle, verbose);
1237 if (n_include > 0) include = build_include(dle, verbose);
1240 *nb_exclude = n_exclude;
1242 *file_exclude = exclude;
1247 *nb_include = n_include;
1249 *file_include = include;
1255 amgtar_get_incrname(
1256 application_argument_t *argument,
1261 char *basename = NULL;
1262 char *incrname = NULL;
1265 char *inputname = NULL;
1266 char *errmsg = NULL;
1269 if (gnutar_listdir) {
1270 char number[NUM_STR_SIZE];
1272 char *sdisk = sanitise_filename(argument->dle.disk);
1274 basename = vstralloc(gnutar_listdir,
1281 snprintf(number, SIZEOF(number), "%d", level);
1282 incrname = vstralloc(basename, "_", number, ".new", NULL);
1286 * Open the listed incremental file from the previous level. Search
1287 * backward until one is found. If none are found (which will also
1288 * be true for a level 0), arrange to read from /dev/null.
1292 while (infd == -1) {
1293 if (--baselevel >= 0) {
1294 snprintf(number, SIZEOF(number), "%d", baselevel);
1295 inputname = newvstralloc(inputname,
1296 basename, "_", number, NULL);
1298 inputname = newstralloc(inputname, "/dev/null");
1300 if ((infd = open(inputname, O_RDONLY)) == -1) {
1302 errmsg = vstrallocf(_("amgtar: error opening %s: %s"),
1303 inputname, strerror(errno));
1304 dbprintf("%s\n", errmsg);
1305 if (baselevel < 0) {
1306 if (command == CMD_ESTIMATE) {
1307 fprintf(mesgstream, "ERROR %s\n", errmsg);
1309 fprintf(mesgstream, "? %s\n", errmsg);
1318 * Copy the previous listed incremental file to the new one.
1320 if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1321 errmsg = vstrallocf(_("error opening %s: %s"),
1322 incrname, strerror(errno));
1323 dbprintf("%s\n", errmsg);
1324 if (command == CMD_ESTIMATE) {
1325 fprintf(mesgstream, "ERROR %s\n", errmsg);
1327 fprintf(mesgstream, "? %s\n", errmsg);
1332 while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1333 if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
1334 errmsg = vstrallocf(_("writing to %s: %s"),
1335 incrname, strerror(errno));
1336 dbprintf("%s\n", errmsg);
1342 errmsg = vstrallocf(_("reading from %s: %s"),
1343 inputname, strerror(errno));
1344 dbprintf("%s\n", errmsg);
1348 if (close(infd) != 0) {
1349 errmsg = vstrallocf(_("closing %s: %s"),
1350 inputname, strerror(errno));
1351 dbprintf("%s\n", errmsg);
1354 if (close(outfd) != 0) {
1355 errmsg = vstrallocf(_("closing %s: %s"),
1356 incrname, strerror(errno));
1357 dbprintf("%s\n", errmsg);
1367 GPtrArray *amgtar_build_argv(
1368 application_argument_t *argument,
1377 char tmppath[PATH_MAX];
1378 GPtrArray *argv_ptr = g_ptr_array_new();
1381 amgtar_build_exinclude(&argument->dle, 1,
1382 &nb_exclude, &file_exclude,
1383 &nb_include, &file_include);
1385 if (gnutar_directory) {
1386 dirname = gnutar_directory;
1388 dirname = amname_to_dirname(argument->dle.device);
1391 g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1393 g_ptr_array_add(argv_ptr, stralloc("--create"));
1394 if (command == CMD_BACKUP && argument->dle.create_index)
1395 g_ptr_array_add(argv_ptr, stralloc("--verbose"));
1396 g_ptr_array_add(argv_ptr, stralloc("--file"));
1397 if (command == CMD_ESTIMATE) {
1398 g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
1400 g_ptr_array_add(argv_ptr, stralloc("-"));
1402 if (gnutar_no_unquote)
1403 g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1404 g_ptr_array_add(argv_ptr, stralloc("--directory"));
1405 canonicalize_pathname(dirname, tmppath);
1406 g_ptr_array_add(argv_ptr, stralloc(tmppath));
1407 if (gnutar_onefilesystem)
1408 g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
1409 if (gnutar_atimepreserve)
1410 g_ptr_array_add(argv_ptr, stralloc("--atime-preserve=system"));
1411 if (!gnutar_checkdevice)
1412 g_ptr_array_add(argv_ptr, stralloc("--no-check-device"));
1414 g_ptr_array_add(argv_ptr, stralloc("--acls"));
1416 g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1418 g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1419 g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
1420 g_ptr_array_add(argv_ptr, stralloc(incrname));
1422 g_ptr_array_add(argv_ptr, stralloc("--sparse"));
1423 if (argument->tar_blocksize) {
1424 g_ptr_array_add(argv_ptr, stralloc("--blocking-factor"));
1425 g_ptr_array_add(argv_ptr, stralloc(argument->tar_blocksize));
1427 g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
1428 g_ptr_array_add(argv_ptr, stralloc("--totals"));
1430 for (copt = argument->command_options; copt != NULL; copt = copt->next) {
1431 g_ptr_array_add(argv_ptr, stralloc((char *)copt->data));
1435 g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1436 g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1440 g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1441 g_ptr_array_add(argv_ptr, stralloc(file_include));
1444 g_ptr_array_add(argv_ptr, stralloc("."));
1446 g_ptr_array_add(argv_ptr, NULL);