c8d5a4a428221e588ab6958be46e5e77d88abe4a
[debian/amanda] / server-src / amflush.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: amflush.c,v 1.41.2.13.4.6.2.9.2.3 2005/09/20 21:31:52 jrjackson Exp $
28  *
29  * write files from work directory onto tape
30  */
31 #include "amanda.h"
32
33 #include "conffile.h"
34 #include "diskfile.h"
35 #include "tapefile.h"
36 #include "logfile.h"
37 #include "clock.h"
38 #include "version.h"
39 #include "holding.h"
40 #include "driverio.h"
41 #include "server_util.h"
42
43 static char *conf_logdir;
44 FILE *driver_stream;
45 char *driver_program;
46 char *reporter_program;
47 char *logroll_program;
48 char *datestamp;
49 sl_t *datestamp_list;
50
51 /* local functions */
52 int main P((int main_argc, char **main_argv));
53 void flush_holdingdisk P((char *diskdir, char *datestamp));
54 void confirm P((void));
55 void redirect_stderr P((void));
56 void detach P((void));
57 void run_dumps P((void));
58
59
60 int main(main_argc, main_argv)
61 int main_argc;
62 char **main_argv;
63 {
64     int foreground;
65     int batch;
66     int redirect;
67     struct passwd *pw;
68     char *dumpuser;
69     char **datearg = NULL;
70     int nb_datearg = 0;
71     char *conffile;
72     char *conf_diskfile;
73     char *conf_tapelist;
74     char *conf_logfile;
75     disklist_t *diskqp;
76     disk_t *dp;
77     pid_t pid, driver_pid, reporter_pid;
78     amwait_t exitcode;
79     int opt;
80     dumpfile_t file;
81     sl_t *holding_list=NULL;
82     sle_t *holding_file;
83     int driver_pipe[2];
84     char date_string[100];
85     time_t today;
86
87     safe_fd(-1, 0);
88     safe_cd();
89
90     set_pname("amflush");
91
92     signal(SIGPIPE, SIG_IGN);
93
94     erroutput_type = ERR_INTERACTIVE;
95     foreground = 0;
96     batch = 0;
97     redirect = 1;
98
99     /* process arguments */
100
101     while((opt = getopt(main_argc, main_argv, "bfsD:")) != EOF) {
102         switch(opt) {
103         case 'b': batch = 1;
104                   break;
105         case 'f': foreground = 1;
106                   break;
107         case 's': redirect = 0;
108                   break;
109         case 'D': if (datearg == NULL)
110                         datearg = alloc(21*sizeof(char *));
111                   if(nb_datearg == 20) {
112                       fprintf(stderr,"maximum of 20 -D arguments.\n");
113                       exit(1);
114                   }
115                   datearg[nb_datearg++] = stralloc(optarg);
116                   datearg[nb_datearg] = NULL;
117                   break;
118         }
119     }
120     if(!foreground && !redirect) {
121         fprintf(stderr,"Can't redirect to stdout/stderr if not in forground.\n");
122         exit(1);
123     }
124
125     main_argc -= optind, main_argv += optind;
126
127     if(main_argc < 1) {
128         error("Usage: amflush%s [-b] [-f] [-s] [-D date]* <confdir> [host [disk]* ]*", versionsuffix());
129     }
130
131     config_name = main_argv[0];
132
133     config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
134     conffile = stralloc2(config_dir, CONFFILE_NAME);
135     if(read_conffile(conffile)) {
136         error("errors processing config file \"%s\"", conffile);
137     }
138     amfree(conffile);
139     conf_diskfile = getconf_str(CNF_DISKFILE);
140     if (*conf_diskfile == '/') {
141         conf_diskfile = stralloc(conf_diskfile);
142     } else {
143         conf_diskfile = stralloc2(config_dir, conf_diskfile);
144     }
145     if((diskqp = read_diskfile(conf_diskfile)) == NULL) {
146         error("could not read disklist file \"%s\"", conf_diskfile);
147     }
148     match_disklist(diskqp, main_argc-1, main_argv+1);
149     amfree(conf_diskfile);
150     conf_tapelist = getconf_str(CNF_TAPELIST);
151     if (*conf_tapelist == '/') {
152         conf_tapelist = stralloc(conf_tapelist);
153     } else {
154         conf_tapelist = stralloc2(config_dir, conf_tapelist);
155     }
156     if(read_tapelist(conf_tapelist)) {
157         error("could not load tapelist \"%s\"", conf_tapelist);
158     }
159     amfree(conf_tapelist);
160
161     datestamp = construct_datestamp(NULL);
162
163     dumpuser = getconf_str(CNF_DUMPUSER);
164     if((pw = getpwnam(dumpuser)) == NULL)
165         error("dumpuser %s not found in password file", dumpuser);
166     if(pw->pw_uid != getuid())
167         error("must run amflush as user %s", dumpuser);
168
169     conf_logdir = getconf_str(CNF_LOGDIR);
170     if (*conf_logdir == '/') {
171         conf_logdir = stralloc(conf_logdir);
172     } else {
173         conf_logdir = stralloc2(config_dir, conf_logdir);
174     }
175     conf_logfile = vstralloc(conf_logdir, "/log", NULL);
176     if (access(conf_logfile, F_OK) == 0) {
177         error("%s exists: amdump or amflush is already running, or you must run amcleanup", conf_logfile);
178     }
179     amfree(conf_logfile);
180  
181     driver_program = vstralloc(libexecdir, "/", "driver", versionsuffix(),
182                                  NULL);
183     reporter_program = vstralloc(sbindir, "/", "amreport", versionsuffix(),
184                                  NULL);
185     logroll_program = vstralloc(libexecdir, "/", "amlogroll", versionsuffix(),
186                                 NULL);
187
188     if(datearg) {
189         sle_t *dir, *next_dir;
190         int i, ok;
191
192         datestamp_list = pick_all_datestamp(1);
193         for(dir = datestamp_list->first; dir != NULL;) {
194             next_dir = dir->next;
195             ok = 0;
196             for(i=0; i<nb_datearg && ok==0; i++) {
197                 ok = match_datestamp(datearg[i], dir->name);
198             }
199             if(ok == 0) { /* remove dir */
200                 remove_sl(datestamp_list, dir);
201             }
202             dir = next_dir;
203         }
204     }
205     else {
206         if(batch) {
207             datestamp_list = pick_all_datestamp(1);
208         }
209         else {
210             datestamp_list = pick_datestamp(1);
211         }
212     }
213
214     if(is_empty_sl(datestamp_list)) {
215         printf("Could not find any Amanda directories to flush.\n");
216         exit(1);
217     }
218
219     holding_list = get_flush(datestamp_list, NULL, 1, 0);
220     if(holding_list->first == NULL) {
221         printf("Could not find any valid dump image, check directory.\n");
222         exit(1);
223     }
224
225     if(!batch) confirm();
226
227     for(dp = diskqp->head; dp != NULL; dp = dp->next) {
228         if(dp->todo)
229             log_add(L_DISK, "%s %s", dp->host->hostname, dp->name);
230     }
231
232     if(!foreground) { /* write it before redirecting stdout */
233         puts("Running in background, you can log off now.");
234         puts("You'll get mail when amflush is finished.");
235     }
236
237     if(redirect) redirect_stderr();
238
239     if(!foreground) detach();
240
241     erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
242     set_logerror(logerror);
243     today = time(NULL);
244     strftime(date_string, 100, "%a %b %e %H:%M:%S %Z %Y", localtime (&today));
245     fprintf(stderr, "amflush: start at %s\n", date_string);
246     fprintf(stderr, "amflush: datestamp %s\n", datestamp);
247     log_add(L_START, "date %s", datestamp);
248
249     /* START DRIVER */
250     if(pipe(driver_pipe) == -1) {
251         error("error [opening pipe to driver: %s]", strerror(errno));
252     }
253     if((driver_pid = fork()) == 0) {
254         /*
255          * This is the child process.
256          */
257         dup2(driver_pipe[0], 0);
258         close(driver_pipe[1]);
259         execle(driver_program,
260                "driver", config_name, "nodump", (char *)0,
261                safe_env());
262         error("cannot exec %s: %s", driver_program, strerror(errno));
263     } else if(driver_pid == -1) {
264         error("cannot fork for %s: %s", driver_program, strerror(errno));
265     }
266     driver_stream = fdopen(driver_pipe[1], "w");
267
268     for(holding_file=holding_list->first; holding_file != NULL;
269                                    holding_file = holding_file->next) {
270         get_dumpfile(holding_file->name, &file);
271
272         dp = lookup_disk(file.name, file.disk);
273         if (dp->todo == 0) continue;
274
275         fprintf(stderr,
276                 "FLUSH %s %s %s %d %s\n",
277                 file.name,
278                 file.disk,
279                 file.datestamp,
280                 file.dumplevel,
281                 holding_file->name);
282         fprintf(driver_stream,
283                 "FLUSH %s %s %s %d %s\n",
284                 file.name,
285                 file.disk,
286                 file.datestamp,
287                 file.dumplevel,
288                 holding_file->name);
289     }
290     fprintf(stderr, "ENDFLUSH\n"); fflush(stderr);
291     fprintf(driver_stream, "ENDFLUSH\n"); fflush(driver_stream);
292     fclose(driver_stream);
293
294     /* WAIT DRIVER */
295     while(1) {
296         if((pid = wait(&exitcode)) == -1) {
297             if(errno == EINTR) {
298                 continue;
299             } else {
300                 error("wait for %s: %s", driver_program, strerror(errno));
301             }
302         } else if (pid == driver_pid) {
303             break;
304         }
305     }
306
307     free_sl(datestamp_list);
308     datestamp_list = NULL;
309     free_sl(holding_list);
310     holding_list = NULL;
311
312     if(redirect) { /* rename errfile */
313         char *errfile, *errfilex, *nerrfilex, number[100];
314         int tapecycle;
315         int maxdays, days;
316                 
317         struct stat stat_buf;
318
319         errfile = vstralloc(conf_logdir, "/amflush", NULL);
320         errfilex = NULL;
321         nerrfilex = NULL;
322         tapecycle = getconf_int(CNF_TAPECYCLE);
323         maxdays = tapecycle + 2;
324         days = 1;
325         /* First, find out the last existing errfile,           */
326         /* to avoid ``infinite'' loops if tapecycle is infinite */
327
328         ap_snprintf(number,100,"%d",days);
329         errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
330         while ( days < maxdays && stat(errfilex,&stat_buf)==0) {
331             days++;
332             ap_snprintf(number,100,"%d",days);
333             errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
334         }
335         ap_snprintf(number,100,"%d",days);
336         errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
337         nerrfilex = NULL;
338         while (days > 1) {
339             amfree(nerrfilex);
340             nerrfilex = errfilex;
341             days--;
342             ap_snprintf(number,100,"%d",days);
343             errfilex = vstralloc(errfile, ".", number, NULL);
344             if (rename(errfilex, nerrfilex) != 0) {
345                 error("cannot rename \"%s\" to \"%s\": %s",
346                       errfilex, nerrfilex, strerror(errno));
347             }
348         }
349         errfilex = newvstralloc(errfilex, errfile, ".1", NULL);
350         if (rename(errfile,errfilex) != 0) {
351             error("cannot rename \"%s\" to \"%s\": %s",
352                   errfilex, nerrfilex, strerror(errno));
353         }
354         amfree(errfile);
355         amfree(errfilex);
356         amfree(nerrfilex);
357     }
358
359     /*
360      * Have amreport generate report and send mail.  Note that we do
361      * not bother checking the exit status.  If it does not work, it
362      * can be rerun.
363      */
364
365     if((reporter_pid = fork()) == 0) {
366         /*
367          * This is the child process.
368          */
369         execle(reporter_program,
370                "amreport", config_name, (char *)0,
371                safe_env());
372         error("cannot exec %s: %s", reporter_program, strerror(errno));
373     } else if(reporter_pid == -1) {
374         error("cannot fork for %s: %s", reporter_program, strerror(errno));
375     }
376     while(1) {
377         if((pid = wait(&exitcode)) == -1) {
378             if(errno == EINTR) {
379                 continue;
380             } else {
381                 error("wait for %s: %s", reporter_program, strerror(errno));
382             }
383         } else if (pid == reporter_pid) {
384             break;
385         }
386     }
387
388     /*
389      * Call amlogroll to rename the log file to its datestamped version.
390      * Since we exec at this point, our exit code will be that of amlogroll.
391      */
392
393     execle(logroll_program,
394            "amlogroll", config_name, (char *)0,
395            safe_env());
396     error("cannot exec %s: %s", logroll_program, strerror(errno));
397     return 0;                           /* keep the compiler happy */
398 }
399
400
401 int get_letter_from_user()
402 {
403     int r = '\0';
404     int ch;
405
406     fflush(stdout); fflush(stderr);
407     while((ch = getchar()) != EOF && ch != '\n' && isspace(ch)) {}
408     if(ch == '\n') {
409         r = '\0';
410     } else if (ch != EOF) {
411         r = ch;
412         if(islower(r)) r = toupper(r);
413         while((ch = getchar()) != EOF && ch != '\n') {}
414     } else {
415         r = ch;
416         clearerr(stdin);
417     }
418     return r;
419 }
420
421
422 void confirm()
423 /* confirm before detaching and running */
424 {
425     tape_t *tp;
426     char *tpchanger;
427     sle_t *dir;
428     int ch;
429     char *extra;
430
431     printf("\nToday is: %s\n",datestamp);
432     printf("Flushing dumps in");
433     extra = "";
434     for(dir = datestamp_list->first; dir != NULL; dir = dir->next) {
435         printf("%s %s", extra, dir->name);
436         extra = ",";
437     }
438     tpchanger = getconf_str(CNF_TPCHANGER);
439     if(*tpchanger != '\0') {
440         printf(" using tape changer \"%s\".\n", tpchanger);
441     } else {
442         printf(" to tape drive \"%s\".\n", getconf_str(CNF_TAPEDEV));
443     }
444
445     printf("Expecting ");
446     tp = lookup_last_reusable_tape(0);
447     if(tp != NULL) printf("tape %s or ", tp->label);
448     printf("a new tape.");
449     tp = lookup_tapepos(1);
450     if(tp != NULL) printf("  (The last dumps were to tape %s)", tp->label);
451
452     while (1) {
453         printf("\nAre you sure you want to do this [yN]? ");
454         if((ch = get_letter_from_user()) == 'Y') {
455             return;
456         } else if (ch == 'N' || ch == '\0' || ch == EOF) {
457             if (ch == EOF) {
458                 putchar('\n');
459             }
460             break;
461         }
462     }
463
464     printf("Ok, quitting.  Run amflush again when you are ready.\n");
465     exit(1);
466 }
467
468 void redirect_stderr()
469 {
470     int fderr;
471     char *errfile;
472
473     fflush(stdout); fflush(stderr);
474     errfile = vstralloc(conf_logdir, "/amflush", NULL);
475     if((fderr = open(errfile, O_WRONLY| O_CREAT | O_TRUNC, 0600)) == -1)
476         error("could not open %s: %s", errfile, strerror(errno));
477     dup2(fderr,1);
478     dup2(fderr,2);
479     aclose(fderr);
480     amfree(errfile);
481 }
482
483 void detach()
484 {
485     int fd;
486
487     fflush(stdout); fflush(stderr);
488     if((fd = open("/dev/null", O_RDWR, 0666)) == -1)
489         error("could not open /dev/null: %s", strerror(errno));
490
491     dup2(fd,0);
492     aclose(fd);
493
494     switch(fork()) {
495     case -1: error("could not fork: %s", strerror(errno));
496     case 0:
497         setsid();
498         return;
499     }
500
501     exit(0);
502 }
503
504