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: amflush.c,v 1.95 2006/07/25 21:41:24 martinea Exp $
29 * write files from work directory onto tape
41 #include "server_util.h"
42 #include "timestamp.h"
44 static char *conf_logdir;
47 char *reporter_program;
48 char *logroll_program;
50 char *amflush_timestamp;
51 char *amflush_datestamp;
54 void flush_holdingdisk(char *diskdir, char *datestamp);
55 static GSList * pick_datestamp(void);
56 void confirm(GSList *datestamp_list);
57 void redirect_stderr(void);
60 static int get_letter_from_user(void);
70 char **datearg = NULL;
75 int conf_usetimestamps;
79 pid_t driver_pid, reporter_pid;
83 GSList *holding_list=NULL, *holding_file;
85 char date_string[100];
86 char date_string_standard[100];
93 GSList *datestamp_list = NULL;
94 config_overwrites_t *cfg_ovr;
95 char **config_options;
98 * Configure program for internationalization:
99 * 1) Only set the message locale for now.
100 * 2) Set textdomain for all amanda related programs to "amanda"
101 * We don't want to be forced to support dozens of message catalogs.
103 setlocale(LC_MESSAGES, "C");
104 textdomain("amanda");
109 set_pname("amflush");
111 /* Don't die when child closes pipe */
112 signal(SIGPIPE, SIG_IGN);
114 dbopen(DBG_SUBDIR_SERVER);
116 erroutput_type = ERR_INTERACTIVE;
121 /* process arguments */
123 cfg_ovr = new_config_overwrites(argc/2);
124 while((opt = getopt(argc, argv, "bfso:D:")) != EOF) {
128 case 'f': foreground = 1;
130 case 's': redirect = 0;
132 case 'o': add_config_overwrite_opt(cfg_ovr, optarg);
134 case 'D': if (datearg == NULL)
135 datearg = alloc(21*SIZEOF(char *));
136 if(nb_datearg == 20) {
137 g_fprintf(stderr,_("maximum of 20 -D arguments.\n"));
140 datearg[nb_datearg++] = stralloc(optarg);
141 datearg[nb_datearg] = NULL;
145 argc -= optind, argv += optind;
147 if(!foreground && !redirect) {
148 g_fprintf(stderr,_("Can't redirect to stdout/stderr if not in forground.\n"));
153 error(_("Usage: amflush%s [-b] [-f] [-s] [-D date]* <confdir> [host [disk]* ]* [-o configoption]*"), versionsuffix());
157 config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_FATAL,
159 apply_config_overwrites(cfg_ovr);
160 check_running_as(RUNNING_AS_DUMPUSER);
162 dbrename(config_name, DBG_SUBDIR_SERVER);
164 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
165 if (read_diskfile(conf_diskfile, &diskq) < 0) {
166 error(_("could not read disklist file \"%s\""), conf_diskfile);
169 errstr = match_disklist(&diskq, argc-1, argv+1);
171 g_printf(_("%s"),errstr);
174 amfree(conf_diskfile);
176 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
177 if(read_tapelist(conf_tapelist)) {
178 error(_("could not load tapelist \"%s\""), conf_tapelist);
181 amfree(conf_tapelist);
183 conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
185 amflush_datestamp = get_datestamp_from_time(0);
186 if(conf_usetimestamps == 0) {
187 amflush_timestamp = stralloc(amflush_datestamp);
190 amflush_timestamp = get_timestamp_from_time(0);
193 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
194 conf_logfile = vstralloc(conf_logdir, "/log", NULL);
195 if (access(conf_logfile, F_OK) == 0) {
196 error(_("%s exists: amdump or amflush is already running, or you must run amcleanup"), conf_logfile);
199 amfree(conf_logfile);
201 driver_program = vstralloc(amlibexecdir, "/", "driver", versionsuffix(),
203 reporter_program = vstralloc(sbindir, "/", "amreport", versionsuffix(),
205 logroll_program = vstralloc(amlibexecdir, "/", "amlogroll", versionsuffix(),
208 tapedev = getconf_str(CNF_TAPEDEV);
209 tpchanger = getconf_str(CNF_TPCHANGER);
210 if (tapedev == NULL && tpchanger == NULL) {
211 error(_("No tapedev or tpchanger specified"));
214 /* if dates were specified (-D), then use match_datestamp
215 * against the list of all datestamps to turn that list
216 * into a set of existing datestamps (basically, evaluate the
217 * expressions into actual datestamps) */
219 GSList *all_datestamps;
223 all_datestamps = holding_get_all_datestamps();
224 for(datestamp = all_datestamps; datestamp != NULL; datestamp = datestamp->next) {
226 for(i=0; i<nb_datearg && ok==0; i++) {
227 ok = match_datestamp(datearg[i], (char *)datestamp->data);
230 datestamp_list = g_slist_insert_sorted(datestamp_list,
231 stralloc((char *)datestamp->data),
234 g_slist_free_full(all_datestamps);
237 /* otherwise, in batch mode, use all datestamps */
239 datestamp_list = holding_get_all_datestamps();
241 /* or allow the user to pick datestamps */
243 datestamp_list = pick_datestamp();
247 if(!datestamp_list) {
248 g_printf(_("Could not find any Amanda directories to flush.\n"));
252 holding_list = holding_get_files_for_flush(datestamp_list);
253 if (holding_list == NULL) {
254 g_printf(_("Could not find any valid dump image, check directory.\n"));
258 if(!batch) confirm(datestamp_list);
260 for(dp = diskq.head; dp != NULL; dp = dp->next) {
263 qname = quote_string(dp->name);
264 log_add(L_DISK, "%s %s", dp->host->hostname, qname);
269 if(!foreground) { /* write it before redirecting stdout */
270 puts(_("Running in background, you can log off now."));
271 puts(_("You'll get mail when amflush is finished."));
274 if(redirect) redirect_stderr();
276 if(!foreground) detach();
278 erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
279 set_logerror(logerror);
281 tm = localtime(&today);
283 strftime(date_string, 100, "%a %b %e %H:%M:%S %Z %Y", tm);
284 strftime(date_string_standard, 100, "%Y-%m-%d %H:%M:%S %Z", tm);
286 error(_("BAD DATE")); /* should never happen */
288 g_fprintf(stderr, _("amflush: start at %s\n"), date_string);
289 g_fprintf(stderr, _("amflush: datestamp %s\n"), amflush_timestamp);
290 g_fprintf(stderr, _("amflush: starttime %s\n"), amflush_timestamp);
291 g_fprintf(stderr, _("amflush: starttime-locale-independent %s\n"),
292 date_string_standard);
293 log_add(L_START, _("date %s"), amflush_timestamp);
296 if(pipe(driver_pipe) == -1) {
297 error(_("error [opening pipe to driver: %s]"), strerror(errno));
300 if((driver_pid = fork()) == 0) {
302 * This is the child process.
304 dup2(driver_pipe[0], 0);
305 close(driver_pipe[1]);
306 config_options = get_config_options(3);
307 config_options[0] = "driver";
308 config_options[1] = config_name;
309 config_options[2] = "nodump";
311 execve(driver_program, config_options, safe_env());
312 error(_("cannot exec %s: %s"), driver_program, strerror(errno));
314 } else if(driver_pid == -1) {
315 error(_("cannot fork for %s: %s"), driver_program, strerror(errno));
318 driver_stream = fdopen(driver_pipe[1], "w");
319 if (!driver_stream) {
320 error(_("Can't fdopen: %s"), strerror(errno));
324 g_fprintf(driver_stream, "DATE %s\n", amflush_timestamp);
325 for(holding_file=holding_list; holding_file != NULL;
326 holding_file = holding_file->next) {
327 holding_file_get_dumpfile((char *)holding_file->data, &file);
329 if (holding_file_size((char *)holding_file->data, 1) <= 0) {
330 log_add(L_INFO, "%s: removing file with no data.",
331 (char *)holding_file->data);
332 holding_file_unlink((char *)holding_file->data);
336 dp = lookup_disk(file.name, file.disk);
341 if (dp->todo == 0) continue;
343 qdisk = quote_string(file.disk);
344 qhname = quote_string((char *)holding_file->data);
346 "FLUSH %s %s %s %d %s\n",
352 g_fprintf(driver_stream,
353 "FLUSH %s %s %s %d %s\n",
362 g_fprintf(stderr, "ENDFLUSH\n"); fflush(stderr);
363 g_fprintf(driver_stream, "ENDFLUSH\n"); fflush(driver_stream);
364 fclose(driver_stream);
368 if((pid = wait(&exitcode)) == -1) {
372 error(_("wait for %s: %s"), driver_program, strerror(errno));
375 } else if (pid == driver_pid) {
380 g_slist_free_full(datestamp_list);
381 datestamp_list = NULL;
382 g_slist_free_full(holding_list);
385 if(redirect) { /* rename errfile */
386 char *errfile, *errfilex, *nerrfilex, number[100];
390 struct stat stat_buf;
392 errfile = vstralloc(conf_logdir, "/amflush", NULL);
395 tapecycle = getconf_int(CNF_TAPECYCLE);
396 maxdays = tapecycle + 2;
398 /* First, find out the last existing errfile, */
399 /* to avoid ``infinite'' loops if tapecycle is infinite */
401 g_snprintf(number,100,"%d",days);
402 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
403 while ( days < maxdays && stat(errfilex,&stat_buf)==0) {
405 g_snprintf(number,100,"%d",days);
406 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
408 g_snprintf(number,100,"%d",days);
409 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
413 nerrfilex = errfilex;
415 g_snprintf(number,100,"%d",days);
416 errfilex = vstralloc(errfile, ".", number, NULL);
417 if (rename(errfilex, nerrfilex) != 0) {
418 error(_("cannot rename \"%s\" to \"%s\": %s"),
419 errfilex, nerrfilex, strerror(errno));
423 errfilex = newvstralloc(errfilex, errfile, ".1", NULL);
424 if (rename(errfile,errfilex) != 0) {
425 error(_("cannot rename \"%s\" to \"%s\": %s"),
426 errfilex, nerrfilex, strerror(errno));
435 * Have amreport generate report and send mail. Note that we do
436 * not bother checking the exit status. If it does not work, it
440 if((reporter_pid = fork()) == 0) {
442 * This is the child process.
444 config_options = get_config_options(2);
445 config_options[0] = "amreport";
446 config_options[1] = config_name;
448 execve(reporter_program, config_options, safe_env());
449 error(_("cannot exec %s: %s"), reporter_program, strerror(errno));
451 } else if(reporter_pid == -1) {
452 error(_("cannot fork for %s: %s"), reporter_program, strerror(errno));
456 if((pid = wait(&exitcode)) == -1) {
460 error(_("wait for %s: %s"), reporter_program, strerror(errno));
463 } else if (pid == reporter_pid) {
469 * Call amlogroll to rename the log file to its datestamped version.
470 * Since we exec at this point, our exit code will be that of amlogroll.
472 config_options = get_config_options(2);
473 config_options[0] = "amlogroll";
474 config_options[1] = config_name;
476 execve(logroll_program, config_options, safe_env());
477 error(_("cannot exec %s: %s"), logroll_program, strerror(errno));
479 return 0; /* keep the compiler happy */
484 get_letter_from_user(void)
488 fflush(stdout); fflush(stderr);
489 while((ch = getchar()) != EOF && ch != '\n' && isspace(ch)) {
490 (void)ch; /* Quite lint */
494 } else if (ch != EOF) {
496 if(islower(r)) r = toupper(r);
497 while((ch = getchar()) != EOF && ch != '\n') {
498 (void)ch; /* Quite lint */
507 /* Allow the user to select a set of datestamps from those in
508 * holding disks. The result can be passed to
509 * holding_get_files_for_flush. If less than two dates are
510 * available, then no user interaction takes place.
512 * @returns: a new GSList listing the selected datestamps
517 GSList *datestamp_list;
518 GSList *r_datestamp_list = NULL;
520 char **datestamps = NULL;
525 char max_char = '\0', chupper = '\0';
527 datestamp_list = holding_get_all_datestamps();
529 if(g_slist_length(datestamp_list) < 2) {
530 return datestamp_list;
532 datestamps = alloc(g_slist_length(datestamp_list) * SIZEOF(char *));
533 for(ds = datestamp_list, i=0; ds != NULL; ds = ds->next,i++) {
534 datestamps[i] = (char *)ds->data; /* borrowing reference */
538 puts(_("\nMultiple Amanda runs in holding disks; please pick one by letter:"));
539 for(ds = datestamp_list, max_char = 'A';
540 ds != NULL && max_char <= 'Z';
541 ds = ds->next, max_char++) {
542 g_printf(" %c. %s\n", max_char, (char *)ds->data);
545 g_printf(_("Select datestamps to flush [A..%c or <enter> for all]: "), max_char);
546 fflush(stdout); fflush(stderr);
548 if ((answer = agets(stdin)) == NULL) {
553 if (*answer == '\0' || strncasecmp(answer, "ALL", 3) == 0) {
558 while ((ch = *a++) != '\0') {
563 /* rewrite the selected list into r_datestamp_list, then copy it over
564 * to datestamp_list */
566 if (isspace(ch) || ch == ',') {
569 chupper = (char)toupper(ch);
570 if (chupper < 'A' || chupper > max_char) {
571 g_slist_free_full(r_datestamp_list);
572 r_datestamp_list = NULL;
575 r_datestamp_list = g_slist_append(r_datestamp_list,
576 stralloc(datestamps[chupper - 'A']));
577 } while ((ch = *a++) != '\0');
578 if (r_datestamp_list && ch == '\0') {
579 g_slist_free_full(datestamp_list);
580 datestamp_list = r_datestamp_list;
585 amfree(datestamps); /* references in this array are borrowed */
588 return datestamp_list;
593 * confirm before detaching and running
597 confirm(GSList *datestamp_list)
605 g_printf(_("\nToday is: %s\n"),amflush_datestamp);
606 g_printf(_("Flushing dumps from"));
608 for(datestamp = datestamp_list; datestamp != NULL; datestamp = datestamp->next) {
609 g_printf("%s %s", extra, (char *)datestamp->data);
612 tpchanger = getconf_str(CNF_TPCHANGER);
613 if(*tpchanger != '\0') {
614 g_printf(_(" using tape changer \"%s\".\n"), tpchanger);
616 g_printf(_(" to tape drive \"%s\".\n"), getconf_str(CNF_TAPEDEV));
619 g_printf(_("Expecting "));
620 tp = lookup_last_reusable_tape(0);
622 g_printf(_("tape %s or "), tp->label);
623 g_printf(_("a new tape."));
624 tp = lookup_tapepos(1);
626 g_printf(_(" (The last dumps were to tape %s)"), tp->label);
629 g_printf(_("\nAre you sure you want to do this [yN]? "));
630 if((ch = get_letter_from_user()) == 'Y') {
632 } else if (ch == 'N' || ch == '\0' || ch == EOF) {
640 g_printf(_("Ok, quitting. Run amflush again when you are ready.\n"));
645 redirect_stderr(void)
650 fflush(stdout); fflush(stderr);
651 errfile = vstralloc(conf_logdir, "/amflush", NULL);
652 if((fderr = open(errfile, O_WRONLY| O_CREAT | O_TRUNC, 0600)) == -1) {
653 error(_("could not open %s: %s"), errfile, strerror(errno));
667 fflush(stdout); fflush(stderr);
668 if((fd = open("/dev/null", O_RDWR, 0666)) == -1) {
669 error(_("could not open /dev/null: %s"), strerror(errno));
678 error(_("could not fork: %s"), strerror(errno));