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"
43 static char *conf_logdir;
46 char *reporter_program;
47 char *logroll_program;
49 char *amflush_timestamp;
50 char *amflush_datestamp;
54 int main(int main_argc, char **main_argv);
55 void flush_holdingdisk(char *diskdir, char *datestamp);
57 void redirect_stderr(void);
60 static int get_letter_from_user(void);
72 char **datearg = NULL;
78 int conf_usetimestamps;
82 pid_t driver_pid, reporter_pid;
86 sl_t *holding_list=NULL;
89 char date_string[100];
91 int new_argc, my_argc;
92 char **new_argv, **my_argv;
102 set_pname("amflush");
104 dbopen(DBG_SUBDIR_SERVER);
106 /* Don't die when child closes pipe */
107 signal(SIGPIPE, SIG_IGN);
109 erroutput_type = ERR_INTERACTIVE;
114 /* process arguments */
116 parse_conf(main_argc, main_argv, &new_argc, &new_argv);
120 while((opt = getopt(my_argc, my_argv, "bfsD:")) != EOF) {
124 case 'f': foreground = 1;
126 case 's': redirect = 0;
128 case 'D': if (datearg == NULL)
129 datearg = alloc(21*SIZEOF(char *));
130 if(nb_datearg == 20) {
131 fprintf(stderr,"maximum of 20 -D arguments.\n");
134 datearg[nb_datearg++] = stralloc(optarg);
135 datearg[nb_datearg] = NULL;
139 if(!foreground && !redirect) {
140 fprintf(stderr,"Can't redirect to stdout/stderr if not in forground.\n");
144 my_argc -= optind, my_argv += optind;
147 error("Usage: amflush%s [-b] [-f] [-s] [-D date]* <confdir> [host [disk]* ]* [-o configoption]*", versionsuffix());
151 config_name = my_argv[0];
152 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
154 conffile = stralloc2(config_dir, CONFFILE_NAME);
155 if(read_conffile(conffile)) {
156 error("errors processing config file \"%s\"", conffile);
161 dbrename(config_name, DBG_SUBDIR_SERVER);
163 report_bad_conf_arg();
165 conf_diskfile = getconf_str(CNF_DISKFILE);
166 if (*conf_diskfile == '/') {
167 conf_diskfile = stralloc(conf_diskfile);
169 conf_diskfile = stralloc2(config_dir, conf_diskfile);
171 if (read_diskfile(conf_diskfile, &diskq) < 0) {
172 error("could not read disklist file \"%s\"", conf_diskfile);
175 errstr = match_disklist(&diskq, my_argc-1, my_argv+1);
180 amfree(conf_diskfile);
182 conf_tapelist = getconf_str(CNF_TAPELIST);
183 if (*conf_tapelist == '/') {
184 conf_tapelist = stralloc(conf_tapelist);
186 conf_tapelist = stralloc2(config_dir, conf_tapelist);
188 if(read_tapelist(conf_tapelist)) {
189 error("could not load tapelist \"%s\"", conf_tapelist);
192 amfree(conf_tapelist);
194 conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
196 amflush_datestamp = construct_datestamp(NULL);
197 if(conf_usetimestamps == 0) {
198 amflush_timestamp = stralloc(amflush_datestamp);
201 amflush_timestamp = construct_timestamp(NULL);
204 dumpuser = getconf_str(CNF_DUMPUSER);
205 if((pw = getpwnam(dumpuser)) == NULL) {
206 error("dumpuser %s not found in password file", dumpuser);
209 if(pw->pw_uid != getuid()) {
210 error("must run amflush as user %s", dumpuser);
214 conf_logdir = getconf_str(CNF_LOGDIR);
215 if (*conf_logdir == '/') {
216 conf_logdir = stralloc(conf_logdir);
218 conf_logdir = stralloc2(config_dir, conf_logdir);
220 conf_logfile = vstralloc(conf_logdir, "/log", NULL);
221 if (access(conf_logfile, F_OK) == 0) {
222 error("%s exists: amdump or amflush is already running, or you must run amcleanup", conf_logfile);
225 amfree(conf_logfile);
227 driver_program = vstralloc(libexecdir, "/", "driver", versionsuffix(),
229 reporter_program = vstralloc(sbindir, "/", "amreport", versionsuffix(),
231 logroll_program = vstralloc(libexecdir, "/", "amlogroll", versionsuffix(),
234 tapedev = getconf_str(CNF_TAPEDEV);
235 tpchanger = getconf_str(CNF_TPCHANGER);
236 if (tapedev == NULL && tpchanger == NULL) {
237 error("No tapedev or tpchanger specified");
241 sle_t *dir, *next_dir;
244 datestamp_list = pick_all_datestamp(1);
245 for(dir = datestamp_list->first; dir != NULL;) {
246 next_dir = dir->next;
248 for(i=0; i<nb_datearg && ok==0; i++) {
249 ok = match_datestamp(datearg[i], dir->name);
251 if(ok == 0) { /* remove dir */
252 remove_sl(datestamp_list, dir);
259 datestamp_list = pick_all_datestamp(1);
262 datestamp_list = pick_datestamp(1);
266 if(is_empty_sl(datestamp_list)) {
267 printf("Could not find any Amanda directories to flush.\n");
271 holding_list = holding_get_files_for_flush(datestamp_list, 1);
272 if(holding_list->first == NULL) {
273 printf("Could not find any valid dump image, check directory.\n");
277 if(!batch) confirm();
279 for(dp = diskq.head; dp != NULL; dp = dp->next) {
282 qname = quote_string(dp->name);
283 log_add(L_DISK, "%s %s", dp->host->hostname, qname);
288 if(!foreground) { /* write it before redirecting stdout */
289 puts("Running in background, you can log off now.");
290 puts("You'll get mail when amflush is finished.");
293 if(redirect) redirect_stderr();
295 if(!foreground) detach();
297 erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
298 set_logerror(logerror);
300 tm = localtime(&today);
302 strftime(date_string, 100, "%a %b %e %H:%M:%S %Z %Y", tm);
304 error("BAD DATE"); /* should never happen */
305 fprintf(stderr, "amflush: start at %s\n", date_string);
306 fprintf(stderr, "amflush: datestamp %s\n", amflush_timestamp);
307 fprintf(stderr, "amflush: starttime %s\n", construct_timestamp(NULL));
308 log_add(L_START, "date %s", amflush_timestamp);
311 if(pipe(driver_pipe) == -1) {
312 error("error [opening pipe to driver: %s]", strerror(errno));
315 if((driver_pid = fork()) == 0) {
317 * This is the child process.
319 dup2(driver_pipe[0], 0);
320 close(driver_pipe[1]);
321 execle(driver_program,
322 "driver", config_name, "nodump", (char *)0,
324 error("cannot exec %s: %s", driver_program, strerror(errno));
326 } else if(driver_pid == -1) {
327 error("cannot fork for %s: %s", driver_program, strerror(errno));
330 driver_stream = fdopen(driver_pipe[1], "w");
331 if (!driver_stream) {
332 error("Can't fdopen: %s", strerror(errno));
336 fprintf(driver_stream, "DATE %s\n", amflush_timestamp);
337 for(holding_file=holding_list->first; holding_file != NULL;
338 holding_file = holding_file->next) {
339 holding_file_get_dumpfile(holding_file->name, &file);
341 if (holding_file_size(holding_file->name, 1) <= 0) {
342 log_add(L_INFO, "%s: removing file with no data.",
344 holding_file_unlink(holding_file->name);
348 dp = lookup_disk(file.name, file.disk);
353 if (dp->todo == 0) continue;
355 qdisk = quote_string(file.disk);
356 qhname = quote_string(holding_file->name);
358 "FLUSH %s %s %s %d %s\n",
364 fprintf(driver_stream,
365 "FLUSH %s %s %s %d %s\n",
374 fprintf(stderr, "ENDFLUSH\n"); fflush(stderr);
375 fprintf(driver_stream, "ENDFLUSH\n"); fflush(driver_stream);
376 fclose(driver_stream);
380 if((pid = wait(&exitcode)) == -1) {
384 error("wait for %s: %s", driver_program, strerror(errno));
387 } else if (pid == driver_pid) {
392 free_sl(datestamp_list);
393 datestamp_list = NULL;
394 free_sl(holding_list);
397 if(redirect) { /* rename errfile */
398 char *errfile, *errfilex, *nerrfilex, number[100];
402 struct stat stat_buf;
404 errfile = vstralloc(conf_logdir, "/amflush", NULL);
407 tapecycle = getconf_int(CNF_TAPECYCLE);
408 maxdays = tapecycle + 2;
410 /* First, find out the last existing errfile, */
411 /* to avoid ``infinite'' loops if tapecycle is infinite */
413 snprintf(number,100,"%d",days);
414 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
415 while ( days < maxdays && stat(errfilex,&stat_buf)==0) {
417 snprintf(number,100,"%d",days);
418 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
420 snprintf(number,100,"%d",days);
421 errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
425 nerrfilex = errfilex;
427 snprintf(number,100,"%d",days);
428 errfilex = vstralloc(errfile, ".", number, NULL);
429 if (rename(errfilex, nerrfilex) != 0) {
430 error("cannot rename \"%s\" to \"%s\": %s",
431 errfilex, nerrfilex, strerror(errno));
435 errfilex = newvstralloc(errfilex, errfile, ".1", NULL);
436 if (rename(errfile,errfilex) != 0) {
437 error("cannot rename \"%s\" to \"%s\": %s",
438 errfilex, nerrfilex, strerror(errno));
447 * Have amreport generate report and send mail. Note that we do
448 * not bother checking the exit status. If it does not work, it
452 if((reporter_pid = fork()) == 0) {
454 * This is the child process.
456 execle(reporter_program,
457 "amreport", config_name, (char *)0,
459 error("cannot exec %s: %s", reporter_program, strerror(errno));
461 } else if(reporter_pid == -1) {
462 error("cannot fork for %s: %s", reporter_program, strerror(errno));
466 if((pid = wait(&exitcode)) == -1) {
470 error("wait for %s: %s", reporter_program, strerror(errno));
473 } else if (pid == reporter_pid) {
479 * Call amlogroll to rename the log file to its datestamped version.
480 * Since we exec at this point, our exit code will be that of amlogroll.
482 execle(logroll_program,
483 "amlogroll", config_name, (char *)0,
485 error("cannot exec %s: %s", logroll_program, strerror(errno));
487 return 0; /* keep the compiler happy */
492 get_letter_from_user(void)
496 fflush(stdout); fflush(stderr);
497 while((ch = getchar()) != EOF && ch != '\n' && isspace(ch)) {
498 (void)ch; /* Quite lint */
502 } else if (ch != EOF) {
504 if(islower(r)) r = toupper(r);
505 while((ch = getchar()) != EOF && ch != '\n') {
506 (void)ch; /* Quite lint */
517 * confirm before detaching and running
529 printf("\nToday is: %s\n",amflush_datestamp);
530 printf("Flushing dumps in");
532 for(dir = datestamp_list->first; dir != NULL; dir = dir->next) {
533 printf("%s %s", extra, dir->name);
536 tpchanger = getconf_str(CNF_TPCHANGER);
537 if(*tpchanger != '\0') {
538 printf(" using tape changer \"%s\".\n", tpchanger);
540 printf(" to tape drive \"%s\".\n", getconf_str(CNF_TAPEDEV));
543 printf("Expecting ");
544 tp = lookup_last_reusable_tape(0);
545 if(tp != NULL) printf("tape %s or ", tp->label);
546 printf("a new tape.");
547 tp = lookup_tapepos(1);
548 if(tp != NULL) printf(" (The last dumps were to tape %s)", tp->label);
551 printf("\nAre you sure you want to do this [yN]? ");
552 if((ch = get_letter_from_user()) == 'Y') {
554 } else if (ch == 'N' || ch == '\0' || ch == EOF) {
562 printf("Ok, quitting. Run amflush again when you are ready.\n");
567 redirect_stderr(void)
572 fflush(stdout); fflush(stderr);
573 errfile = vstralloc(conf_logdir, "/amflush", NULL);
574 if((fderr = open(errfile, O_WRONLY| O_CREAT | O_TRUNC, 0600)) == -1) {
575 error("could not open %s: %s", errfile, strerror(errno));
589 fflush(stdout); fflush(stderr);
590 if((fd = open("/dev/null", O_RDWR, 0666)) == -1) {
591 error("could not open /dev/null: %s", strerror(errno));
600 error("could not fork: %s", strerror(errno));