Imported Upstream version 2.5.2p1
[debian/amanda] / server-src / amcheck.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-2000 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: amcheck.c,v 1.149 2006/08/24 01:57:16 paddy_s Exp $
28  *
29  * checks for common problems in server and clients
30  */
31 #include "amanda.h"
32 #include "util.h"
33 #include "conffile.h"
34 #include "statfs.h"
35 #include "diskfile.h"
36 #include "tapefile.h"
37 #include "tapeio.h"
38 #include "changer.h"
39 #include "packet.h"
40 #include "security.h"
41 #include "protocol.h"
42 #include "clock.h"
43 #include "version.h"
44 #include "amindex.h"
45 #include "token.h"
46 #include "taperscan.h"
47 #include "server_util.h"
48 #include "pipespawn.h"
49 #include "amfeatures.h"
50
51 #define BUFFER_SIZE     32768
52
53 static time_t conf_ctimeout;
54 static int overwrite;
55
56 static disklist_t origq;
57
58 static uid_t uid_dumpuser;
59
60 /* local functions */
61
62 void usage(void);
63 pid_t start_client_checks(int fd);
64 pid_t start_server_check(int fd, int do_localchk, int do_tapechk);
65 int main(int argc, char **argv);
66 int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
67
68 void
69 usage(void)
70 {
71     error("Usage: amcheck%s [-am] [-w] [-sclt] [-M <address>] <conf> [host [disk]* ]* [-o configoption]*", versionsuffix());
72     /*NOTREACHED*/
73 }
74
75 static unsigned long malloc_hist_1, malloc_size_1;
76 static unsigned long malloc_hist_2, malloc_size_2;
77
78 static am_feature_t *our_features = NULL;
79 static char *our_feature_string = NULL;
80 static char *displayunit;
81 static long int unitdivisor;
82
83 int
84 main(
85     int         argc,
86     char **     argv)
87 {
88     char buffer[BUFFER_SIZE];
89     char *version_string;
90     char *mainfname = NULL;
91     char pid_str[NUM_STR_SIZE];
92     int do_clientchk, client_probs;
93     int do_localchk, do_tapechk, server_probs;
94     pid_t clientchk_pid, serverchk_pid;
95     int opt, tempfd, mainfd;
96     ssize_t size;
97     amwait_t retstat;
98     pid_t pid;
99     extern int optind;
100     char *mailto = NULL;
101     extern char *optarg;
102     int mailout;
103     int alwaysmail;
104     char *tempfname = NULL;
105     char *conffile;
106     char *conf_diskfile;
107     char *dumpuser;
108     struct passwd *pw;
109     uid_t uid_me;
110     int    new_argc,   my_argc;
111     char **new_argv, **my_argv;
112     char *errstr;
113
114     safe_fd(-1, 0);
115     safe_cd();
116
117     set_pname("amcheck");
118
119     /* Don't die when child closes pipe */
120     signal(SIGPIPE, SIG_IGN);
121
122     dbopen(DBG_SUBDIR_SERVER);
123
124     memset(buffer, 0, sizeof(buffer));
125     malloc_size_1 = malloc_inuse(&malloc_hist_1);
126
127     snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid());
128
129     erroutput_type = ERR_INTERACTIVE;
130
131     our_features = am_init_feature_set();
132     our_feature_string = am_feature_to_string(our_features);
133
134     if(geteuid() == 0) {
135         seteuid(getuid());
136     }
137     uid_me = getuid();
138
139     alwaysmail = mailout = overwrite = 0;
140     do_localchk = do_tapechk = do_clientchk = 0;
141     server_probs = client_probs = 0;
142     tempfd = mainfd = -1;
143
144     parse_conf(argc, argv, &new_argc, &new_argv);
145     my_argc = new_argc;
146     my_argv = new_argv;
147
148     /* process arguments */
149
150     while((opt = getopt(my_argc, my_argv, "M:mawsclt")) != EOF) {
151         switch(opt) {
152         case 'M':       mailto=stralloc(optarg);
153                         if(!validate_mailto(mailto)){
154                            printf("Invalid characters in mail address\n");
155                            exit(1);
156                         }
157                         /*FALLTHROUGH*/
158         case 'm':       
159 #ifdef MAILER
160                         mailout = 1;
161 #else
162                         printf("You can't use -%c because configure didn't find a mailer.\n",
163                                 opt);
164                         exit(1);
165 #endif
166                         break;
167         case 'a':       
168 #ifdef MAILER
169                         mailout = 1;
170                         alwaysmail = 1;
171 #else
172                         printf("You can't use -%c because configure didn't find a mailer.\n",
173                                 opt);
174                         exit(1);
175 #endif
176                         break;
177         case 's':       do_localchk = do_tapechk = 1;
178                         break;
179         case 'c':       do_clientchk = 1;
180                         break;
181         case 'l':       do_localchk = 1;
182                         break;
183         case 'w':       overwrite = 1;
184                         break;
185         case 't':       do_tapechk = 1;
186                         break;
187         case '?':
188         default:
189             usage();
190         }
191     }
192     my_argc -= optind, my_argv += optind;
193     if(my_argc < 1) usage();
194
195
196     if ((do_localchk | do_clientchk | do_tapechk) == 0) {
197         /* Check everything if individual checks were not asked for */
198         do_localchk = do_clientchk = do_tapechk = 1;
199     }
200
201     if(overwrite)
202         do_tapechk = 1;
203
204     config_name = stralloc(*my_argv);
205
206     config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
207     conffile = stralloc2(config_dir, CONFFILE_NAME);
208     if(read_conffile(conffile)) {
209         error("errors processing config file \"%s\"", conffile);
210         /*NOTREACHED*/
211     }
212
213     dbrename(config_name, DBG_SUBDIR_SERVER);
214
215     report_bad_conf_arg();
216
217     amfree(conffile);
218     if(mailout && !mailto && 
219        (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
220         printf("\nNo mail address configured in  amanda.conf\n");
221         if(alwaysmail)        
222                 printf("When using -a option please specify -Maddress also\n\n"); 
223         else
224                 printf("Use -Maddress instead of -m\n\n"); 
225         exit(1);
226     }
227     if(mailout && !mailto)
228     { 
229        if(getconf_seen(CNF_MAILTO) && 
230           strlen(getconf_str(CNF_MAILTO)) > 0) {
231           if(!validate_mailto(getconf_str(CNF_MAILTO))){
232                 printf("\nMail address in amanda.conf has invalid characters"); 
233                 printf("\nNo email will be sent\n"); 
234                 mailout = 0;
235           }
236        }
237        else {
238           printf("\nNo mail address configured in  amanda.conf\n");
239           if(alwaysmail)        
240                 printf("When using -a option please specify -Maddress also\n\n"); 
241           else
242                 printf("Use -Maddress instead of -m\n\n"); 
243           exit(1);
244       }
245     }
246
247     conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
248
249     conf_diskfile = getconf_str(CNF_DISKFILE);
250     if (*conf_diskfile == '/') {
251         conf_diskfile = stralloc(conf_diskfile);
252     } else {
253         conf_diskfile = stralloc2(config_dir, conf_diskfile);
254     }
255     if(read_diskfile(conf_diskfile, &origq) < 0) {
256         error("could not load disklist %s", conf_diskfile);
257         /*NOTREACHED*/
258     }
259     errstr = match_disklist(&origq, my_argc-1, my_argv+1);
260     if (errstr) {
261         printf("%s",errstr);
262         amfree(errstr);
263     }
264     amfree(conf_diskfile);
265
266     /*
267      * Make sure we are running as the dump user.
268      */
269     dumpuser = getconf_str(CNF_DUMPUSER);
270     if ((pw = getpwnam(dumpuser)) == NULL) {
271         error("cannot look up dump user \"%s\"", dumpuser);
272         /*NOTREACHED*/
273     }
274     uid_dumpuser = pw->pw_uid;
275     if ((pw = getpwuid(uid_me)) == NULL) {
276         error("cannot look up my own uid (%ld)", (long)uid_me);
277         /*NOTREACHED*/
278     }
279     if (uid_me != uid_dumpuser) {
280         error("running as user \"%s\" instead of \"%s\"",
281               pw->pw_name, dumpuser);
282         /*NOTREACHED*/
283     }
284
285     displayunit = getconf_str(CNF_DISPLAYUNIT);
286     unitdivisor = getconf_unit_divisor();
287
288     /*
289      * If both server and client side checks are being done, the server
290      * check output goes to the main output, while the client check output
291      * goes to a temporary file and is copied to the main output when done.
292      *
293      * If the output is to be mailed, the main output is also a disk file,
294      * otherwise it is stdout.
295      */
296     if(do_clientchk && (do_localchk || do_tapechk)) {
297         /* we need the temp file */
298         tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
299         if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
300             error("could not open %s: %s", tempfname, strerror(errno));
301             /*NOTREACHED*/
302         }
303         unlink(tempfname);                      /* so it goes away on close */
304         amfree(tempfname);
305     }
306
307     if(mailout) {
308         /* the main fd is a file too */
309         mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
310         if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
311             error("could not open %s: %s", mainfname, strerror(errno));
312             /*NOTREACHED*/
313         }
314         unlink(mainfname);                      /* so it goes away on close */
315         amfree(mainfname);
316     }
317     else
318         /* just use stdout */
319         mainfd = 1;
320
321     /* start server side checks */
322
323     if(do_localchk || do_tapechk)
324         serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
325     else
326         serverchk_pid = 0;
327
328     /* start client side checks */
329
330     if(do_clientchk) {
331         clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
332     } else {
333         clientchk_pid = 0;
334     }
335
336     /* wait for child processes and note any problems */
337
338     while(1) {
339         if((pid = wait(&retstat)) == -1) {
340             if(errno == EINTR) continue;
341             else break;
342         } else if(pid == clientchk_pid) {
343             client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
344             clientchk_pid = 0;
345         } else if(pid == serverchk_pid) {
346             server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
347             serverchk_pid = 0;
348         } else {
349             char number[NUM_STR_SIZE];
350             char *wait_msg = NULL;
351
352             snprintf(number, SIZEOF(number), "%ld", (long)pid);
353             wait_msg = vstralloc("parent: reaped bogus pid ", number, "\n",
354                                  NULL);
355             if (fullwrite(mainfd, wait_msg, strlen(wait_msg)) < 0) {
356                 error("write main file: %s", strerror(errno));
357                 /*NOTREACHED*/
358             }
359             amfree(wait_msg);
360         }
361     }
362
363     /* copy temp output to main output and write tagline */
364
365     if(do_clientchk && (do_localchk || do_tapechk)) {
366         if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) {
367             error("seek temp file: %s", strerror(errno));
368             /*NOTREACHED*/
369         }
370
371         while((size = fullread(tempfd, buffer, SIZEOF(buffer))) > 0) {
372             if (fullwrite(mainfd, buffer, (size_t)size) < 0) {
373                 error("write main file: %s", strerror(errno));
374                 /*NOTREACHED*/
375             }
376         }
377         if(size < 0) {
378             error("read temp file: %s", strerror(errno));
379             /*NOTREACHED*/
380         }
381         aclose(tempfd);
382     }
383
384     version_string = vstralloc("\n",
385                                "(brought to you by Amanda ", version(), ")\n",
386                                NULL);
387     if (fullwrite(mainfd, version_string, strlen(version_string)) < 0) {
388         error("write main file: %s", strerror(errno));
389         /*NOTREACHED*/
390     }
391     amfree(version_string);
392     amfree(config_dir);
393     amfree(config_name);
394     amfree(our_feature_string);
395     am_release_feature_set(our_features);
396     our_features = NULL;
397
398     malloc_size_2 = malloc_inuse(&malloc_hist_2);
399
400     if(malloc_size_1 != malloc_size_2) {
401         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
402     }
403
404     /* send mail if requested, but only if there were problems */
405 #ifdef MAILER
406
407 #define MAILTO_LIMIT    10
408
409     if((server_probs || client_probs || alwaysmail) && mailout) {
410         int mailfd;
411         int nullfd;
412         int errfd;
413         FILE *ferr;
414         char *subject;
415         char **a;
416         amwait_t retstat;
417         ssize_t r;
418         ssize_t w;
419         char *err = NULL;
420         char *extra_info = NULL;
421         char *line = NULL;
422         int ret;
423         int rc;
424         int sig;
425         char number[NUM_STR_SIZE];
426
427         fflush(stdout);
428         if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
429             error("lseek main file: %s", strerror(errno));
430             /*NOTREACHED*/
431         }
432         if(alwaysmail && !(server_probs || client_probs)) {
433             subject = stralloc2(getconf_str(CNF_ORG),
434                             " AMCHECK REPORT: NO PROBLEMS FOUND");
435         } else {
436             subject = stralloc2(getconf_str(CNF_ORG),
437                             " AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE");
438         }
439         /*
440          * Variable arg lists are hard to deal with when we do not know
441          * ourself how many args are involved.  Split the address list
442          * and hope there are not more than 9 entries.
443          *
444          * Remember that split() returns the original input string in
445          * argv[0], so we have to skip over that.
446          */
447         a = (char **) alloc((MAILTO_LIMIT + 1) * SIZEOF(char *));
448         memset(a, 0, (MAILTO_LIMIT + 1) * SIZEOF(char *));
449         if(mailto) {
450             a[1] = mailto;
451             a[2] = NULL;
452         } else {
453             r = (ssize_t)split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " ");
454             a[r + 1] = NULL;
455         }
456         if((nullfd = open("/dev/null", O_RDWR)) < 0) {
457             error("nullfd: /dev/null: %s", strerror(errno));
458             /*NOTREACHED*/
459         }
460         pipespawn(MAILER, STDIN_PIPE | STDERR_PIPE,
461                             &mailfd, &nullfd, &errfd,
462                             MAILER,
463                             "-s", subject,
464                                   a[1], a[2], a[3], a[4],
465                             a[5], a[6], a[7], a[8], a[9],
466                             NULL);
467         amfree(subject);
468         /*
469          * There is the potential for a deadlock here since we are writing
470          * to the process and then reading stderr, but in the normal case,
471          * nothing should be coming back to us, and hopefully in error
472          * cases, the pipe will break and we will exit out of the loop.
473          */
474         signal(SIGPIPE, SIG_IGN);
475         while((r = fullread(mainfd, buffer, SIZEOF(buffer))) > 0) {
476             if((w = fullwrite(mailfd, buffer, (size_t)r)) != (ssize_t)r) {
477                 if(w < 0 && errno == EPIPE) {
478                     strappend(extra_info, "EPIPE writing to mail process\n");
479                     break;
480                 } else if(w < 0) {
481                     error("mailfd write: %s", strerror(errno));
482                     /*NOTREACHED*/
483                 } else {
484                     error("mailfd write: wrote " SSIZE_T_FMT " instead of " SIZE_T_FMT, w, r);
485                     /*NOTREACHED*/
486                 }
487             }
488         }
489         aclose(mailfd);
490         ferr = fdopen(errfd, "r");
491         if (!ferr) {
492             error("Can't fdopen: %s", strerror(errno));
493             /*NOTREACHED*/
494         }
495         for(; (line = agets(ferr)) != NULL; free(line)) {
496             if (line[0] == '\0')
497                 continue;
498             strappend(extra_info, line);
499             strappend(extra_info, "\n");
500         }
501         afclose(ferr);
502         errfd = -1;
503         rc = 0;
504         while (wait(&retstat) != -1) {
505             if (WIFSIGNALED(retstat)) {
506                     ret = 0;
507                     rc = sig = WTERMSIG(retstat);
508             } else {
509                     sig = 0;
510                     rc = ret = WEXITSTATUS(retstat);
511             }
512             if (rc != 0) {
513                     if (ret == 0) {
514                         strappend(err, "got signal ");
515                         ret = sig;
516                     } else {
517                         strappend(err, "returned ");
518                     }
519                     snprintf(number, SIZEOF(number), "%d", ret);
520                     strappend(err, number);
521             }
522         }
523         if (rc != 0) {
524             if(extra_info) {
525                 fputs(extra_info, stderr);
526                 amfree(extra_info);
527             }
528             error("error running mailer %s: %s", MAILER, err);
529             /*NOTREACHED*/
530         }
531     }
532 #endif
533     free_new_argv(new_argc, new_argv);
534     free_server_config();
535
536     dbclose();
537     return (server_probs || client_probs);
538 }
539
540 /* --------------------------------------------------- */
541
542 int nslots, backwards, found, got_match, tapedays;
543 char *datestamp;
544 char *first_match_label = NULL, *first_match = NULL, *found_device = NULL;
545 char *label;
546 char *searchlabel, *labelstr;
547 tape_t *tp;
548 FILE *errf = NULL;
549
550 int
551 test_server_pgm(
552     FILE *      outf,
553     char *      dir,
554     char *      pgm,
555     int         suid,
556     uid_t       dumpuid)
557 {
558     struct stat statbuf;
559     int pgmbad = 0;
560     char *quoted;
561
562     pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL);
563     quoted = quote_string(pgm);
564     if(stat(pgm, &statbuf) == -1) {
565         fprintf(outf, "ERROR: program %s: does not exist\n",
566                 quoted);
567         pgmbad = 1;
568     } else if (!S_ISREG(statbuf.st_mode)) {
569         fprintf(outf, "ERROR: program %s: not a file\n",
570                 quoted);
571         pgmbad = 1;
572     } else if (access(pgm, X_OK) == -1) {
573         fprintf(outf, "ERROR: program %s: not executable\n",
574                 quoted);
575         pgmbad = 1;
576     } else if (suid \
577                && dumpuid != 0
578                && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
579         fprintf(outf, "ERROR: program %s: not setuid-root\n",
580                 quoted);
581         pgmbad = 1;
582     }
583     amfree(quoted);
584     amfree(pgm);
585     return pgmbad;
586 }
587
588 pid_t
589 start_server_check(
590     int         fd,
591     int         do_localchk,
592     int         do_tapechk)
593 {
594     char *tapename;
595     generic_fs_stats_t fs;
596     FILE *outf = NULL;
597     holdingdisk_t *hdp;
598     pid_t pid;
599     int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
600     int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
601     int testtape = do_tapechk;
602     tapetype_t *tp = NULL;
603     char *quoted;
604     int res;
605     struct addrinfo *gaires;
606     struct addrinfo hints;
607
608     switch(pid = fork()) {
609     case -1:
610         error("could not fork server check: %s", strerror(errno));
611         /*NOTREACHED*/
612
613     case 0:
614         break;
615
616     default:
617         return pid;
618     }
619
620     dup2(fd, 1);
621     dup2(fd, 2);
622
623     set_pname("amcheck-server");
624
625     startclock();
626
627     if((outf = fdopen(fd, "w")) == NULL) {
628         error("fdopen %d: %s", fd, strerror(errno));
629         /*NOTREACHED*/
630     }
631     errf = outf;
632
633     fprintf(outf, "Amanda Tape Server Host Check\n");
634     fprintf(outf, "-----------------------------\n");
635
636     if (do_localchk || testtape) {
637         tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
638     }
639
640     /*
641      * Check various server side config file settings.
642      */
643     if(do_localchk) {
644         char *ColumnSpec;
645         char *errstr = NULL;
646         char *lbl_templ;
647
648         ColumnSpec = getconf_str(CNF_COLUMNSPEC);
649         if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
650             fprintf(outf, "ERROR: %s\n", errstr);
651             amfree(errstr);
652             confbad = 1;
653         }
654         lbl_templ = tapetype_get_lbl_templ(tp);
655         if(strcmp(lbl_templ, "") != 0) {
656             if(strchr(lbl_templ, '/') == NULL) {
657                 lbl_templ = stralloc2(config_dir, lbl_templ);
658             } else {
659                 lbl_templ = stralloc(lbl_templ);
660             }
661             if(access(lbl_templ, R_OK) == -1) {
662                 fprintf(outf,
663                         "ERROR: cannot access lbl_templ file %s: %s\n",
664                         lbl_templ,
665                         strerror(errno));
666                 confbad = 1;
667             }
668 #if !defined(LPRCMD)
669             fprintf(outf, "ERROR: lbl_templ set but no LPRCMD defined, you should reconfigure amanda\n       and make sure it find a lpr or lp command.\n");
670             confbad = 1;
671 #endif
672         }
673
674         /* Double-check that 'localhost' resolves properly */
675 #ifdef WORKING_IPV6
676         hints.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ALL;
677         hints.ai_family = AF_UNSPEC;
678 #else
679         hints.ai_flags = AI_CANONNAME;
680         hints.ai_family = AF_INET;
681 #endif
682         hints.ai_socktype = 0;
683         hints.ai_protocol = 0;
684         hints.ai_addrlen = 0;
685         hints.ai_addr = NULL;
686         hints.ai_canonname = NULL;
687         hints.ai_next = NULL;
688         res = getaddrinfo("localhost", NULL, &hints, &gaires);
689 #ifdef WORKING_IPV6
690         if (res != 0) {
691             hints.ai_flags = AI_CANONNAME;
692             hints.ai_family = AF_UNSPEC;
693             res = getaddrinfo("localhost", NULL, &hints, &gaires);
694         }
695 #endif
696         if (res != 0) {
697             fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
698         }
699         if (gaires) freeaddrinfo(gaires);
700     }
701
702     /*
703      * Look up the programs used on the server side.
704      */
705     if(do_localchk) {
706         /* 
707          * entreprise version will do planner/dumper suid check
708          */
709         if(access(libexecdir, X_OK) == -1) {
710             quoted = quote_string(libexecdir);
711             fprintf(outf, "ERROR: program dir %s: not accessible\n",
712                     quoted);
713             pgmbad = 1;
714             amfree(quoted);
715         } else {
716             if(test_server_pgm(outf, libexecdir, "planner", 1, uid_dumpuser))
717                 pgmbad = 1;
718             if(test_server_pgm(outf, libexecdir, "dumper", 1, uid_dumpuser))
719                 pgmbad = 1;
720             if(test_server_pgm(outf, libexecdir, "driver", 0, uid_dumpuser))
721                 pgmbad = 1;
722             if(test_server_pgm(outf, libexecdir, "taper", 0, uid_dumpuser))
723                 pgmbad = 1;
724             if(test_server_pgm(outf, libexecdir, "amtrmidx", 0, uid_dumpuser))
725                 pgmbad = 1;
726             if(test_server_pgm(outf, libexecdir, "amlogroll", 0, uid_dumpuser))
727                 pgmbad = 1;
728         }
729         if(access(sbindir, X_OK) == -1) {
730             quoted = quote_string(sbindir);
731             fprintf(outf, "ERROR: program dir %s: not accessible\n",
732                     sbindir);
733             pgmbad = 1;
734             amfree(quoted);
735         } else {
736             if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
737                 pgmbad = 1;
738             if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
739                 pgmbad = 1;
740             if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
741                 pgmbad = 1;
742             if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
743                 pgmbad = 1;
744         }
745         if(access(COMPRESS_PATH, X_OK) == -1) {
746             quoted = quote_string(COMPRESS_PATH);
747             fprintf(outf, "WARNING: %s is not executable, server-compression and indexing will not work\n",
748                     quoted);
749             amfree(quoted);
750         }
751     }
752
753     /*
754      * Check that the directory for the tapelist file is writable, as well
755      * as the tapelist file itself (if it already exists).  Also, check for
756      * a "hold" file (just because it is convenient to do it here) and warn
757      * if tapedev is set to the null device.
758      */
759
760     if(do_localchk || do_tapechk) {
761         char *conf_tapelist;
762         char *tapefile;
763         char *tape_dir;
764         char *lastslash;
765         char *holdfile;
766         struct stat statbuf;
767         
768         conf_tapelist=getconf_str(CNF_TAPELIST);
769         if (*conf_tapelist == '/') {
770             tapefile = stralloc(conf_tapelist);
771         } else {
772             tapefile = stralloc2(config_dir, conf_tapelist);
773         }
774         /*
775          * XXX There Really Ought to be some error-checking here... dhw
776          */
777         tape_dir = stralloc(tapefile);
778         if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
779             *lastslash = '\0';
780         /*
781          * else whine Really Loudly about a path with no slashes??!?
782          */
783         }
784         if(access(tape_dir, W_OK) == -1) {
785             quoted = quote_string(tape_dir);
786             fprintf(outf, "ERROR: tapelist dir %s: not writable.\n", quoted);
787             tapebad = 1;
788             amfree(quoted);
789         }
790         else if(stat(tapefile, &statbuf) == -1) {
791             quoted = quote_string(tape_dir);
792             fprintf(outf, "ERROR: tapelist %s (%s), "
793                     "you must create an empty file.\n",
794                     quoted, strerror(errno));
795             tapebad = 1;
796             amfree(quoted);
797         }
798         else if(!S_ISREG(statbuf.st_mode)) {
799             quoted = quote_string(tapefile);
800             fprintf(outf, "ERROR: tapelist %s: should be a regular file.\n",
801                     quoted);
802             tapebad = 1;
803             amfree(quoted);
804         }
805         else if(access(tapefile, F_OK) != 0) {
806             quoted = quote_string(tapefile);
807             fprintf(outf, "ERROR: can't access tapelist %s\n", quoted);
808             tapebad = 1;
809             amfree(quoted);
810         } else if(access(tapefile, F_OK) == 0 && access(tapefile, W_OK) != 0) {
811             quoted = quote_string(tapefile);
812             fprintf(outf, "ERROR: tapelist %s: not writable\n", quoted);
813             tapebad = 1;
814             amfree(quoted);
815         } else if(read_tapelist(tapefile)) {
816             quoted = quote_string(tapefile);
817             fprintf(outf, "ERROR: tapelist %s: parse error\n", quoted);
818             tapebad = 1;
819             amfree(quoted);
820         }
821         holdfile = vstralloc(config_dir, "/", "hold", NULL);
822         if(access(holdfile, F_OK) != -1) {
823             quoted = quote_string(holdfile);
824             fprintf(outf, "WARNING: hold file %s exists\n", holdfile);
825             amfree(quoted);
826         }
827         amfree(tapefile);
828         amfree(tape_dir);
829         amfree(holdfile);
830         tapename = getconf_str(CNF_TAPEDEV);
831         if (tapename == NULL) {
832             if (getconf_str(CNF_TPCHANGER) == NULL) {
833                 fprintf(outf, "WARNING: No tapedev or tpchanger specified\n");
834                 testtape = 0;
835                 do_tapechk = 0;
836             }
837         } else if (strncmp_const(tapename, "null:") == 0) {
838             fprintf(outf,
839                     "WARNING: tapedev is %s, dumps will be thrown away\n",
840                     tapename);
841             testtape = 0;
842             do_tapechk = 0;
843         }
844     }
845
846     /* check available disk space */
847
848     if(do_localchk) {
849         for(hdp = holdingdisks; hdp != NULL; hdp = hdp->next) {
850             quoted = quote_string(holdingdisk_get_diskdir(hdp));
851             if(get_fs_stats(holdingdisk_get_diskdir(hdp), &fs) == -1) {
852                 fprintf(outf, "ERROR: holding dir %s (%s), "
853                         "you must create a directory.\n",
854                         quoted, strerror(errno));
855                 disklow = 1;
856             }
857             else if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
858                 fprintf(outf, "ERROR: holding disk %s: not writable: %s.\n",
859                         quoted, strerror(errno));
860                 disklow = 1;
861             }
862             else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
863                 fprintf(outf, "ERROR: holding disk %s: not searcheable: %s.\n",
864                         quoted, strerror(errno));
865                 disklow = 1;
866             }
867             else if(fs.avail == (off_t)-1) {
868                 fprintf(outf,
869                         "WARNING: holding disk %s: "
870                         "available space unknown (" OFF_T_FMT" KB requested)\n",
871                         quoted, (OFF_T_FMT_TYPE)holdingdisk_get_disksize(hdp));
872                 disklow = 1;
873             }
874             else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
875                 if(fs.avail < holdingdisk_get_disksize(hdp)) {
876                     fprintf(outf,
877                             "WARNING: holding disk %s: "
878                             "only " OFF_T_FMT " %sB free ("
879                             OFF_T_FMT " %sB requested)\n", quoted,
880                             (OFF_T_FMT_TYPE)(fs.avail / (off_t)unitdivisor),
881                             displayunit,
882                             (OFF_T_FMT_TYPE)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
883                             displayunit);
884                     disklow = 1;
885                 }
886                 else {
887                     fprintf(outf,
888                             "Holding disk %s: " OFF_T_FMT
889                             " %sB disk space available,"
890                             " using " OFF_T_FMT " %sB as requested\n",
891                             quoted,
892                             (OFF_T_FMT_TYPE)(fs.avail/(off_t)unitdivisor),
893                             displayunit,
894                             (OFF_T_FMT_TYPE)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
895                             displayunit);
896                 }
897             }
898             else {
899                 if((fs.avail + holdingdisk_get_disksize(hdp)) < (off_t)0) {
900                     fprintf(outf,
901                             "WARNING: holding disk %s: "
902                             "only " OFF_T_FMT " %sB free, using nothing\n",
903                             quoted, (OFF_T_FMT_TYPE)(fs.avail/(off_t)unitdivisor),
904                             displayunit);
905                     disklow = 1;
906                 }
907                 else {
908                     fprintf(outf,
909                             "Holding disk %s: "
910                             OFF_T_FMT " %sB disk space available, using "
911                             OFF_T_FMT " %sB\n",
912                             quoted,
913                             (OFF_T_FMT_TYPE)(fs.avail/(off_t)unitdivisor),
914                             displayunit,
915                             (OFF_T_FMT_TYPE)((fs.avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
916                             displayunit);
917                 }
918             }
919             amfree(quoted);
920         }
921     }
922
923     /* check that the log file is writable if it already exists */
924
925     if(do_localchk) {
926         char *conf_logdir;
927         char *logfile;
928         char *olddir;
929         struct stat stat_old;
930         struct stat statbuf;
931
932         conf_logdir = getconf_str(CNF_LOGDIR);
933         if (*conf_logdir == '/') {
934             conf_logdir = stralloc(conf_logdir);
935         } else {
936             conf_logdir = stralloc2(config_dir, conf_logdir);
937         }
938         logfile = vstralloc(conf_logdir, "/log", NULL);
939
940         quoted = quote_string(conf_logdir);
941         if(stat(conf_logdir, &statbuf) == -1) {
942             fprintf(outf, "ERROR: logdir %s (%s), you must create directory.\n",
943                     quoted, strerror(errno));
944             disklow = 1;
945         }
946         else if(access(conf_logdir, W_OK) == -1) {
947             fprintf(outf, "ERROR: log dir %s: not writable\n", quoted);
948             logbad = 1;
949         }
950         amfree(quoted);
951
952         if(access(logfile, F_OK) == 0) {
953             testtape = 0;
954             logbad = 2;
955             if(access(logfile, W_OK) != 0) {
956                 quoted = quote_string(logfile);
957                 fprintf(outf, "ERROR: log file %s: not writable\n", quoted);
958                 amfree(quoted);
959             }
960         }
961
962         olddir = vstralloc(conf_logdir, "/oldlog", NULL);
963         quoted = quote_string(olddir);
964         if (stat(olddir,&stat_old) == 0) { /* oldlog exist */
965             if(!(S_ISDIR(stat_old.st_mode))) {
966                 fprintf(outf, "ERROR: oldlog directory %s is not a directory\n",
967                         quoted);
968                 logbad = 1;
969             }
970             if(access(olddir, W_OK) == -1) {
971                 fprintf(outf, "ERROR: oldlog dir %s: not writable\n", quoted);
972                 logbad = 1;
973             }
974         }
975         else if(lstat(olddir,&stat_old) == 0) {
976             fprintf(outf, "ERROR: oldlog directory %s is not a directory\n",
977                     quoted);
978             logbad = 1;
979         }
980         amfree(quoted);
981
982         if (testtape) {
983             logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
984             if (access(logfile, F_OK) == 0) {
985                 testtape = 0;
986                 logbad = 2;
987             }
988         }
989
990         amfree(olddir);
991         amfree(logfile);
992         amfree(conf_logdir);
993     }
994
995     if (testtape) {
996         /* check that the tape is a valid amanda tape */
997         int tape_status;
998
999         tapedays = getconf_int(CNF_TAPECYCLE);
1000         labelstr = getconf_str(CNF_LABELSTR);
1001         tapename = getconf_str(CNF_TAPEDEV);
1002
1003         if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
1004             fprintf(outf,
1005                     "WARNING: if a tape changer is not available, runtapes must be set to 1\n");
1006         }
1007
1008         tape_status = taper_scan(NULL, &label, &datestamp, &tapename,
1009                                  FILE_taperscan_output_callback, outf);
1010         if (tapename) {
1011             if (tape_access(tapename,F_OK) == -1) {
1012                 fprintf(outf, "ERROR: Can't access device %s: %s\n", tapename,
1013                         strerror(errno));
1014                 tapebad = 1;
1015             }
1016             if (tape_access(tapename,R_OK) == -1) {
1017                 fprintf(outf, "ERROR: Can't read device %s: %s\n", tapename,
1018                         strerror(errno));
1019                 tapebad = 1;
1020             }
1021             if (tape_access(tapename,W_OK) == -1) {
1022                 fprintf(outf, "ERROR: Can't write to device %s: %s\n", tapename,
1023                         strerror(errno));
1024                 tapebad = 1;
1025             }
1026         }
1027         if (tape_status < 0) {
1028             tape_t *exptape = lookup_last_reusable_tape(0);
1029             fprintf(outf, "       (expecting ");
1030             if(exptape != NULL) fprintf(outf, "tape %s or ", exptape->label);
1031             fprintf(outf, "a new tape)\n");
1032             tapebad = 1;
1033         } else {
1034             if (overwrite) {
1035                 char *wrlabel_status;
1036                 wrlabel_status = tape_wrlabel(tapename, "X", label,
1037                                 (unsigned)(tapetype_get_blocksize(tp) * 1024));
1038                 if (wrlabel_status != NULL) {
1039                     if (tape_status == 3) {
1040                         fprintf(outf,
1041                                 "ERROR: Could not label brand new tape: %s\n",
1042                                 wrlabel_status);
1043                     } else {
1044                         fprintf(outf,
1045                                 "ERROR: tape %s label ok, but is not writable (%s)\n",
1046                                 label, wrlabel_status);
1047                     }
1048                     tapebad = 1;
1049                 } else {
1050                     if (tape_status != 3) {
1051                         fprintf(outf, "Tape %s is writable; rewrote label.\n", label);
1052                     } else {
1053                         fprintf(outf, "Wrote label %s to brand new tape.\n", label);
1054                     }
1055                 }
1056             } else {
1057                 fprintf(outf, "NOTE: skipping tape-writable test\n");
1058                 if (tape_status == 3) {
1059                     fprintf(outf,
1060                             "Found a brand new tape, will label it %s.\n", 
1061                             label);
1062                 } else {
1063                     fprintf(outf, "Tape %s label ok\n", label);
1064                 }                    
1065             }
1066         }
1067         amfree(tapename);
1068     } else if (do_tapechk) {
1069         fprintf(outf, "WARNING: skipping tape test because amdump or amflush seem to be running\n");
1070         fprintf(outf, "WARNING: if they are not, you must run amcleanup\n");
1071     } else if (logbad == 2) {
1072         fprintf(outf, "WARNING: amdump or amflush seem to be running\n");
1073         fprintf(outf, "WARNING: if they are not, you must run amcleanup\n");
1074     } else {
1075         fprintf(outf, "NOTE: skipping tape checks\n");
1076     }
1077
1078     /*
1079      * See if the information file and index directory for each client
1080      * and disk is OK.  Since we may be seeing clients and/or disks for
1081      * the first time, these are just warnings, not errors.
1082      */
1083     if(do_localchk) {
1084         char *conf_infofile;
1085         char *conf_indexdir;
1086         char *hostinfodir = NULL;
1087         char *hostindexdir = NULL;
1088         char *diskdir = NULL;
1089         char *infofile = NULL;
1090         struct stat statbuf;
1091         disk_t *dp;
1092         am_host_t *hostp;
1093         int indexdir_checked = 0;
1094         int hostindexdir_checked = 0;
1095         char *host;
1096         char *disk;
1097         int conf_tapecycle, conf_runspercycle;
1098
1099         conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1100         conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1101
1102         if(conf_tapecycle <= conf_runspercycle) {
1103                 fprintf(outf, "WARNING: tapecycle (%d) <= runspercycle (%d).\n",
1104                         conf_tapecycle, conf_runspercycle);
1105         }
1106
1107         conf_infofile = getconf_str(CNF_INFOFILE);
1108         if (*conf_infofile == '/') {
1109             conf_infofile = stralloc(conf_infofile);
1110         } else {
1111             conf_infofile = stralloc2(config_dir, conf_infofile);
1112         }
1113
1114         conf_indexdir = getconf_str(CNF_INDEXDIR);
1115         if (*conf_indexdir == '/') {
1116             conf_indexdir = stralloc(conf_indexdir);
1117         } else {
1118             conf_indexdir = stralloc2(config_dir, conf_indexdir);
1119         }
1120
1121 #if TEXTDB
1122         quoted = quote_string(conf_infofile);
1123         if(stat(conf_infofile, &statbuf) == -1) {
1124             if (errno == ENOENT) {
1125                 fprintf(outf, "NOTE: conf info dir %s does not exist\n",
1126                         quoted);
1127                 fprintf(outf, "NOTE: it will be created on the next run.\n");
1128             } else {
1129                 fprintf(outf, "ERROR: conf info dir %s (%s)\n",
1130                         quoted, strerror(errno));
1131                 infobad = 1;
1132             }   
1133             amfree(conf_infofile);
1134         } else if (!S_ISDIR(statbuf.st_mode)) {
1135             fprintf(outf, "ERROR: info dir %s: not a directory\n", quoted);
1136             amfree(conf_infofile);
1137             infobad = 1;
1138         } else if (access(conf_infofile, W_OK) == -1) {
1139             fprintf(outf, "ERROR: info dir %s: not writable\n", quoted);
1140             amfree(conf_infofile);
1141             infobad = 1;
1142         } else {
1143             char *errmsg = NULL;
1144             if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1145                 fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1146                 infobad = 1;
1147                 amfree(errmsg);
1148             }
1149             strappend(conf_infofile, "/");
1150         }
1151         amfree(quoted);
1152 #endif
1153
1154         while(!empty(origq)) {
1155             hostp = origq.head->host;
1156             host = sanitise_filename(hostp->hostname);
1157 #if TEXTDB
1158             if(conf_infofile) {
1159                 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1160                 quoted = quote_string(hostinfodir);
1161                 if(stat(hostinfodir, &statbuf) == -1) {
1162                     if (errno == ENOENT) {
1163                         fprintf(outf, "NOTE: host info dir %s does not exist\n",
1164                                 quoted);
1165                         fprintf(outf,
1166                                 "NOTE: it will be created on the next run.\n");
1167                     } else {
1168                         fprintf(outf, "ERROR: host info dir %s (%s)\n",
1169                                 quoted, strerror(errno));
1170                         infobad = 1;
1171                     }   
1172                     amfree(hostinfodir);
1173                 } else if (!S_ISDIR(statbuf.st_mode)) {
1174                     fprintf(outf, "ERROR: info dir %s: not a directory\n",
1175                             quoted);
1176                     amfree(hostinfodir);
1177                     infobad = 1;
1178                 } else if (access(hostinfodir, W_OK) == -1) {
1179                     fprintf(outf, "ERROR: info dir %s: not writable\n", quoted);
1180                     amfree(hostinfodir);
1181                     infobad = 1;
1182                 } else {
1183                     strappend(hostinfodir, "/");
1184                 }
1185                 amfree(quoted);
1186             }
1187 #endif
1188             for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1189                 disk = sanitise_filename(dp->name);
1190 #if TEXTDB
1191                 if(hostinfodir) {
1192                     char *quotedif;
1193
1194                     diskdir = newstralloc2(diskdir, hostinfodir, disk);
1195                     infofile = vstralloc(diskdir, "/", "info", NULL);
1196                     quoted = quote_string(diskdir);
1197                     quotedif = quote_string(infofile);
1198                     if(stat(diskdir, &statbuf) == -1) {
1199                         if (errno == ENOENT) {
1200                             fprintf(outf, "NOTE: info dir %s does not exist\n",
1201                                 quoted);
1202                             fprintf(outf,
1203                                 "NOTE: it will be created on the next run.\n");
1204                         } else {
1205                             fprintf(outf, "ERROR: info dir %s (%s)\n",
1206                                     quoted, strerror(errno));
1207                             infobad = 1;
1208                         }       
1209                     } else if (!S_ISDIR(statbuf.st_mode)) {
1210                         fprintf(outf, "ERROR: info dir %s: not a directory\n",
1211                                 quoted);
1212                         infobad = 1;
1213                     } else if (access(diskdir, W_OK) == -1) {
1214                         fprintf(outf, "ERROR: info dir %s: not writable\n",
1215                                 quoted);
1216                         infobad = 1;
1217                     } else if(stat(infofile, &statbuf) == -1) {
1218                         if (errno == ENOENT) {
1219                             fprintf(outf, "NOTE: info file %s does not exist\n",
1220                                     quotedif);
1221                             fprintf(outf, "NOTE: it will be created on the next run.\n");
1222                         } else {
1223                             fprintf(outf, "ERROR: info dir %s (%s)\n",
1224                                     quoted, strerror(errno));
1225                             infobad = 1;
1226                         }       
1227                     } else if (!S_ISREG(statbuf.st_mode)) {
1228                         fprintf(outf, "ERROR: info file %s: not a file\n",
1229                                 quotedif);
1230                         infobad = 1;
1231                     } else if (access(infofile, R_OK) == -1) {
1232                         fprintf(outf, "ERROR: info file %s: not readable\n",
1233                                 quotedif);
1234                         infobad = 1;
1235                     }
1236                     amfree(quotedif);
1237                     amfree(quoted);
1238                     amfree(infofile);
1239                 }
1240 #endif
1241                 if(dp->index) {
1242                     if(! indexdir_checked) {
1243                         quoted = quote_string(conf_indexdir);
1244                         if(stat(conf_indexdir, &statbuf) == -1) {
1245                             if (errno == ENOENT) {
1246                                 fprintf(outf, "NOTE: index dir %s does not exist\n",
1247                                         quoted);
1248                                 fprintf(outf, "NOTE: it will be created on the next run.\n");
1249                             } else {
1250                                 fprintf(outf, "ERROR: index dir %s (%s)\n",
1251                                         quoted, strerror(errno));
1252                                 indexbad = 1;
1253                             }   
1254                             amfree(conf_indexdir);
1255                         } else if (!S_ISDIR(statbuf.st_mode)) {
1256                             fprintf(outf, "ERROR: index dir %s: not a directory\n",
1257                                     quoted);
1258                             amfree(conf_indexdir);
1259                             indexbad = 1;
1260                         } else if (access(conf_indexdir, W_OK) == -1) {
1261                             fprintf(outf, "ERROR: index dir %s: not writable\n",
1262                                     quoted);
1263                             amfree(conf_indexdir);
1264                             indexbad = 1;
1265                         } else {
1266                             strappend(conf_indexdir, "/");
1267                         }
1268                         indexdir_checked = 1;
1269                         amfree(quoted);
1270                     }
1271                     if(conf_indexdir) {
1272                         if(! hostindexdir_checked) {
1273                             hostindexdir = stralloc2(conf_indexdir, host);
1274                             quoted = quote_string(hostindexdir);
1275                             if(stat(hostindexdir, &statbuf) == -1) {
1276                                 if (errno == ENOENT) {
1277                                     fprintf(outf, "NOTE: index dir %s does not exist\n",
1278                                             quoted);
1279                                     fprintf(outf, "NOTE: it will be created on the next run.\n");
1280                                 } else {
1281                                     fprintf(outf, "ERROR: index dir %s (%s)\n",
1282                                             quoted, strerror(errno));
1283                                     indexbad = 1;
1284                                 }
1285                                 amfree(hostindexdir);
1286                             } else if (!S_ISDIR(statbuf.st_mode)) {
1287                                 fprintf(outf, "ERROR: index dir %s: not a directory\n",
1288                                         quoted);
1289                                 amfree(hostindexdir);
1290                                 indexbad = 1;
1291                             } else if (access(hostindexdir, W_OK) == -1) {
1292                                 fprintf(outf, "ERROR: index dir %s: not writable\n",
1293                                         quoted);
1294                                 amfree(hostindexdir);
1295                                 indexbad = 1;
1296                             } else {
1297                                 strappend(hostindexdir, "/");
1298                             }
1299                             hostindexdir_checked = 1;
1300                             amfree(quoted);
1301                         }
1302                         if(hostindexdir) {
1303                             diskdir = newstralloc2(diskdir, hostindexdir, disk);
1304                             quoted = quote_string(diskdir);
1305                             if(stat(diskdir, &statbuf) == -1) {
1306                                 if (errno == ENOENT) {
1307                                     fprintf(outf, "NOTE: index dir %s does not exist\n",
1308                                             quoted);
1309                                     fprintf(outf, "NOTE: it will be created on the next run.\n");
1310                                 } else {
1311                                     fprintf(outf, "ERROR: index dir %s (%s)\n",
1312                                         quoted, strerror(errno));
1313                                     indexbad = 1;
1314                                 }       
1315                             } else if (!S_ISDIR(statbuf.st_mode)) {
1316                                 fprintf(outf, "ERROR: index dir %s: not a directory\n",
1317                                         quoted);
1318                                 indexbad = 1;
1319                             } else if (access(diskdir, W_OK) == -1) {
1320                                 fprintf(outf, "ERROR: index dir %s: is not writable\n",
1321                                         quoted);
1322                                 indexbad = 1;
1323                             }
1324                             amfree(quoted);
1325                         }
1326                     }
1327                 }
1328
1329                 if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1330                   if ( dp->srv_encrypt[0] == '\0' ) {
1331                     fprintf(outf, "ERROR: server encryption program not specified\n");
1332                     pgmbad = 1;
1333                   }
1334                   else if(access(dp->srv_encrypt, X_OK) == -1) {
1335                     fprintf(outf, "ERROR: %s is not executable, server encryption will not work\n",
1336                             dp->srv_encrypt );
1337                     pgmbad = 1;
1338                   }
1339                 }
1340                 if ( dp->compress == COMP_SERVER_CUST ) {
1341                   if ( dp->srvcompprog[0] == '\0' ) {
1342                     fprintf(outf, "ERROR: server custom compression program not specified\n");
1343                     pgmbad = 1;
1344                   }
1345                   else if(access(dp->srvcompprog, X_OK) == -1) {
1346                     quoted = quote_string(dp->srvcompprog);
1347
1348                     fprintf(outf, "ERROR: %s is not executable, server custom compression will not work\n",
1349                             quoted);
1350                     amfree(quoted);
1351                     pgmbad = 1;
1352                   }
1353                 }
1354
1355                 amfree(disk);
1356                 remove_disk(&origq, dp);
1357             }
1358             amfree(host);
1359             amfree(hostindexdir);
1360             hostindexdir_checked = 0;
1361         }
1362         amfree(diskdir);
1363         amfree(hostinfodir);
1364         amfree(conf_infofile);
1365         amfree(conf_indexdir);
1366     }
1367
1368     amfree(datestamp);
1369     amfree(label);
1370     amfree(config_dir);
1371     amfree(config_name);
1372
1373     fprintf(outf, "Server check took %s seconds\n", walltime_str(curclock()));
1374
1375     fflush(outf);
1376
1377     malloc_size_2 = malloc_inuse(&malloc_hist_2);
1378
1379     if(malloc_size_1 != malloc_size_2) {
1380         malloc_list(fd, malloc_hist_1, malloc_hist_2);
1381     }
1382
1383     exit(userbad \
1384          || confbad \
1385          || tapebad \
1386          || disklow \
1387          || logbad \
1388          || infobad \
1389          || indexbad \
1390          || pgmbad);
1391     /*NOTREACHED*/
1392     return 0;
1393 }
1394
1395 /* --------------------------------------------------- */
1396
1397 int remote_errors;
1398 FILE *outf;
1399
1400 static void handle_result(void *, pkt_t *, security_handle_t *);
1401 void start_host(am_host_t *hostp);
1402
1403 #define HOST_READY                              ((void *)0)     /* must be 0 */
1404 #define HOST_ACTIVE                             ((void *)1)
1405 #define HOST_DONE                               ((void *)2)
1406
1407 #define DISK_READY                              ((void *)0)     /* must be 0 */
1408 #define DISK_ACTIVE                             ((void *)1)
1409 #define DISK_DONE                               ((void *)2)
1410
1411 void
1412 start_host(
1413     am_host_t *hostp)
1414 {
1415     disk_t *dp;
1416     char *req = NULL;
1417     size_t req_len = 0;
1418     int disk_count;
1419     const security_driver_t *secdrv;
1420     char number[NUM_STR_SIZE];
1421
1422     if(hostp->up != HOST_READY) {
1423         return;
1424     }
1425
1426     if (strcmp(hostp->hostname,"localhost") == 0) {
1427         fprintf(outf,
1428                     "WARNING: Usage of fully qualified hostname recommended for Client %s.\n",
1429                     hostp->hostname);
1430     }
1431
1432     /*
1433      * The first time through here we send a "noop" request.  This will
1434      * return the feature list from the client if it supports that.
1435      * If it does not, handle_result() will set the feature list to an
1436      * empty structure.  In either case, we do the disks on the second
1437      * (and subsequent) pass(es).
1438      */
1439     disk_count = 0;
1440     if(hostp->features != NULL) { /* selfcheck service */
1441         int has_features = am_has_feature(hostp->features,
1442                                           fe_req_options_features);
1443         int has_hostname = am_has_feature(hostp->features,
1444                                           fe_req_options_hostname);
1445         int has_maxdumps = am_has_feature(hostp->features,
1446                                           fe_req_options_maxdumps);
1447         int has_config   = am_has_feature(hostp->features,
1448                                           fe_req_options_config);
1449
1450         if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1451            !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1452             fprintf(outf,
1453                     "ERROR: Client %s does not support selfcheck REQ packet.\n",
1454                     hostp->hostname);
1455         }
1456         if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1457             fprintf(outf,
1458                     "ERROR: Client %s does not support selfcheck REP packet.\n",
1459                     hostp->hostname);
1460         }
1461         if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1462            !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1463            !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1464             fprintf(outf,
1465                     "ERROR: Client %s does not support sendsize REQ packet.\n",
1466                     hostp->hostname);
1467         }
1468         if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1469             fprintf(outf,
1470                     "ERROR: Client %s does not support sendsize REP packet.\n",
1471                     hostp->hostname);
1472         }
1473         if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1474            !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1475             fprintf(outf,
1476                    "ERROR: Client %s does not support sendbackup REQ packet.\n",
1477                    hostp->hostname);
1478         }
1479         if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1480             fprintf(outf,
1481                    "ERROR: Client %s does not support sendbackup REP packet.\n",
1482                    hostp->hostname);
1483         }
1484
1485         snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1486         req = vstralloc("SERVICE ", "selfcheck", "\n",
1487                         "OPTIONS ",
1488                         has_features ? "features=" : "",
1489                         has_features ? our_feature_string : "",
1490                         has_features ? ";" : "",
1491                         has_maxdumps ? "maxdumps=" : "",
1492                         has_maxdumps ? number : "",
1493                         has_maxdumps ? ";" : "",
1494                         has_hostname ? "hostname=" : "",
1495                         has_hostname ? hostp->hostname : "",
1496                         has_hostname ? ";" : "",
1497                         has_config   ? "config=" : "",
1498                         has_config   ? config_name : "",
1499                         has_config   ? ";" : "",
1500                         "\n",
1501                         NULL);
1502
1503         req_len = strlen(req);
1504         req_len += 128;                         /* room for SECURITY ... */
1505         req_len += 256;                         /* room for non-disk answers */
1506         for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1507             char *l;
1508             size_t l_len;
1509             char *o;
1510             char *calcsize;
1511             char *qname;
1512             char *qdevice;
1513
1514             if(dp->up != DISK_READY || dp->todo != 1) {
1515                 continue;
1516             }
1517             o = optionstr(dp, hostp->features, outf);
1518             if (o == NULL) {
1519                 remote_errors++;
1520                 continue;
1521             }
1522             qname = quote_string(dp->name); 
1523             qdevice = quote_string(dp->device); 
1524             if ((dp->name && qname[0] == '"') || 
1525                 (dp->device && qdevice[0] == '"')) {
1526                 if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1527                     fprintf(outf,
1528                             "WARNING: %s:%s:%s host does not support quoted text\n",
1529                             hostp->hostname, qname, qdevice);
1530                 }
1531             }
1532
1533             if(dp->device) {
1534                 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1535                     fprintf(outf,
1536                      "ERROR: %s:%s (%s): selfcheck does not support device.\n",
1537                      hostp->hostname, qname, dp->device);
1538                 }
1539                 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1540                     fprintf(outf,
1541                      "ERROR: %s:%s (%s): sendsize does not support device.\n",
1542                      hostp->hostname, qname, dp->device);
1543                 }
1544                 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1545                     fprintf(outf,
1546                      "ERROR: %s:%s (%s): sendbackup does not support device.\n",
1547                      hostp->hostname, qname, dp->device);
1548                 }
1549             }
1550             if(strcmp(dp->program,"DUMP") == 0 || 
1551                strcmp(dp->program,"GNUTAR") == 0) {
1552                 if(strcmp(dp->program, "DUMP") == 0 &&
1553                    !am_has_feature(hostp->features, fe_program_dump)) {
1554                     fprintf(outf, "ERROR: %s:%s does not support DUMP.\n",
1555                             hostp->hostname, qname);
1556                 }
1557                 if(strcmp(dp->program, "GNUTAR") == 0 &&
1558                    !am_has_feature(hostp->features, fe_program_gnutar)) {
1559                     fprintf(outf, "ERROR: %s:%s does not support GNUTAR.\n",
1560                             hostp->hostname, qname);
1561                 }
1562                 if(dp->estimate == ES_CALCSIZE &&
1563                    !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1564                     fprintf(outf, "ERROR: %s:%s does not support CALCSIZE for estimate, using CLIENT.\n",
1565                             hostp->hostname, qname);
1566                     dp->estimate = ES_CLIENT;
1567                 }
1568                 if(dp->estimate == ES_CALCSIZE &&
1569                    am_has_feature(hostp->features, fe_selfcheck_calcsize))
1570                     calcsize = "CALCSIZE ";
1571                 else
1572                     calcsize = "";
1573
1574                 if(dp->compress == COMP_CUST &&
1575                    !am_has_feature(hostp->features, fe_options_compress_cust)) {
1576                   fprintf(outf,
1577                           "ERROR: Client %s does not support custom compression.\n",
1578                           hostp->hostname);
1579                 }
1580                 if(dp->encrypt == ENCRYPT_CUST ) {
1581                   if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1582                     fprintf(outf,
1583                             "ERROR: Client %s does not support data encryption.\n",
1584                             hostp->hostname);
1585                     remote_errors++;
1586                   } else if ( dp->compress == COMP_SERVER_FAST || 
1587                               dp->compress == COMP_SERVER_BEST ||
1588                               dp->compress == COMP_SERVER_CUST ) {
1589                     fprintf(outf,
1590                             "ERROR: %s: Client encryption with server compression is not supported. See amanda.conf(5) for detail.\n", hostp->hostname);
1591                     remote_errors++;
1592                   } 
1593                 }
1594                 if(dp->device) {
1595                     l = vstralloc(calcsize,
1596                                   dp->program, " ",
1597                                   qname, " ",
1598                                   qdevice,
1599                                   " 0 OPTIONS |",
1600                                   o,
1601                                   "\n",
1602                                   NULL);
1603                 }
1604                 else {
1605                     l = vstralloc(calcsize,
1606                                   dp->program, " ",
1607                                   qname,
1608                                   " 0 OPTIONS |",
1609                                   o,
1610                                   "\n",
1611                                   NULL);
1612                 }
1613             } else {
1614                 if(!am_has_feature(hostp->features, fe_program_backup_api)) {
1615                     fprintf(outf, "ERROR: %s:%s does not support BACKUP-API.\n",
1616                             hostp->hostname, qname);
1617                 }
1618                 if(dp->device) {
1619                     l = vstralloc("BACKUP ",
1620                                   dp->program, 
1621                                   " ",
1622                                   qname,
1623                                   " ",
1624                                   qdevice,
1625                                   " 0 OPTIONS |",
1626                                   o,
1627                                   "\n",
1628                                   NULL);
1629                 } else {
1630                     l = vstralloc("BACKUP ",
1631                                   dp->program, 
1632                                   " ",
1633                                   qname,
1634                                   " 0 OPTIONS |",
1635                                   o,
1636                                   "\n",
1637                                   NULL);
1638                 }
1639             }
1640             amfree(qname);
1641             amfree(qdevice);
1642             l_len = strlen(l);
1643             amfree(o);
1644
1645             strappend(req, l);
1646             req_len += l_len;
1647             amfree(l);
1648             dp->up = DISK_ACTIVE;
1649             disk_count++;
1650         }
1651     }
1652     else { /* noop service */
1653         req = vstralloc("SERVICE ", "noop", "\n",
1654                         "OPTIONS ",
1655                         "features=", our_feature_string, ";",
1656                         "\n",
1657                         NULL);
1658         for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1659             if(dp->up != DISK_READY || dp->todo != 1) {
1660                 continue;
1661             }
1662             disk_count++;
1663         }
1664     }
1665
1666     if(disk_count == 0) {
1667         amfree(req);
1668         hostp->up = HOST_DONE;
1669         return;
1670     }
1671
1672     secdrv = security_getdriver(hostp->disks->security_driver);
1673     if (secdrv == NULL) {
1674         error("could not find security driver '%s' for host '%s'",
1675               hostp->disks->security_driver, hostp->hostname);
1676         /*NOTREACHED*/
1677     }
1678     protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, 
1679                      req, conf_ctimeout, handle_result, hostp);
1680
1681     amfree(req);
1682
1683     hostp->up = HOST_ACTIVE;
1684 }
1685
1686 pid_t
1687 start_client_checks(
1688     int         fd)
1689 {
1690     am_host_t *hostp;
1691     disk_t *dp;
1692     int hostcount;
1693     pid_t pid;
1694     int userbad = 0;
1695
1696     switch(pid = fork()) {
1697     case -1:
1698         error("could not fork client check: %s", strerror(errno));
1699         /*NOTREACHED*/
1700
1701     case 0:
1702         break;
1703
1704     default:
1705         return pid;
1706     }
1707
1708     dup2(fd, 1);
1709     dup2(fd, 2);
1710
1711     set_pname("amcheck-clients");
1712
1713     startclock();
1714
1715     if((outf = fdopen(fd, "w")) == NULL) {
1716         error("fdopen %d: %s", fd, strerror(errno));
1717         /*NOTREACHED*/
1718     }
1719     errf = outf;
1720
1721     fprintf(outf, "\nAmanda Backup Client Hosts Check\n");
1722     fprintf(outf,   "--------------------------------\n");
1723
1724     protocol_init();
1725
1726     hostcount = remote_errors = 0;
1727
1728     for(dp = origq.head; dp != NULL; dp = dp->next) {
1729         hostp = dp->host;
1730         if(hostp->up == HOST_READY && dp->todo == 1) {
1731             start_host(hostp);
1732             hostcount++;
1733             protocol_check();
1734         }
1735     }
1736
1737     protocol_run();
1738
1739     fprintf(outf,
1740      "Client check: %d host%s checked in %s seconds, %d problem%s found\n",
1741             hostcount, (hostcount == 1) ? "" : "s",
1742             walltime_str(curclock()),
1743             remote_errors, (remote_errors == 1) ? "" : "s");
1744     fflush(outf);
1745
1746     amfree(config_dir);
1747     amfree(config_name);
1748
1749     malloc_size_2 = malloc_inuse(&malloc_hist_2);
1750
1751     if(malloc_size_1 != malloc_size_2) {
1752         malloc_list(fd, malloc_hist_1, malloc_hist_2);
1753     }
1754
1755     exit(userbad || remote_errors > 0);
1756     /*NOTREACHED*/
1757     return 0;
1758 }
1759
1760 static void
1761 handle_result(
1762     void *              datap,
1763     pkt_t *             pkt,
1764     security_handle_t * sech)
1765 {
1766     am_host_t *hostp;
1767     disk_t *dp;
1768     char *line;
1769     char *s;
1770     char *t;
1771     int ch;
1772     int tch;
1773
1774     hostp = (am_host_t *)datap;
1775     hostp->up = HOST_READY;
1776
1777     if (pkt == NULL) {
1778         fprintf(outf,
1779             "WARNING: %s: selfcheck request failed: %s\n", hostp->hostname,
1780             security_geterror(sech));
1781         remote_errors++;
1782         hostp->up = HOST_DONE;
1783         return;
1784     }
1785
1786 #if 0
1787     fprintf(errf, "got response from %s:\n----\n%s----\n\n",
1788             hostp->hostname, pkt->body);
1789 #endif
1790
1791     s = pkt->body;
1792     ch = *s++;
1793     while(ch) {
1794         line = s - 1;
1795         skip_quoted_line(s, ch);
1796         if (s[-2] == '\n') {
1797             s[-2] = '\0';
1798         }
1799
1800         if(strncmp_const(line, "OPTIONS ") == 0) {
1801
1802             t = strstr(line, "features=");
1803             if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1804                 t += SIZEOF("features=")-1;
1805                 am_release_feature_set(hostp->features);
1806                 if((hostp->features = am_string_to_feature(t)) == NULL) {
1807                     fprintf(outf, "ERROR: %s: bad features value: %s\n",
1808                             hostp->hostname, line);
1809                 }
1810             }
1811
1812             continue;
1813         }
1814
1815         if(strncmp_const(line, "OK ") == 0) {
1816             continue;
1817         }
1818
1819         t = line;
1820         if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
1821             skip_whitespace(t, tch);
1822             /*
1823              * If the "error" is that the "noop" service is unknown, it
1824              * just means the client is "old" (does not support the service).
1825              * We can ignore this.
1826              */
1827             if(!((hostp->features == NULL) && (pkt->type == P_NAK)
1828                && ((strcmp(t - 1, "unknown service: noop") == 0)
1829                    || (strcmp(t - 1, "noop: invalid service") == 0)))) {
1830                 fprintf(outf, "ERROR: %s%s: %s\n",
1831                         (pkt->type == P_NAK) ? "NAK " : "",
1832                         hostp->hostname,
1833                         t - 1);
1834                 remote_errors++;
1835                 hostp->up = HOST_DONE;
1836             }
1837             continue;
1838         }
1839
1840         fprintf(outf, "ERROR: %s: unknown response: %s\n",
1841                 hostp->hostname, line);
1842         remote_errors++;
1843         hostp->up = HOST_DONE;
1844     }
1845     if(hostp->up == HOST_READY && hostp->features == NULL) {
1846         /*
1847          * The client does not support the features list, so give it an
1848          * empty one.
1849          */
1850         dbprintf(("%s: no feature set from host %s\n",
1851                   debug_prefix_time(NULL), hostp->hostname));
1852         hostp->features = am_set_default_feature_set();
1853     }
1854     for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1855         if(dp->up == DISK_ACTIVE) {
1856             dp->up = DISK_DONE;
1857         }
1858     }
1859     start_host(hostp);
1860     if(hostp->up == HOST_DONE)
1861         security_close_connection(sech, hostp->hostname);
1862 }