2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-2000 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: amcheck.c,v 1.50.2.19.2.7.2.20.2.10 2005/04/06 12:32:31 martinea Exp $
29 * checks for common problems in server and clients
44 #include "pipespawn.h"
45 #include "amfeatures.h"
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.
54 # define WEXITSTATUS(r) (((union wait *) &(r))->w_retcode)
55 # define WTERMSIG(r) (((union wait *) &(r))->w_termsig)
58 # define WIFSIGNALED(r) (((union wait *) &(r))->w_termsig != 0)
61 #define BUFFER_SIZE 32768
63 static int conf_ctimeout;
67 static disklist_t *origqp;
69 static uid_t uid_dumpuser;
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));
85 error("Usage: amcheck%s [-M <username>] [-mawsclt] <conf> [host [disk]* ]*", versionsuffix());
88 static unsigned long malloc_hist_1, malloc_size_1;
89 static unsigned long malloc_hist_2, malloc_size_2;
91 static am_feature_t *our_features = NULL;
92 static char *our_feature_string = NULL;
93 static char *displayunit;
94 static long int unitdivisor;
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;
107 int opt, size, result_port, tempfd, mainfd;
117 char *tempfname = NULL;
124 for(fd = 3; fd < FD_SETSIZE; fd++) {
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).
136 set_pname("amcheck");
139 malloc_size_1 = malloc_inuse(&malloc_hist_1);
141 ap_snprintf(pid_str, sizeof(pid_str), "%ld", (long)getpid());
143 erroutput_type = ERR_INTERACTIVE;
145 our_features = am_init_feature_set();
146 our_feature_string = am_feature_to_string(our_features);
148 /* set up dgram port first thing */
152 if(dgram_bind(msg, &result_port) == -1)
153 error("could not bind result datagram port: %s", strerror(errno));
156 /* set both real and effective uid's to real uid, likewise for gid */
162 alwaysmail = mailout = overwrite = 0;
163 do_localchk = do_tapechk = do_clientchk = 0;
165 server_probs = client_probs = 0;
166 tempfd = mainfd = -1;
168 /* process arguments */
170 while((opt = getopt(argc, argv, "M:mawsclt")) != EOF) {
172 case 'M': mailto=stralloc(optarg);
177 printf("You can't use -%c because configure didn't find a mailer.\n",
187 printf("You can't use -%c because configure didn't find a mailer.\n",
192 case 's': do_localchk = 1; do_tapechk = 1;
195 case 'c': do_clientchk = 1;
198 case 'l': do_localchk = 1;
201 case 'w': do_tapechk = 1; overwrite = 1;
204 case 't': do_tapechk = 1;
212 argc -= optind, argv += optind;
214 do_localchk = do_clientchk = do_tapechk = 1;
217 if(argc < 1) usage();
219 config_name = stralloc(*argv);
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);
227 conf_ctimeout = getconf_int(CNF_CTIMEOUT);
228 conf_diskfile = getconf_str(CNF_DISKFILE);
229 if (*conf_diskfile == '/') {
230 conf_diskfile = stralloc(conf_diskfile);
232 conf_diskfile = stralloc2(config_dir, conf_diskfile);
234 if((origqp = read_diskfile(conf_diskfile)) == NULL) {
235 error("could not load disklist %s", conf_diskfile);
237 match_disklist(origqp, argc-1, argv+1);
238 amfree(conf_diskfile);
241 * Make sure we are running as the dump user.
243 dumpuser = getconf_str(CNF_DUMPUSER);
244 if ((pw = getpwnam(dumpuser)) == NULL) {
245 error("cannot look up dump user \"%s\"", dumpuser);
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);
251 if (uid_me != uid_dumpuser) {
252 error("running as user \"%s\" instead of \"%s\"",
257 displayunit = getconf_str(CNF_DISPLAYUNIT);
258 unitdivisor = getconf_unit_divisor();
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.
265 * If the output is to be mailed, the main output is also a disk file,
266 * otherwise it is stdout.
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 */
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 */
286 /* just use stdout */
289 /* start server side checks */
291 if(do_localchk || do_tapechk) {
292 serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
297 /* start client side checks */
300 clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
305 /* wait for child processes and note any problems */
308 if((pid = wait(&retstat)) == -1) {
309 if(errno == EINTR) continue;
311 } else if(pid == clientchk_pid) {
312 client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
314 } else if(pid == serverchk_pid) {
315 server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
318 char number[NUM_STR_SIZE];
319 char *wait_msg = NULL;
321 ap_snprintf(number, sizeof(number), "%ld", (long)pid);
322 wait_msg = vstralloc("parent: reaped bogus pid ", number, "\n",
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));
334 /* copy temp output to main output and write tagline */
336 if(do_clientchk && (do_localchk || do_tapechk)) {
337 if(lseek(tempfd, 0, 0) == -1)
338 error("seek temp file: %s", strerror(errno));
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));
348 error("read temp file: %s", strerror(errno));
352 version_string = vstralloc("\n",
353 "(brought to you by Amanda ", version(), ")\n",
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));
360 amfree(version_string);
363 amfree(our_feature_string);
364 am_release_feature_set(our_features);
367 malloc_size_2 = malloc_inuse(&malloc_hist_2);
369 if(malloc_size_1 != malloc_size_2) {
370 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
373 /* send mail if requested, but only if there were problems */
376 #define MAILTO_LIMIT 10
378 if((server_probs || client_probs || alwaysmail) && mailout) {
391 char *extra_info = NULL;
396 char number[NUM_STR_SIZE];
399 if(lseek(mainfd, (off_t)0, SEEK_SET) == -1) {
400 error("lseek main file: %s", strerror(errno));
402 if(alwaysmail && !(server_probs || client_probs)) {
403 subject = stralloc2(getconf_str(CNF_ORG),
404 " AMCHECK REPORT: NO PROBLEMS FOUND");
406 subject = stralloc2(getconf_str(CNF_ORG),
407 " AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE");
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.
414 * Remember that split() returns the original input string in
415 * argv[0], so we have to skip over that.
417 a = (char **) alloc((MAILTO_LIMIT + 1) * sizeof(char *));
418 memset(a, 0, (MAILTO_LIMIT + 1) * sizeof(char *));
423 n = split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " ");
426 if((nullfd = open("/dev/null", O_RDWR)) < 0) {
427 error("nullfd: /dev/null: %s", strerror(errno));
429 mailpid = pipespawn(MAILER, STDIN_PIPE | STDERR_PIPE,
430 &mailfd, &nullfd, &errfd,
433 a[1], a[2], a[3], a[4],
434 a[5], a[6], a[7], a[8], a[9],
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.
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");
450 error("mailfd write: %s", strerror(errno));
452 error("mailfd write: wrote %d instead of %d", w, r);
457 ferr = fdopen(errfd, "r");
458 for(; (line = agets(ferr)) != NULL; free(line)) {
459 strappend(extra_info, line);
460 strappend(extra_info, "\n");
465 while ((wpid = wait(&retstat)) != -1) {
466 if (WIFSIGNALED(retstat)) {
468 rc = sig = WTERMSIG(retstat);
471 rc = ret = WEXITSTATUS(retstat);
475 strappend(err, "got signal ");
478 strappend(err, "returned ");
480 ap_snprintf(number, sizeof(number), "%d", ret);
481 strappend(err, number);
486 fputs(extra_info, stderr);
489 error("error running mailer %s: %s", MAILER, err);
494 return (server_probs || client_probs);
497 /* --------------------------------------------------- */
499 int nslots, backwards, found, got_match, tapedays;
501 char *first_match_label = NULL, *first_match = NULL, *found_device = NULL;
503 char *searchlabel, *labelstr;
507 int scan_init(rc, ns, bk)
511 error("could not get changer info: %s", changer_resultstr);
519 int taperscan_slot(rc, slotstr, device)
527 fprintf(errf, "%s: fatal slot %s: %s\n",
528 get_pname(), slotstr, changer_resultstr);
532 fprintf(errf, "%s: slot %s: %s\n",
533 get_pname(), slotstr, changer_resultstr);
537 if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL) {
538 fprintf(errf, "%s: slot %s: %s\n", get_pname(), slotstr, errstr);
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);
552 else if(!match(labelstr, label))
553 fprintf(errf, " (no match)\n");
555 /* not an exact label match, but a labelstr match */
556 /* check against tape list */
557 tp = lookup_tapelabel(label);
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) {
564 first_match = newstralloc(first_match, slotstr);
565 first_match_label = newstralloc(first_match_label, label);
566 fprintf(errf, " (new tape)\n");
568 found_device = newstralloc(found_device, device);
572 fprintf(errf, " (labelstr match)\n");
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) {
580 found_device = newstralloc(found_device, device);
592 char *outslot = NULL;
594 if((tp = lookup_last_reusable_tape(0)) == NULL)
597 searchlabel = tp->label;
602 changer_find(scan_init, taperscan_slot, searchlabel);
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) {
614 changer_resultstr = newvstralloc(changer_resultstr,
615 "label ", searchlabel,
616 " or new tape not found in rack",
619 changer_resultstr = newstralloc(changer_resultstr,
620 "new tape not found in rack");
625 return found ? found_device : NULL;
628 int test_server_pgm(outf, dir, pgm, suid, dumpuid)
638 pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL);
639 if(stat(pgm, &statbuf) == -1) {
640 fprintf(outf, "ERROR: program %s: does not exist\n",
643 } else if (!S_ISREG(statbuf.st_mode)) {
644 fprintf(outf, "ERROR: program %s: not a file\n",
647 } else if (access(pgm, X_OK) == -1) {
648 fprintf(outf, "ERROR: program %s: not executable\n",
653 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
654 fprintf(outf, "WARNING: program %s: not setuid-root\n",
661 int start_server_check(fd, do_localchk, do_tapechk)
664 char *errstr, *tapename;
665 generic_fs_stats_t fs;
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;
674 switch(pid = fork()) {
675 case -1: error("could not fork server check: %s", strerror(errno));
684 set_pname("amcheck-server");
690 if((outf = fdopen(fd, "w")) == NULL)
691 error("fdopen %d: %s", fd, strerror(errno));
694 fprintf(outf, "Amanda Tape Server Host Check\n");
695 fprintf(outf, "-----------------------------\n");
698 * Check various server side config file settings.
706 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
707 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
708 fprintf(outf, "ERROR: %s\n", errstr);
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);
718 lbl_templ = stralloc(lbl_templ);
720 if(access(lbl_templ, R_OK) == -1) {
722 "ERROR: cannot access lbl_templ file %s: %s\n",
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");
735 * Look up the programs used on the server side.
738 if(access(libexecdir, X_OK) == -1) {
739 fprintf(outf, "ERROR: program dir %s: not accessible\n",
744 || test_server_pgm(outf, libexecdir, "planner",
747 || test_server_pgm(outf, libexecdir, "dumper",
750 || test_server_pgm(outf, libexecdir, "driver",
753 || test_server_pgm(outf, libexecdir, "taper",
756 || test_server_pgm(outf, libexecdir, "amtrmidx",
759 || test_server_pgm(outf, libexecdir, "amlogroll",
762 if(access(sbindir, X_OK) == -1) {
763 fprintf(outf, "ERROR: program dir %s: not accessible\n",
768 || test_server_pgm(outf, sbindir, "amgetconf",
771 || test_server_pgm(outf, sbindir, "amcheck",
774 || test_server_pgm(outf, sbindir, "amdump",
777 || test_server_pgm(outf, sbindir, "amreport",
780 if(access(COMPRESS_PATH, X_OK) == -1) {
781 fprintf(outf, "WARNING: %s is not executable, server-compression and indexing will not work\n",
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.
793 if(do_localchk || do_tapechk) {
801 conf_tapelist=getconf_str(CNF_TAPELIST);
802 if (*conf_tapelist == '/') {
803 tapefile = stralloc(conf_tapelist);
805 tapefile = stralloc2(config_dir, conf_tapelist);
808 * XXX There Really Ought to be some error-checking here... dhw
810 tape_dir = stralloc(tapefile);
811 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
814 * else whine Really Loudly about a path with no slashes??!?
817 if(access(tape_dir, W_OK) == -1) {
818 fprintf(outf, "ERROR: tapelist dir %s: not writable.\n", tape_dir);
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));
826 else if(access(tapefile, F_OK) != 0) {
827 fprintf(outf, "ERROR: can't access tape list %s\n", tapefile);
829 } else if(access(tapefile, F_OK) == 0 && access(tapefile, W_OK) != 0) {
830 fprintf(outf, "ERROR: tape list %s: not writable\n", tapefile);
832 } else if(read_tapelist(tapefile)) {
833 fprintf(outf, "ERROR: tape list %s: parse error\n", tapefile);
836 holdfile = vstralloc(config_dir, "/", "hold", NULL);
837 if(access(holdfile, F_OK) != -1) {
838 fprintf(outf, "WARNING: hold file %s exists\n", holdfile);
843 tapename = getconf_str(CNF_TAPEDEV);
844 if (strncmp(tapename, "null:", 5) == 0) {
846 "WARNING: tapedev is %s, dumps will be thrown away\n",
853 /* check available disk space */
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));
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));
867 else if(fs.avail == -1) {
869 "WARNING: holding disk %s: available space unknown (%ld KB requested)\n",
870 hdp->diskdir, (long)hdp->disksize);
873 else if(hdp->disksize > 0) {
874 if(fs.avail < hdp->disksize) {
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);
883 "Holding disk %s: %ld %sB disk space available, that's plenty\n",
884 hdp->diskdir, fs.avail/unitdivisor, displayunit);
887 if(fs.avail < -hdp->disksize) {
889 "WARNING: holding disk %s: only %ld %sB free, using nothing\n",
890 hdp->diskdir, fs.avail/unitdivisor, displayunit);
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);
902 /* check that the log file is writable if it already exists */
908 struct stat stat_old;
911 conf_logdir = getconf_str(CNF_LOGDIR);
912 if (*conf_logdir == '/') {
913 conf_logdir = stralloc(conf_logdir);
915 conf_logdir = stralloc2(config_dir, conf_logdir);
917 logfile = vstralloc(conf_logdir, "/log", NULL);
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));
924 else if(access(conf_logdir, W_OK) == -1) {
925 fprintf(outf, "ERROR: log dir %s: not writable\n", conf_logdir);
929 if(access(logfile, F_OK) == 0) {
932 if(access(logfile, W_OK) != 0)
933 fprintf(outf, "ERROR: log file %s: not writable\n",
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);
942 if(access(olddir, W_OK) == -1) {
943 fprintf(outf, "ERROR: oldlog dir %s: not writable\n", olddir);
946 else if(lstat(olddir,&stat_old) == 0) {
947 fprintf(outf, "ERROR: oldlog directory \"%s\" is not a directory\n", olddir);
951 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
952 if (access(logfile, F_OK) == 0) {
963 /* check that the tape is a valid amanda tape */
965 tapedays = getconf_int(CNF_TAPECYCLE);
966 labelstr = getconf_str(CNF_LABELSTR);
967 tapename = getconf_str(CNF_TAPEDEV);
969 if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
971 "WARNING: if a tape changer is not available, runtapes must be set to 1\n");
974 if(changer_init() && (tapename = taper_scan()) == NULL) {
975 fprintf(outf, "ERROR: %s\n", changer_resultstr);
977 } else if(tape_access(tapename,F_OK|R_OK|W_OK) == -1) {
978 fprintf(outf, "ERROR: %s: %s\n", tapename, strerror(errno));
980 } else if((errstr = tape_rdlabel(tapename, &datestamp, &label)) != NULL) {
981 fprintf(outf, "ERROR: %s: %s\n", tapename, errstr);
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",
990 tp = lookup_tapelabel(label);
992 fprintf(outf, "ERROR: label %s match labelstr but it not listed in the tapelist file.\n", label);
995 else if(tp != NULL && !reusable_tape(tp)) {
996 fprintf(outf, "ERROR: cannot overwrite active tape %s\n",
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");
1011 if(!tapebad && overwrite) {
1012 if((errstr = tape_writable(tapename)) != NULL) {
1014 "ERROR: tape %s label ok, but is not writable\n",
1018 else fprintf(outf, "Tape %s is writable\n", label);
1020 else fprintf(outf, "NOTE: skipping tape-writable test\n");
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");
1028 fprintf(outf, "NOTE: skipping tape checks\n");
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.
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;
1046 int indexdir_checked = 0;
1047 int hostindexdir_checked = 0;
1050 int conf_tapecycle, conf_runspercycle;
1052 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1053 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1055 if(conf_tapecycle <= conf_runspercycle) {
1056 fprintf(outf, "WARNING: tapecycle (%d) <= runspercycle (%d).\n",
1057 conf_tapecycle, conf_runspercycle);
1060 conf_infofile = stralloc(getconf_str(CNF_INFOFILE));
1061 if (*conf_infofile != '/') {
1062 char *ci = stralloc2(config_dir, conf_infofile);
1063 amfree(conf_infofile);
1066 conf_indexdir = stralloc(getconf_str(CNF_INDEXDIR));
1067 if (*conf_indexdir != '/') {
1068 char *ci = stralloc2(config_dir, conf_indexdir);
1069 amfree(conf_indexdir);
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);
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);
1086 strappend(conf_infofile, "/");
1089 while(!empty(*origqp)) {
1090 hostp = origqp->head->host;
1091 host = sanitise_filename(hostp->hostname);
1094 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1095 if(stat(hostinfodir, &statbuf) == -1) {
1096 fprintf(outf, "NOTE: info dir %s: does not exist\n",
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",
1103 amfree(hostinfodir);
1105 } else if (access(hostinfodir, W_OK) == -1) {
1106 fprintf(outf, "ERROR: info dir %s: not writable\n",
1108 amfree(hostinfodir);
1111 strappend(hostinfodir, "/");
1115 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1116 disk = sanitise_filename(dp->name);
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",
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",
1129 } else if (access(diskdir, W_OK) == -1) {
1130 fprintf(outf, "ERROR: info dir %s: not writable\n",
1133 } else if(stat(infofile, &statbuf) == -1) {
1134 fprintf(outf, "WARNING: info file %s: does not exist\n",
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",
1141 } else if (access(infofile, R_OK) == -1) {
1142 fprintf(outf, "ERROR: info file %s: not readable\n",
1150 if(! indexdir_checked) {
1151 if(stat(conf_indexdir, &statbuf) == -1) {
1152 fprintf(outf, "NOTE: index dir %s: does not exist\n",
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",
1159 amfree(conf_indexdir);
1161 } else if (access(conf_indexdir, W_OK) == -1) {
1162 fprintf(outf, "ERROR: index dir %s: not writable\n",
1164 amfree(conf_indexdir);
1167 strappend(conf_indexdir, "/");
1169 indexdir_checked = 1;
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",
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",
1182 amfree(hostindexdir);
1184 } else if (access(hostindexdir, W_OK) == -1) {
1185 fprintf(outf, "ERROR: index dir %s: not writable\n",
1187 amfree(hostindexdir);
1190 strappend(hostindexdir, "/");
1192 hostindexdir_checked = 1;
1195 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1196 if(stat(diskdir, &statbuf) == -1) {
1197 fprintf(outf, "NOTE: index dir %s: does not exist\n",
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",
1204 } else if (access(diskdir, W_OK) == -1) {
1205 fprintf(outf, "ERROR: index dir %s: is not writable\n",
1213 remove_disk(origqp, dp);
1216 amfree(hostindexdir);
1217 hostindexdir_checked = 0;
1220 amfree(hostinfodir);
1221 amfree(conf_infofile);
1222 amfree(conf_indexdir);
1228 amfree(config_name);
1230 fprintf(outf, "Server check took %s seconds\n", walltime_str(curclock()));
1234 malloc_size_2 = malloc_inuse(&malloc_hist_2);
1236 if(malloc_size_1 != malloc_size_2) {
1237 malloc_list(fd, malloc_hist_1, malloc_hist_2);
1252 /* --------------------------------------------------- */
1258 #ifdef KRB4_SECURITY
1262 static void handle_response P((proto_t *p, pkt_t *pkt));
1264 #define HOST_READY ((void *)0) /* must be 0 */
1265 #define HOST_ACTIVE ((void *)1)
1266 #define HOST_DONE ((void *)2)
1268 #define DISK_READY ((void *)0) /* must be 0 */
1269 #define DISK_ACTIVE ((void *)1)
1270 #define DISK_DONE ((void *)2)
1272 int start_host(hostp)
1280 char number[NUM_STR_SIZE];
1282 if(hostp->up != HOST_READY) {
1286 if (strncmp (hostp->hostname,"localhost",9) == 0) {
1288 "WARNING: Usage of fully qualified hostname recommended for Client %s.\n",
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).
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);
1308 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1309 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1311 "ERROR: Client %s does not support selfcheck REQ packet.\n",
1314 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1316 "ERROR: Client %s does not support selfcheck REP packet.\n",
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)) {
1323 "ERROR: Client %s does not support sendsize REQ packet.\n",
1326 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1328 "ERROR: Client %s does not support sendsize REP packet.\n",
1331 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1332 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1334 "ERROR: Client %s does not support sendbackup REQ packet.\n",
1337 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1339 "ERROR: Client %s does not support sendbackup REP packet.\n",
1343 ap_snprintf(number, sizeof(number), "%d", hostp->maxdumps);
1344 req = vstralloc("SERVICE ", "selfcheck", "\n",
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 ? ";" : "",
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) {
1367 if(dp->todo == 0) continue;
1369 if(dp->up != DISK_READY) {
1372 o = optionstr(dp, hostp->features, outf);
1375 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1377 "ERROR: %s:%s (%s): selfcheck does not support device.\n",
1378 hostp->hostname, dp->name, dp->device);
1380 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1382 "ERROR: %s:%s (%s): sendsize does not support device.\n",
1383 hostp->hostname, dp->name, dp->device);
1385 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1387 "ERROR: %s:%s (%s): sendbackup does not support device.\n",
1388 hostp->hostname, dp->name, dp->device);
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);
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);
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;
1408 if(dp->estimate == ES_CALCSIZE &&
1409 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1410 calcsize = "CALCSIZE ";
1414 l = vstralloc(calcsize,
1417 dp->device ? dp->device : "",
1425 * Allow 2X for error response in return packet.
1427 if(req_len + l_len > MAX_DGRAM / 2) {
1434 dp->up = DISK_ACTIVE;
1439 else { /* noop service */
1440 req = vstralloc("SERVICE ", "noop", "\n",
1442 "features=", our_feature_string, ";",
1445 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1446 if(dp->todo == 0) continue;
1448 if(dp->up != DISK_READY) {
1454 if(disk_count == 0) {
1456 hostp->up = HOST_DONE;
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);
1466 rc = make_request(hostp->hostname, amanda_port, req,
1467 hostp, conf_ctimeout, handle_response);
1469 req = NULL; /* do not own this any more */
1472 /* couldn't resolve hostname */
1474 "ERROR: %s: could not resolve hostname\n", hostp->hostname);
1476 hostp->up = HOST_DONE;
1478 hostp->up = HOST_ACTIVE;
1483 int start_client_checks(fd)
1489 struct servent *amandad;
1492 switch(pid = fork()) {
1493 case -1: error("could not fork client check: %s", strerror(errno));
1502 set_pname("amcheck-clients");
1506 if((outf = fdopen(fd, "w")) == NULL)
1507 error("fdopen %d: %s", fd, strerror(errno));
1510 fprintf(outf, "\nAmanda Backup Client Hosts Check\n");
1511 fprintf(outf, "--------------------------------\n");
1513 #ifdef KRB4_SECURITY
1514 kerberos_service_init();
1517 proto_init(msg->socket, time(0), 1024);
1519 /* get remote service port */
1520 if((amandad = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL)
1521 amanda_port = AMANDA_SERVICE_DEFAULT;
1523 amanda_port = ntohs(amandad->s_port);
1525 #ifdef KRB4_SECURITY
1526 if((amandad = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)
1527 kamanda_port = KAMANDA_SERVICE_DEFAULT;
1529 kamanda_port = ntohs(amandad->s_port);
1532 hostcount = remote_errors = 0;
1534 for(dp = origqp->head; dp != NULL; dp = dp->next) {
1536 if(hostp->up == HOST_READY) {
1537 if(start_host(hostp) == 1) {
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");
1555 amfree(config_name);
1557 malloc_size_2 = malloc_inuse(&malloc_hist_2);
1559 if(malloc_size_1 != malloc_size_2) {
1560 malloc_list(fd, malloc_hist_1, malloc_hist_2);
1563 exit(userbad || remote_errors > 0);
1568 static void handle_response(p, pkt)
1580 hostp = (am_host_t *) p->datap;
1581 hostp->up = HOST_READY;
1583 if(p->state == S_FAILED && pkt == NULL) {
1584 if(p->prevstate == S_REPWAIT) {
1586 "WARNING: %s: selfcheck reply timed out.\n",
1591 "WARNING: %s: selfcheck request timed out. Host down?\n",
1595 hostp->up = HOST_DONE;
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",
1605 hostp->up = HOST_DONE;
1611 fprintf(errf, "got %sresponse from %s:\n----\n%s----\n\n",
1612 (p->state == S_FAILED) ? "NAK " : "", hostp->hostname, pkt->body);
1620 if (s[-2] == '\n') {
1624 #define sc "OPTIONS "
1625 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1628 #define sc "features="
1629 t = strstr(line, sc);
1630 if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
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);
1644 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1650 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1651 t = line + sizeof(sc)-1;
1655 skip_whitespace(t, tch);
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.
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)) {
1666 fprintf(outf, "ERROR: %s%s: %s\n",
1667 (p->state == S_FAILED) ? "NAK " : "",
1671 hostp->up = HOST_DONE;
1676 fprintf(outf, "ERROR: %s: unknown response: %s\n",
1677 hostp->hostname, line);
1679 hostp->up = HOST_DONE;
1681 if(hostp->up == HOST_READY && hostp->features == NULL) {
1683 * The client does not support the features list, so give it an
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();
1690 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1691 if(dp->up == DISK_ACTIVE) {