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: amflush.c,v 1.95 2006/07/25 21:41:24 martinea Exp $
30 * write files from work directory onto tape
42 #include "server_util.h"
43 #include "timestamp.h"
46 static struct option long_options[] = {
47 {"version" , 0, NULL, 1},
48 {"exact-match" , 0, NULL, 2},
52 static char *conf_logdir;
55 char *reporter_program;
56 char *logroll_program;
58 char *amflush_timestamp;
59 char *amflush_datestamp;
62 void flush_holdingdisk(char *diskdir, char *datestamp);
63 static GSList * pick_datestamp(void);
64 void confirm(GSList *datestamp_list);
65 void redirect_stderr(void);
68 static int get_letter_from_user(void);
78 char **datearg = NULL;
83 int conf_usetimestamps;
87 pid_t driver_pid, reporter_pid;
90 GSList *holding_list=NULL, *holding_file;
92 char date_string[100];
93 char date_string_standard[100];
100 GSList *datestamp_list = NULL;
101 config_overrides_t *cfg_ovr;
102 char **config_options;
103 find_result_t *holding_files;
104 disklist_t holding_disklist = { NULL, NULL };
105 gboolean exact_match = FALSE;
108 * Configure program for internationalization:
109 * 1) Only set the message locale for now.
110 * 2) Set textdomain for all amanda related programs to "amanda"
111 * We don't want to be forced to support dozens of message catalogs.
113 setlocale(LC_MESSAGES, "C");
114 textdomain("amanda");
119 set_pname("amflush");
121 /* Don't die when child closes pipe */
122 signal(SIGPIPE, SIG_IGN);
124 dbopen(DBG_SUBDIR_SERVER);
126 add_amanda_log_handler(amanda_log_stderr);
131 /* process arguments */
133 cfg_ovr = new_config_overrides(argc/2);
134 while((opt = getopt_long(argc, argv, "bfso:D:", long_options, NULL)) != EOF) {
136 case 1 : printf("amflush-%s\n", VERSION);
139 case 2 : exact_match = TRUE;
143 case 'f': foreground = 1;
145 case 's': redirect = 0;
147 case 'o': add_config_override_opt(cfg_ovr, optarg);
149 case 'D': if (datearg == NULL)
150 datearg = alloc(21*SIZEOF(char *));
151 if(nb_datearg == 20) {
152 g_fprintf(stderr,_("maximum of 20 -D arguments.\n"));
155 datearg[nb_datearg++] = stralloc(optarg);
156 datearg[nb_datearg] = NULL;
160 argc -= optind, argv += optind;
162 if(!foreground && !redirect) {
163 g_fprintf(stderr,_("Can't redirect to stdout/stderr if not in forground.\n"));
168 error(_("Usage: amflush [-b] [-f] [-s] [-D date]* [--exact-match] [-o configoption]* <confdir> [host [disk]* ]*"));
172 set_config_overrides(cfg_ovr);
173 config_init(CONFIG_INIT_EXPLICIT_NAME,
176 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
177 read_diskfile(conf_diskfile, &diskq);
178 amfree(conf_diskfile);
180 if (config_errors(NULL) >= CFGERR_WARNINGS) {
181 config_print_errors();
182 if (config_errors(NULL) >= CFGERR_ERRORS) {
183 g_critical(_("errors processing config file"));
187 check_running_as(RUNNING_AS_DUMPUSER);
189 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
191 /* load DLEs from the holding disk, in case there's anything to flush there */
192 search_holding_disk(&holding_files, &holding_disklist);
193 /* note that the dumps are added to the global disklist, so we need not
194 * consult holding_files or holding_disklist after this. The holding-only
195 * dumps will be filtered properly by match_disklist, setting the dp->todo
196 * flag appropriately. */
198 errstr = match_disklist(&diskq, exact_match, argc-1, argv+1);
200 g_printf(_("%s"),errstr);
204 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
205 if(read_tapelist(conf_tapelist)) {
206 error(_("could not load tapelist \"%s\""), conf_tapelist);
209 amfree(conf_tapelist);
211 conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
213 amflush_datestamp = get_datestamp_from_time(0);
214 if(conf_usetimestamps == 0) {
215 amflush_timestamp = stralloc(amflush_datestamp);
218 amflush_timestamp = get_timestamp_from_time(0);
221 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
222 conf_logfile = vstralloc(conf_logdir, "/log", NULL);
223 if (access(conf_logfile, F_OK) == 0) {
224 run_amcleanup(get_config_name());
226 if (access(conf_logfile, F_OK) == 0) {
227 char *process_name = get_master_process(conf_logfile);
228 error(_("%s exists: %s is already running, or you must run amcleanup"), conf_logfile, process_name);
232 driver_program = vstralloc(amlibexecdir, "/", "driver", NULL);
233 reporter_program = vstralloc(sbindir, "/", "amreport", NULL);
234 logroll_program = vstralloc(amlibexecdir, "/", "amlogroll", NULL);
236 tapedev = getconf_str(CNF_TAPEDEV);
237 tpchanger = getconf_str(CNF_TPCHANGER);
238 if (tapedev == NULL && tpchanger == NULL) {
239 error(_("No tapedev or tpchanger specified"));
242 /* if dates were specified (-D), then use match_datestamp
243 * against the list of all datestamps to turn that list
244 * into a set of existing datestamps (basically, evaluate the
245 * expressions into actual datestamps) */
247 GSList *all_datestamps;
251 all_datestamps = holding_get_all_datestamps();
252 for(datestamp = all_datestamps; datestamp != NULL; datestamp = datestamp->next) {
254 for(i=0; i<nb_datearg && ok==0; i++) {
255 ok = match_datestamp(datearg[i], (char *)datestamp->data);
258 datestamp_list = g_slist_insert_sorted(datestamp_list,
259 stralloc((char *)datestamp->data),
262 slist_free_full(all_datestamps, g_free);
265 /* otherwise, in batch mode, use all datestamps */
267 datestamp_list = holding_get_all_datestamps();
269 /* or allow the user to pick datestamps */
271 datestamp_list = pick_datestamp();
275 if(!datestamp_list) {
276 g_printf(_("Could not find any Amanda directories to flush.\n"));
280 holding_list = holding_get_files_for_flush(datestamp_list);
281 if (holding_list == NULL) {
282 g_printf(_("Could not find any valid dump image, check directory.\n"));
286 if (access(conf_logfile, F_OK) == 0) {
287 char *process_name = get_master_process(conf_logfile);
288 error(_("%s exists: someone started %s"), conf_logfile, process_name);
291 log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
293 if(!batch) confirm(datestamp_list);
295 for(dp = diskq.head; dp != NULL; dp = dp->next) {
297 /* is it holding_list */
298 for (holding_file=holding_list; holding_file != NULL;
299 holding_file = holding_file->next) {
301 holding_file_get_dumpfile((char *)holding_file->data, &file);
302 if (g_str_equal(dp->host->hostname, file.name) &&
303 g_str_equal(dp->name, file.disk)) {
305 qname = quote_string(dp->name);
306 log_add(L_DISK, "%s %s", dp->host->hostname, qname);
313 if(!foreground) { /* write it before redirecting stdout */
314 puts(_("Running in background, you can log off now."));
315 puts(_("You'll get mail when amflush is finished."));
318 if(redirect) redirect_stderr();
320 if(!foreground) detach();
322 add_amanda_log_handler(amanda_log_stderr);
323 add_amanda_log_handler(amanda_log_trace_log);
325 tm = localtime(&today);
327 strftime(date_string, 100, "%a %b %e %H:%M:%S %Z %Y", tm);
328 strftime(date_string_standard, 100, "%Y-%m-%d %H:%M:%S %Z", tm);
330 error(_("BAD DATE")); /* should never happen */
332 g_fprintf(stderr, _("amflush: start at %s\n"), date_string);
333 g_fprintf(stderr, _("amflush: datestamp %s\n"), amflush_timestamp);
334 g_fprintf(stderr, _("amflush: starttime %s\n"), amflush_timestamp);
335 g_fprintf(stderr, _("amflush: starttime-locale-independent %s\n"),
336 date_string_standard);
337 log_add(L_START, _("date %s"), amflush_timestamp);
340 if(pipe(driver_pipe) == -1) {
341 error(_("error [opening pipe to driver: %s]"), strerror(errno));
344 if((driver_pid = fork()) == 0) {
346 * This is the child process.
348 dup2(driver_pipe[0], 0);
349 close(driver_pipe[1]);
350 config_options = get_config_options(3);
351 config_options[0] = "driver";
352 config_options[1] = get_config_name();
353 config_options[2] = "nodump";
355 execve(driver_program, config_options, safe_env());
356 error(_("cannot exec %s: %s"), driver_program, strerror(errno));
358 } else if(driver_pid == -1) {
359 error(_("cannot fork for %s: %s"), driver_program, strerror(errno));
362 driver_stream = fdopen(driver_pipe[1], "w");
363 if (!driver_stream) {
364 error(_("Can't fdopen: %s"), strerror(errno));
368 g_fprintf(driver_stream, "DATE %s\n", amflush_timestamp);
369 for(holding_file=holding_list; holding_file != NULL;
370 holding_file = holding_file->next) {
372 holding_file_get_dumpfile((char *)holding_file->data, &file);
374 if (holding_file_size((char *)holding_file->data, 1) <= 0) {
375 g_debug("%s is empty - ignoring", (char *)holding_file->data);
376 log_add(L_INFO, "%s: removing file with no data.",
377 (char *)holding_file->data);
378 holding_file_unlink((char *)holding_file->data);
379 dumpfile_free_data(&file);
383 /* search_holding_disk should have already ensured that every
384 * holding dumpfile has an entry in the dynamic disklist */
385 dp = lookup_disk(file.name, file.disk);
388 /* but match_disklist may have indicated we should not flush it */
389 if (dp->todo == 0) continue;
391 qdisk = quote_string(file.disk);
392 qhname = quote_string((char *)holding_file->data);
394 "FLUSH %s %s %s %d %s\n",
401 g_debug("flushing '%s'", (char *)holding_file->data);
402 g_fprintf(driver_stream,
403 "FLUSH %s %s %s %d %s\n",
411 dumpfile_free_data(&file);
413 g_fprintf(stderr, "ENDFLUSH\n"); fflush(stderr);
414 g_fprintf(driver_stream, "ENDFLUSH\n"); fflush(driver_stream);
415 fclose(driver_stream);
419 if((pid = wait(&exitcode)) == -1) {
423 error(_("wait for %s: %s"), driver_program, strerror(errno));
426 } else if (pid == driver_pid) {
431 slist_free_full(datestamp_list, g_free);
432 datestamp_list = NULL;
433 slist_free_full(holding_list, g_free);
436 if(redirect) { /* rename errfile */
437 char *errfile, *errfilex, *nerrfilex, number[100];
441 struct stat stat_buf;
443 errfile = vstralloc(conf_logdir, "/amflush", NULL);
446 tapecycle = getconf_int(CNF_TAPECYCLE);
447 maxdays = tapecycle + 2;
449 /* First, find out the last existing errfile, */
450 /* to avoid ``infinite'' loops if tapecycle is infinite */
452 g_snprintf(number,100,"%d",days);
453 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
454 while ( days < maxdays && stat(errfilex,&stat_buf)==0) {
456 g_snprintf(number,100,"%d",days);
457 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
459 g_snprintf(number,100,"%d",days);
460 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
464 nerrfilex = errfilex;
466 g_snprintf(number,100,"%d",days);
467 errfilex = vstralloc(errfile, ".", number, NULL);
468 if (rename(errfilex, nerrfilex) != 0) {
469 error(_("cannot rename \"%s\" to \"%s\": %s"),
470 errfilex, nerrfilex, strerror(errno));
474 errfilex = newvstralloc(errfilex, errfile, ".1", NULL);
475 if (rename(errfile,errfilex) != 0) {
476 error(_("cannot rename \"%s\" to \"%s\": %s"),
477 errfilex, nerrfilex, strerror(errno));
486 * Have amreport generate report and send mail. Note that we do
487 * not bother checking the exit status. If it does not work, it
491 if((reporter_pid = fork()) == 0) {
493 * This is the child process.
495 config_options = get_config_options(3);
496 config_options[0] = "amreport";
497 config_options[1] = get_config_name();
498 config_options[2] = "--from-amdump";
500 execve(reporter_program, config_options, safe_env());
501 error(_("cannot exec %s: %s"), reporter_program, strerror(errno));
503 } else if(reporter_pid == -1) {
504 error(_("cannot fork for %s: %s"), reporter_program, strerror(errno));
508 if((pid = wait(&exitcode)) == -1) {
512 error(_("wait for %s: %s"), reporter_program, strerror(errno));
515 } else if (pid == reporter_pid) {
520 log_add(L_INFO, "pid-done %ld", (long)getpid());
523 * Call amlogroll to rename the log file to its datestamped version.
524 * Since we exec at this point, our exit code will be that of amlogroll.
526 config_options = get_config_options(2);
527 config_options[0] = "amlogroll";
528 config_options[1] = get_config_name();
530 execve(logroll_program, config_options, safe_env());
531 error(_("cannot exec %s: %s"), logroll_program, strerror(errno));
533 return 0; /* keep the compiler happy */
538 get_letter_from_user(void)
542 fflush(stdout); fflush(stderr);
543 while((ch = getchar()) != EOF && ch != '\n' && g_ascii_isspace(ch)) {
544 (void)ch; /* Quite lint */
548 } else if (ch != EOF) {
550 if(islower(r)) r = toupper(r);
551 while((ch = getchar()) != EOF && ch != '\n') {
552 (void)ch; /* Quite lint */
561 /* Allow the user to select a set of datestamps from those in
562 * holding disks. The result can be passed to
563 * holding_get_files_for_flush. If less than two dates are
564 * available, then no user interaction takes place.
566 * @returns: a new GSList listing the selected datestamps
571 GSList *datestamp_list;
572 GSList *r_datestamp_list = NULL;
574 char **datestamps = NULL;
579 char max_char = '\0', chupper = '\0';
581 datestamp_list = holding_get_all_datestamps();
583 if(g_slist_length(datestamp_list) < 2) {
584 return datestamp_list;
586 datestamps = alloc(g_slist_length(datestamp_list) * SIZEOF(char *));
587 for(ds = datestamp_list, i=0; ds != NULL; ds = ds->next,i++) {
588 datestamps[i] = (char *)ds->data; /* borrowing reference */
592 puts(_("\nMultiple Amanda runs in holding disks; please pick one by letter:"));
593 for(ds = datestamp_list, max_char = 'A';
594 ds != NULL && max_char <= 'Z';
595 ds = ds->next, max_char++) {
596 g_printf(" %c. %s\n", max_char, (char *)ds->data);
599 g_printf(_("Select datestamps to flush [A..%c or <enter> for all]: "), max_char);
600 fflush(stdout); fflush(stderr);
602 if ((answer = agets(stdin)) == NULL) {
607 if (*answer == '\0' || strncasecmp(answer, "ALL", 3) == 0) {
612 while ((ch = *a++) != '\0') {
613 if (!g_ascii_isspace(ch))
617 /* rewrite the selected list into r_datestamp_list, then copy it over
618 * to datestamp_list */
620 if (g_ascii_isspace(ch) || ch == ',') {
623 chupper = (char)toupper(ch);
624 if (chupper < 'A' || chupper > max_char) {
625 slist_free_full(r_datestamp_list, g_free);
626 r_datestamp_list = NULL;
629 r_datestamp_list = g_slist_append(r_datestamp_list,
630 stralloc(datestamps[chupper - 'A']));
631 } while ((ch = *a++) != '\0');
632 if (r_datestamp_list && ch == '\0') {
633 slist_free_full(datestamp_list, g_free);
634 datestamp_list = r_datestamp_list;
639 amfree(datestamps); /* references in this array are borrowed */
642 return datestamp_list;
647 * confirm before detaching and running
651 confirm(GSList *datestamp_list)
659 g_printf(_("\nToday is: %s\n"),amflush_datestamp);
660 g_printf(_("Flushing dumps from"));
662 for(datestamp = datestamp_list; datestamp != NULL; datestamp = datestamp->next) {
663 g_printf("%s %s", extra, (char *)datestamp->data);
666 tpchanger = getconf_str(CNF_TPCHANGER);
667 if(*tpchanger != '\0') {
668 g_printf(_(" using tape changer \"%s\".\n"), tpchanger);
670 g_printf(_(" to tape drive \"%s\".\n"), getconf_str(CNF_TAPEDEV));
673 g_printf(_("Expecting "));
674 tp = lookup_last_reusable_tape(0);
676 g_printf(_("tape %s or "), tp->label);
677 g_printf(_("a new tape."));
678 tp = lookup_tapepos(1);
680 g_printf(_(" (The last dumps were to tape %s)"), tp->label);
683 g_printf(_("\nAre you sure you want to do this [yN]? "));
684 if((ch = get_letter_from_user()) == 'Y') {
686 } else if (ch == 'N' || ch == '\0' || ch == EOF) {
694 g_printf(_("Ok, quitting. Run amflush again when you are ready.\n"));
695 log_add(L_INFO, "pid-done %ld", (long)getpid());
700 redirect_stderr(void)
705 fflush(stdout); fflush(stderr);
706 errfile = vstralloc(conf_logdir, "/amflush", NULL);
707 if((fderr = open(errfile, O_WRONLY| O_APPEND | O_CREAT | O_TRUNC, 0600)) == -1) {
708 error(_("could not open %s: %s"), errfile, strerror(errno));
722 fflush(stdout); fflush(stderr);
723 if((fd = open("/dev/null", O_RDWR, 0666)) == -1) {
724 error(_("could not open /dev/null: %s"), strerror(errno));
733 error(_("could not fork: %s"), strerror(errno));