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