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