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