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