Imported Upstream version 2.5.2p1
[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 2006/07/25 21:41:24 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     char *qdisk, *qhname;
98
99     safe_fd(-1, 0);
100     safe_cd();
101
102     set_pname("amflush");
103
104     dbopen(DBG_SUBDIR_SERVER);
105
106     /* Don't die when child closes pipe */
107     signal(SIGPIPE, SIG_IGN);
108
109     erroutput_type = ERR_INTERACTIVE;
110     foreground = 0;
111     batch = 0;
112     redirect = 1;
113
114     /* process arguments */
115
116     parse_conf(main_argc, main_argv, &new_argc, &new_argv);
117     my_argc = new_argc;
118     my_argv = new_argv;
119
120     while((opt = getopt(my_argc, my_argv, "bfsD:")) != EOF) {
121         switch(opt) {
122         case 'b': batch = 1;
123                   break;
124         case 'f': foreground = 1;
125                   break;
126         case 's': redirect = 0;
127                   break;
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");
132                       exit(1);
133                   }
134                   datearg[nb_datearg++] = stralloc(optarg);
135                   datearg[nb_datearg] = NULL;
136                   break;
137         }
138     }
139     if(!foreground && !redirect) {
140         fprintf(stderr,"Can't redirect to stdout/stderr if not in forground.\n");
141         exit(1);
142     }
143
144     my_argc -= optind, my_argv += optind;
145
146     if(my_argc < 1) {
147         error("Usage: amflush%s [-b] [-f] [-s] [-D date]* <confdir> [host [disk]* ]* [-o configoption]*", versionsuffix());
148         /*NOTREACHED*/
149     }
150
151     config_name = my_argv[0];
152     config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
153
154     conffile = stralloc2(config_dir, CONFFILE_NAME);
155     if(read_conffile(conffile)) {
156         error("errors processing config file \"%s\"", conffile);
157         /*NOTREACHED*/
158     }
159     amfree(conffile);
160
161     dbrename(config_name, DBG_SUBDIR_SERVER);
162
163     report_bad_conf_arg();
164
165     conf_diskfile = getconf_str(CNF_DISKFILE);
166     if (*conf_diskfile == '/') {
167         conf_diskfile = stralloc(conf_diskfile);
168     } else {
169         conf_diskfile = stralloc2(config_dir, conf_diskfile);
170     }
171     if (read_diskfile(conf_diskfile, &diskq) < 0) {
172         error("could not read disklist file \"%s\"", conf_diskfile);
173         /*NOTREACHED*/
174     }
175     errstr = match_disklist(&diskq, my_argc-1, my_argv+1);
176     if (errstr) {
177         printf("%s",errstr);
178         amfree(errstr);
179     }
180     amfree(conf_diskfile);
181
182     conf_tapelist = getconf_str(CNF_TAPELIST);
183     if (*conf_tapelist == '/') {
184         conf_tapelist = stralloc(conf_tapelist);
185     } else {
186         conf_tapelist = stralloc2(config_dir, conf_tapelist);
187     }
188     if(read_tapelist(conf_tapelist)) {
189         error("could not load tapelist \"%s\"", conf_tapelist);
190         /*NOTREACHED*/
191     }
192     amfree(conf_tapelist);
193
194     conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
195
196     amflush_datestamp = construct_datestamp(NULL);
197     if(conf_usetimestamps == 0) {
198         amflush_timestamp = stralloc(amflush_datestamp);
199     }
200     else {
201         amflush_timestamp = construct_timestamp(NULL);
202     }
203
204     dumpuser = getconf_str(CNF_DUMPUSER);
205     if((pw = getpwnam(dumpuser)) == NULL) {
206         error("dumpuser %s not found in password file", dumpuser);
207         /*NOTREACHED*/
208     }
209     if(pw->pw_uid != getuid()) {
210         error("must run amflush as user %s", dumpuser);
211         /*NOTREACHED*/
212     }
213
214     conf_logdir = getconf_str(CNF_LOGDIR);
215     if (*conf_logdir == '/') {
216         conf_logdir = stralloc(conf_logdir);
217     } else {
218         conf_logdir = stralloc2(config_dir, conf_logdir);
219     }
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);
223         /*NOTREACHED*/
224     }
225     amfree(conf_logfile);
226
227     driver_program = vstralloc(libexecdir, "/", "driver", versionsuffix(),
228                                NULL);
229     reporter_program = vstralloc(sbindir, "/", "amreport", versionsuffix(),
230                                  NULL);
231     logroll_program = vstralloc(libexecdir, "/", "amlogroll", versionsuffix(),
232                                 NULL);
233
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");
238     }
239
240     if(datearg) {
241         sle_t *dir, *next_dir;
242         int i, ok;
243
244         datestamp_list = pick_all_datestamp(1);
245         for(dir = datestamp_list->first; dir != NULL;) {
246             next_dir = dir->next;
247             ok = 0;
248             for(i=0; i<nb_datearg && ok==0; i++) {
249                 ok = match_datestamp(datearg[i], dir->name);
250             }
251             if(ok == 0) { /* remove dir */
252                 remove_sl(datestamp_list, dir);
253             }
254             dir = next_dir;
255         }
256     }
257     else {
258         if(batch) {
259             datestamp_list = pick_all_datestamp(1);
260         }
261         else {
262             datestamp_list = pick_datestamp(1);
263         }
264     }
265
266     if(is_empty_sl(datestamp_list)) {
267         printf("Could not find any Amanda directories to flush.\n");
268         exit(1);
269     }
270
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");
274         exit(1);
275     }
276
277     if(!batch) confirm();
278
279     for(dp = diskq.head; dp != NULL; dp = dp->next) {
280         if(dp->todo) {
281             char *qname;
282             qname = quote_string(dp->name);
283             log_add(L_DISK, "%s %s", dp->host->hostname, qname);
284             amfree(qname);
285         }
286     }
287
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.");
291     }
292
293     if(redirect) redirect_stderr();
294
295     if(!foreground) detach();
296
297     erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
298     set_logerror(logerror);
299     today = time(NULL);
300     tm = localtime(&today);
301     if (tm)
302         strftime(date_string, 100, "%a %b %e %H:%M:%S %Z %Y", tm);
303     else
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);
309
310     /* START DRIVER */
311     if(pipe(driver_pipe) == -1) {
312         error("error [opening pipe to driver: %s]", strerror(errno));
313         /*NOTREACHED*/
314     }
315     if((driver_pid = fork()) == 0) {
316         /*
317          * This is the child process.
318          */
319         dup2(driver_pipe[0], 0);
320         close(driver_pipe[1]);
321         execle(driver_program,
322                "driver", config_name, "nodump", (char *)0,
323                safe_env());
324         error("cannot exec %s: %s", driver_program, strerror(errno));
325         /*NOTREACHED*/
326     } else if(driver_pid == -1) {
327         error("cannot fork for %s: %s", driver_program, strerror(errno));
328         /*NOTREACHED*/
329     }
330     driver_stream = fdopen(driver_pipe[1], "w");
331     if (!driver_stream) {
332         error("Can't fdopen: %s", strerror(errno));
333         /*NOTREACHED*/
334     }
335
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);
340
341         if (holding_file_size(holding_file->name, 1) <= 0) {
342             log_add(L_INFO, "%s: removing file with no data.",
343                     holding_file->name);
344             holding_file_unlink(holding_file->name);
345             continue;
346         }
347
348         dp = lookup_disk(file.name, file.disk);
349         if (!dp) {
350             error("dp == NULL");
351             /*NOTREACHED*/
352         }
353         if (dp->todo == 0) continue;
354
355         qdisk = quote_string(file.disk);
356         qhname = quote_string(holding_file->name);
357         fprintf(stderr,
358                 "FLUSH %s %s %s %d %s\n",
359                 file.name,
360                 qdisk,
361                 file.datestamp,
362                 file.dumplevel,
363                 qhname);
364         fprintf(driver_stream,
365                 "FLUSH %s %s %s %d %s\n",
366                 file.name,
367                 qdisk,
368                 file.datestamp,
369                 file.dumplevel,
370                 qhname);
371         amfree(qdisk);
372         amfree(qhname);
373     }
374     fprintf(stderr, "ENDFLUSH\n"); fflush(stderr);
375     fprintf(driver_stream, "ENDFLUSH\n"); fflush(driver_stream);
376     fclose(driver_stream);
377
378     /* WAIT DRIVER */
379     while(1) {
380         if((pid = wait(&exitcode)) == -1) {
381             if(errno == EINTR) {
382                 continue;
383             } else {
384                 error("wait for %s: %s", driver_program, strerror(errno));
385                 /*NOTREACHED*/
386             }
387         } else if (pid == driver_pid) {
388             break;
389         }
390     }
391
392     free_sl(datestamp_list);
393     datestamp_list = NULL;
394     free_sl(holding_list);
395     holding_list = NULL;
396
397     if(redirect) { /* rename errfile */
398         char *errfile, *errfilex, *nerrfilex, number[100];
399         int tapecycle;
400         int maxdays, days;
401                 
402         struct stat stat_buf;
403
404         errfile = vstralloc(conf_logdir, "/amflush", NULL);
405         errfilex = NULL;
406         nerrfilex = NULL;
407         tapecycle = getconf_int(CNF_TAPECYCLE);
408         maxdays = tapecycle + 2;
409         days = 1;
410         /* First, find out the last existing errfile,           */
411         /* to avoid ``infinite'' loops if tapecycle is infinite */
412
413         snprintf(number,100,"%d",days);
414         errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
415         while ( days < maxdays && stat(errfilex,&stat_buf)==0) {
416             days++;
417             snprintf(number,100,"%d",days);
418             errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
419         }
420         snprintf(number,100,"%d",days);
421         errfilex = newvstralloc(errfilex, errfile, ".", number, NULL);
422         nerrfilex = NULL;
423         while (days > 1) {
424             amfree(nerrfilex);
425             nerrfilex = errfilex;
426             days--;
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));
432                 /*NOTREACHED*/
433             }
434         }
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));
439             /*NOTREACHED*/
440         }
441         amfree(errfile);
442         amfree(errfilex);
443         amfree(nerrfilex);
444     }
445
446     /*
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
449      * can be rerun.
450      */
451
452     if((reporter_pid = fork()) == 0) {
453         /*
454          * This is the child process.
455          */
456         execle(reporter_program,
457                "amreport", config_name, (char *)0,
458                safe_env());
459         error("cannot exec %s: %s", reporter_program, strerror(errno));
460         /*NOTREACHED*/
461     } else if(reporter_pid == -1) {
462         error("cannot fork for %s: %s", reporter_program, strerror(errno));
463         /*NOTREACHED*/
464     }
465     while(1) {
466         if((pid = wait(&exitcode)) == -1) {
467             if(errno == EINTR) {
468                 continue;
469             } else {
470                 error("wait for %s: %s", reporter_program, strerror(errno));
471                 /*NOTREACHED*/
472             }
473         } else if (pid == reporter_pid) {
474             break;
475         }
476     }
477
478     /*
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.
481      */
482     execle(logroll_program,
483            "amlogroll", config_name, (char *)0,
484            safe_env());
485     error("cannot exec %s: %s", logroll_program, strerror(errno));
486     /*NOTREACHED*/
487     return 0;                           /* keep the compiler happy */
488 }
489
490
491 static int
492 get_letter_from_user(void)
493 {
494     int r, ch;
495
496     fflush(stdout); fflush(stderr);
497     while((ch = getchar()) != EOF && ch != '\n' && isspace(ch)) {
498         (void)ch; /* Quite lint */
499     }
500     if(ch == '\n') {
501         r = '\0';
502     } else if (ch != EOF) {
503         r = ch;
504         if(islower(r)) r = toupper(r);
505         while((ch = getchar()) != EOF && ch != '\n') { 
506             (void)ch; /* Quite lint */
507         }
508     } else {
509         r = ch;
510         clearerr(stdin);
511     }
512     return r;
513 }
514
515
516 /*
517  * confirm before detaching and running
518  */
519
520 void
521 confirm(void)
522 {
523     tape_t *tp;
524     char *tpchanger;
525     sle_t *dir;
526     int ch;
527     char *extra;
528
529     printf("\nToday is: %s\n",amflush_datestamp);
530     printf("Flushing dumps in");
531     extra = "";
532     for(dir = datestamp_list->first; dir != NULL; dir = dir->next) {
533         printf("%s %s", extra, dir->name);
534         extra = ",";
535     }
536     tpchanger = getconf_str(CNF_TPCHANGER);
537     if(*tpchanger != '\0') {
538         printf(" using tape changer \"%s\".\n", tpchanger);
539     } else {
540         printf(" to tape drive \"%s\".\n", getconf_str(CNF_TAPEDEV));
541     }
542
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);
549
550     while (1) {
551         printf("\nAre you sure you want to do this [yN]? ");
552         if((ch = get_letter_from_user()) == 'Y') {
553             return;
554         } else if (ch == 'N' || ch == '\0' || ch == EOF) {
555             if (ch == EOF) {
556                 putchar('\n');
557             }
558             break;
559         }
560     }
561
562     printf("Ok, quitting.  Run amflush again when you are ready.\n");
563     exit(1);
564 }
565
566 void
567 redirect_stderr(void)
568 {
569     int fderr;
570     char *errfile;
571
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));
576         /*NOTREACHED*/
577     }
578     dup2(fderr,1);
579     dup2(fderr,2);
580     aclose(fderr);
581     amfree(errfile);
582 }
583
584 void
585 detach(void)
586 {
587     int fd;
588
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));
592         /*NOTREACHED*/
593     }
594
595     dup2(fd,0);
596     aclose(fd);
597
598     switch(fork()) {
599     case -1:
600         error("could not fork: %s", strerror(errno));
601         /*NOTREACHED*/
602
603     case 0:
604         setsid();
605         return;
606     }
607
608     exit(0);
609 }