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: amstar.c 8888 2007-10-02 13:40:42Z martineau $
29 * send estimated backup sizes using dump
34 * STAR-PATH (default STAR)
42 #include "pipespawn.h"
43 #include "amfeatures.h"
44 #include "amandates.h"
49 #include "client_util.h"
53 #include "sendbackup.h"
55 int debug_application = 1;
56 #define application_debug(i, ...) do { \
57 if ((i) <= debug_application) { \
58 dbprintf(__VA_ARGS__); \
62 static amregex_t re_table[] = {
63 /* tar prints the size in bytes */
64 AM_SIZE_RE("star: [0-9][0-9]* blocks", 10240, 1),
65 AM_NORMAL_RE("^could not open conf file"),
66 AM_NORMAL_RE("^Type of this level "),
67 AM_NORMAL_RE("^Date of this level "),
68 AM_NORMAL_RE("^Date of last level "),
69 AM_NORMAL_RE("^Dump record level "),
70 AM_NORMAL_RE("^Throughput"),
71 AM_NORMAL_RE("^.*is sparse$"),
73 #ifdef IGNORE_TAR_ERRORS
74 AM_NORMAL_RE("^.*shrunk*$"),
75 AM_NORMAL_RE("^.*changed size.*$"),
76 AM_NORMAL_RE("^.*Cannot listxattr for.*$"),
77 AM_NORMAL_RE("^.Cannot: stat .*$"),
78 AM_NORMAL_RE("^.Missing links .*$"),
79 AM_NORMAL_RE("^.Cannot get xattr.*$"),
80 AM_NORMAL_RE("^.Cannot.*acl.*$"),
83 AM_NORMAL_RE("^star: dumped [0-9][0-9]* (tar )?files"),
84 AM_NORMAL_RE("^.*The following problems occurred during .* processing.*$"),
85 AM_NORMAL_RE("^.*Processed all possible files, despite earlier errors.*$"),
86 AM_NORMAL_RE("^.*not written due to problems during backup.*$"),
88 AM_STRANGE_RE("^Perform a level 0 dump first.*$"),
90 /* catch-all: DMP_STRANGE is returned for all other lines */
95 int main(int argc, char **argv);
97 typedef struct application_argument_s {
107 } application_argument_t;
109 enum { CMD_ESTIMATE, CMD_BACKUP };
111 static void amstar_support(application_argument_t *argument);
112 static void amstar_selfcheck(application_argument_t *argument);
113 static void amstar_estimate(application_argument_t *argument);
114 static void amstar_backup(application_argument_t *argument);
115 static void amstar_restore(application_argument_t *argument);
116 static void amstar_validate(application_argument_t *argument);
117 static char **amstar_build_argv(application_argument_t *argument,
122 int star_dle_tardumps;
123 int star_onefilesystem;
126 static struct option long_options[] = {
127 {"config" , 1, NULL, 1},
128 {"host" , 1, NULL, 2},
129 {"disk" , 1, NULL, 3},
130 {"device" , 1, NULL, 4},
131 {"level" , 1, NULL, 5},
132 {"index" , 1, NULL, 6},
133 {"message" , 1, NULL, 7},
134 {"collection" , 0, NULL, 8},
135 {"record" , 0, NULL, 9},
136 {"star-path" , 1, NULL, 10},
137 {"star-tardump" , 1, NULL, 11},
138 {"star-dle-tardump", 1, NULL, 12},
139 {"one-file-system" , 1, NULL, 13},
140 {"sparse" , 1, NULL, 14},
141 {"calcsize" , 0, NULL, 15},
153 application_argument_t argument;
160 star_tardumps = "/etc/tardumps";
161 star_dle_tardumps = 0;
162 star_onefilesystem = 1;
168 * Configure program for internationalization:
169 * 1) Only set the message locale for now.
170 * 2) Set textdomain for all amanda related programs to "amanda"
171 * We don't want to be forced to support dozens of message catalogs.
173 setlocale(LC_MESSAGES, "C");
174 textdomain("amanda");
176 /* drop root privileges */
178 if (!set_root_privs(0)) {
179 error(_("amstar must be run setuid root"));
186 /* Don't die when child closes pipe */
187 signal(SIGPIPE, SIG_IGN);
189 #if defined(USE_DBMALLOC)
190 malloc_size_1 = malloc_inuse(&malloc_hist_1);
193 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
194 dbopen(DBG_SUBDIR_CLIENT);
196 dbprintf(_("version %s\n"), version());
198 config_init(CONFIG_INIT_CLIENT, NULL);
200 //check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
202 //RUNNING_AS_CLIENT_LOGIN from selfcheck, sendsize, sendbackup
207 argument.config = NULL;
208 argument.host = NULL;
209 argument.message = 0;
210 argument.collection = 0;
211 argument.calcsize = 0;
212 argument.level = NULL;
213 init_dle(&argument.dle);
217 int option_index = 0;
218 c = getopt_long (argc, argv, "", long_options, &option_index);
223 case 1: argument.config = stralloc(optarg);
225 case 2: argument.host = stralloc(optarg);
227 case 3: argument.dle.disk = stralloc(optarg);
229 case 4: argument.dle.device = stralloc(optarg);
231 case 5: argument.level = g_slist_append(argument.level,
232 GINT_TO_POINTER(atoi(optarg)));
234 case 6: argument.dle.create_index = 1;
236 case 7: argument.message = 1;
238 case 8: argument.collection = 1;
240 case 9: argument.dle.record = 1;
242 case 10: star_path = stralloc(optarg);
244 case 11: star_tardumps = stralloc(optarg);
246 case 12: if (optarg && strcasecmp(optarg, "YES") == 0)
247 star_dle_tardumps = 1;
249 case 13: if (optarg && strcasecmp(optarg, "YES") != 0)
250 star_onefilesystem = 0;
252 case 14: if (optarg && strcasecmp(optarg, "YES") != 0)
255 case 15: argument.calcsize = 1;
263 argument.argc = argc - optind;
264 argument.argv = argv + optind;
266 if (argument.config) {
267 /* overlay this configuration on the existing (nameless) configuration */
268 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
270 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
274 if (config_errors(NULL) >= CFGERR_ERRORS) {
275 g_critical(_("errors processing config file"));
278 if (strcmp(command, "support") == 0) {
279 amstar_support(&argument);
280 } else if (strcmp(command, "selfcheck") == 0) {
281 amstar_selfcheck(&argument);
282 } else if (strcmp(command, "estimate") == 0) {
283 amstar_estimate(&argument);
284 } else if (strcmp(command, "backup") == 0) {
285 amstar_backup(&argument);
286 } else if (strcmp(command, "restore") == 0) {
287 amstar_restore(&argument);
288 } else if (strcmp(command, "validate") == 0) {
289 amstar_validate(&argument);
291 fprintf(stderr, "Unknown command `%s'.\n", command);
299 application_argument_t *argument)
302 fprintf(stdout, "CONFIG YES\n");
303 fprintf(stdout, "HOST YES\n");
304 fprintf(stdout, "DISK YES\n");
305 fprintf(stdout, "MAX-LEVEL 9\n");
306 fprintf(stdout, "INDEX-LINE YES\n");
307 fprintf(stdout, "INDEX-XML NO\n");
308 fprintf(stdout, "MESSAGE-LINE YES\n");
309 fprintf(stdout, "MESSAGE-XML NO\n");
310 fprintf(stdout, "RECORD YES\n");
311 fprintf(stdout, "INCLUDE-FILE NO\n");
312 fprintf(stdout, "INCLUDE-LIST NO\n");
313 fprintf(stdout, "EXCLUDE-FILE YES\n");
314 fprintf(stdout, "EXCLUDE-LIST YES\n");
315 fprintf(stdout, "COLLECTION NO\n");
316 fprintf(stdout, "MULTI-ESTIMATE YES\n");
317 fprintf(stdout, "CALCSIZE YES\n");
322 application_argument_t *argument)
327 qdisk = quote_string(argument->dle.disk);
328 qdevice = quote_string(argument->dle.device);
329 fprintf(stdout, "OK %s\n", qdisk);
330 fprintf(stdout, "OK %s\n", qdevice);
333 fprintf(stdout, "ERROR STAR-PATH not defined\n");
335 check_file(star_path, X_OK);
339 char *amandates_file;
340 amandates_file = getconf_str(CNF_AMANDATES);
341 check_file(amandates_file, R_OK|W_OK);
348 application_argument_t *argument)
350 char **my_argv = NULL;
354 FILE *dumpout = NULL;
360 amwait_t wait_status;
365 GSList *levels = NULL;
367 qdisk = quote_string(argument->dle.disk);
368 if (argument->calcsize) {
371 dirname = amname_to_dirname(argument->dle.device);
372 run_calcsize(argument->config, "STAR", argument->dle.disk, dirname,
373 argument->level, NULL, NULL);
378 errmsg = vstrallocf(_("STAR-PATH not defined"));
381 cmd = stralloc(star_path);
383 start_time = curclock();
385 for (levels = argument->level; levels != NULL; levels = levels->next) {
386 level = GPOINTER_TO_INT(levels->data);
387 my_argv = amstar_build_argv(argument, level, CMD_ESTIMATE);
389 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
390 errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
395 starpid = pipespawnv(cmd, STDERR_PIPE, 1,
396 &nullfd, &nullfd, &pipefd, my_argv);
398 dumpout = fdopen(pipefd,"r");
400 errmsg = vstrallocf(_("Can't fdopen: %s"), strerror(errno));
405 while (size < 0 && (fgets(line, sizeof(line), dumpout)) != NULL) {
406 if (line[strlen(line)-1] == '\n') /* remove trailling \n */
407 line[strlen(line)-1] = '\0';
410 dbprintf("%s\n", line);
411 /* check for size match */
413 for(rp = re_table; rp->regex != NULL; rp++) {
414 if(match(rp->regex, line)) {
415 if (rp->typ == DMP_SIZE) {
416 size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
418 size = 1.0; /* found on NeXT -- sigh */
426 while ((fgets(line, sizeof(line), dumpout)) != NULL) {
427 dbprintf("%s", line);
431 dbprintf(_("estimate time for %s level %d: %s\n"),
434 walltime_str(timessub(curclock(), start_time)));
435 if(size == (off_t)-1) {
436 errmsg = vstrallocf(_("no size line match in %s output"),
438 dbprintf(_("%s for %s\n"), errmsg, qdisk);
440 } else if(size == (off_t)0 && argument->level == 0) {
441 dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
442 my_argv[0], argument->dle.disk);
445 dbprintf(_("estimate size for %s level %d: %lld KB\n"),
450 kill(-starpid, SIGTERM);
452 dbprintf(_("waiting for %s \"%s\" child\n"), my_argv[0], qdisk);
453 waitpid(starpid, &wait_status, 0);
454 if (WIFSIGNALED(wait_status)) {
455 errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
456 cmd, WTERMSIG(wait_status), dbfn());
457 } else if (WIFEXITED(wait_status)) {
458 if (WEXITSTATUS(wait_status) != 0) {
459 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
460 cmd, WEXITSTATUS(wait_status), dbfn());
465 errmsg = vstrallocf(_("%s got bad exit: see %s"), cmd, dbfn());
467 dbprintf(_("after %s %s wait\n"), my_argv[0], qdisk);
474 fprintf(stdout, "%d %lld 1\n", level, (long long)size);
481 dbprintf("%s\n", errmsg);
482 qerrmsg = quote_string(errmsg);
484 dbprintf("%s", errmsg);
485 fprintf(stdout, "ERROR %s\n", qerrmsg);
493 application_argument_t *argument)
500 off_t dump_size = -1;
510 FILE *indexstream = NULL;
512 int level = GPOINTER_TO_INT(argument->level->data);
514 qdisk = quote_string(argument->dle.disk);
516 my_argv = amstar_build_argv(argument, level, CMD_BACKUP);
518 cmd = stralloc(star_path);
520 starpid = pipespawnv(cmd, STDIN_PIPE|STDERR_PIPE, 1,
521 &dumpin, &dataf, &outf, my_argv);
523 /* close the write ends of the pipes */
526 if (argument->dle.create_index) {
527 indexstream = fdopen(indexf, "w");
529 error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
532 mesgstream = fdopen(mesgf, "w");
534 error(_("error mesgstream(%d): %s\n"), mesgf, strerror(errno));
536 outstream = fdopen(outf, "r");
538 error(_("error outstream(%d): %s\n"), outf, strerror(errno));
541 while ((fgets(line, sizeof(line), outstream)) != NULL) {
542 regmatch_t regmatch[3];
546 if (line[strlen(line)-1] == '\n') /* remove trailling \n */
547 line[strlen(line)-1] = '\0';
549 regcomp(®ex, "^a \\.\\/ directory$", REG_EXTENDED|REG_NEWLINE);
550 if (regexec(®ex, line, 1, regmatch, 0) == 0) {
552 if (argument->dle.create_index)
553 fprintf(indexstream, "%s\n", "/\n");
557 regcomp(®ex, "^a (.*) directory$", REG_EXTENDED|REG_NEWLINE);
558 if (regexec(®ex, line, 3, regmatch, 0) == 0) {
560 if (argument->dle.create_index && regmatch[1].rm_so == 2) {
561 line[regmatch[1].rm_eo+1]='\0';
562 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
567 regcomp(®ex, "^a (.*) (.*) bytes", REG_EXTENDED|REG_NEWLINE);
568 if (regexec(®ex, line, 3, regmatch, 0) == 0) {
570 if (argument->dle.create_index && regmatch[1].rm_so == 2) {
571 line[regmatch[1].rm_eo]='\0';
572 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
577 regcomp(®ex, "^a (.*) special", REG_EXTENDED|REG_NEWLINE);
578 if (regexec(®ex, line, 3, regmatch, 0) == 0) {
580 if (argument->dle.create_index && regmatch[1].rm_so == 2) {
581 line[regmatch[1].rm_eo]='\0';
582 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
587 regcomp(®ex, "^a (.*) symbolic", REG_EXTENDED|REG_NEWLINE);
588 if (regexec(®ex, line, 3, regmatch, 0) == 0) {
590 if (argument->dle.create_index && regmatch[1].rm_so == 2) {
591 line[regmatch[1].rm_eo]='\0';
592 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
597 if (got_match == 0) { /* message */
598 for(rp = re_table; rp->regex != NULL; rp++) {
599 if(match(rp->regex, line)) {
603 if(rp->typ == DMP_SIZE) {
604 dump_size = (long)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
628 dbprintf("%3d: %7s(%c): %s\n", rp->srcline, type, startchr, line);
629 fprintf(mesgstream,"%c %s\n", startchr, line);
633 dbprintf(_("gnutar: %s: pid %ld\n"), cmd, (long)starpid);
635 dbprintf("sendbackup: size %lld\n", (long long)dump_size);
636 fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
637 dbprintf("sendbackup: end\n");
638 fprintf(mesgstream, "sendbackup: end\n");
641 if (argument->dle.create_index)
650 application_argument_t *argument)
659 error(_("STAR-PATH not defined"));
662 cmd = stralloc(star_path);
663 my_argv = alloc(SIZEOF(char *) * (11 + argument->argc));
665 my_argv[i++] = stralloc(star_path);
666 my_argv[i++] = stralloc("-x");
667 my_argv[i++] = stralloc("-v");
668 my_argv[i++] = stralloc("-xattr");
669 my_argv[i++] = stralloc("-acl");
670 my_argv[i++] = stralloc("errctl=WARN|SAMEFILE|SETTIME|DIFF|SETACL|SETXATTR|SETMODE|BADACL *");
671 my_argv[i++] = stralloc("-no-fifo");
672 my_argv[i++] = stralloc("-f");
673 my_argv[i++] = stralloc("-");
675 for (j=1; j< argument->argc; j++)
676 my_argv[i++] = stralloc(argument->argv[j]+2); /* remove ./ */
681 execve(cmd, my_argv, env);
683 error(_("error [exec %s: %s]"), cmd, e);
689 application_argument_t *argument G_GNUC_UNUSED)
698 error(_("STAR-PATH not defined"));
701 cmd = stralloc(star_path);
702 my_argv = alloc(SIZEOF(char *) * 5);
704 my_argv[i++] = stralloc(star_path);
705 my_argv[i++] = stralloc("-t");
706 my_argv[i++] = stralloc("-f");
707 my_argv[i++] = stralloc("-");
711 execve(cmd, my_argv, env);
713 error(_("error [exec %s: %s]"), cmd, e);
717 char **amstar_build_argv(
718 application_argument_t *argument,
725 char levelstr[NUM_STR_SIZE+7];
730 dirname = amname_to_dirname(argument->dle.device);
731 fsname = vstralloc("fs-name=", dirname, NULL);
732 for (s = fsname; *s != '\0'; s++) {
733 if (iscntrl((int)*s))
736 snprintf(levelstr, SIZEOF(levelstr), "-level=%d", level);
738 if (star_dle_tardumps) {
739 char *sdisk = sanitise_filename(argument->dle.disk);
740 tardumpfile = vstralloc(star_tardumps, sdisk, NULL);
743 tardumpfile = stralloc(star_tardumps);
746 my_argv = alloc(SIZEOF(char *) * 32);
749 my_argv[i++] = star_path;
751 my_argv[i++] = stralloc("-c");
752 my_argv[i++] = stralloc("-f");
753 if (command == CMD_ESTIMATE) {
754 my_argv[i++] = stralloc("/dev/null");
756 my_argv[i++] = stralloc("-");
758 my_argv[i++] = stralloc("-C");
759 #if defined(__CYGWIN__)
761 char tmppath[PATH_MAX];
763 cygwin_conv_to_full_posix_path(dirname, tmppath);
764 my_argv[i++] = stralloc(tmppath);
767 my_argv[i++] = stralloc(dirname);
769 my_argv[i++] = stralloc(fsname);
770 if (star_onefilesystem)
771 my_argv[i++] = stralloc("-xdev");
772 my_argv[i++] = stralloc("-link-dirs");
773 my_argv[i++] = stralloc(levelstr);
774 my_argv[i++] = stralloc2("tardumps=", tardumpfile);
775 if (command == CMD_BACKUP)
776 my_argv[i++] = stralloc("-wtardumps");
777 my_argv[i++] = stralloc("-xattr");
778 my_argv[i++] = stralloc("-acl");
779 my_argv[i++] = stralloc("H=exustar");
780 my_argv[i++] = stralloc("errctl=WARN|SAMEFILE|DIFF|GROW|SHRINK|SPECIALFILE|GETXATTR|BADACL *");
782 my_argv[i++] = stralloc("-sparse");
783 my_argv[i++] = stralloc("-dodesc");
785 if (command == CMD_BACKUP && argument->dle.create_index)
786 my_argv[i++] = stralloc("-v");
788 my_argv[i++] = stralloc(".");