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.121 2006/02/06 22:17:09 ktill Exp $
29 * checks for common problems in server and clients
46 #include "taperscan.h"
47 #include "server_util.h"
48 #include "pipespawn.h"
49 #include "amfeatures.h"
51 #define BUFFER_SIZE 32768
53 static int conf_ctimeout;
56 static disklist_t origq;
58 static uid_t uid_dumpuser;
63 int start_client_checks P((int fd));
64 int start_server_check P((int fd, int do_localchk, int do_tapechk));
65 int main P((int argc, char **argv));
66 int test_server_pgm P((FILE *outf, char *dir, char *pgm,
67 int suid, uid_t dumpuid));
71 error("Usage: amcheck%s [-M <username>] [-mawsclt] <conf> [host [disk]* ]*", versionsuffix());
74 static unsigned long malloc_hist_1, malloc_size_1;
75 static unsigned long malloc_hist_2, malloc_size_2;
77 static am_feature_t *our_features = NULL;
78 static char *our_feature_string = NULL;
79 static char *displayunit;
80 static long int unitdivisor;
86 char buffer[BUFFER_SIZE];
88 char *mainfname = NULL;
89 char pid_str[NUM_STR_SIZE];
90 int do_clientchk, clientchk_pid, client_probs;
91 int do_localchk, do_tapechk, serverchk_pid, server_probs;
93 int opt, size, tempfd, mainfd;
101 char *tempfname = NULL;
111 set_pname("amcheck");
113 /* Don't die when child closes pipe */
114 signal(SIGPIPE, SIG_IGN);
118 malloc_size_1 = malloc_inuse(&malloc_hist_1);
120 snprintf(pid_str, sizeof(pid_str), "%ld", (long)getpid());
122 erroutput_type = ERR_INTERACTIVE;
124 our_features = am_init_feature_set();
125 our_feature_string = am_feature_to_string(our_features);
132 alwaysmail = mailout = overwrite = 0;
133 do_localchk = do_tapechk = do_clientchk = 0;
135 server_probs = client_probs = 0;
136 tempfd = mainfd = -1;
138 /* process arguments */
140 while((opt = getopt(argc, argv, "M:mawsclt")) != EOF) {
142 case 'M': mailto=stralloc(optarg);
147 printf("You can't use -%c because configure didn't find a mailer.\n",
157 printf("You can't use -%c because configure didn't find a mailer.\n",
162 case 's': do_localchk = 1; do_tapechk = 1;
165 case 'c': do_clientchk = 1;
168 case 'l': do_localchk = 1;
171 case 'w': do_tapechk = 1; overwrite = 1;
174 case 't': do_tapechk = 1;
182 argc -= optind, argv += optind;
184 do_localchk = do_clientchk = do_tapechk = 1;
187 if(argc < 1) usage();
189 config_name = stralloc(*argv);
191 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
192 conffile = stralloc2(config_dir, CONFFILE_NAME);
193 if(read_conffile(conffile)) {
194 error("errors processing config file \"%s\"", conffile);
198 conf_ctimeout = getconf_int(CNF_CTIMEOUT);
200 conf_diskfile = getconf_str(CNF_DISKFILE);
201 if (*conf_diskfile == '/') {
202 conf_diskfile = stralloc(conf_diskfile);
204 conf_diskfile = stralloc2(config_dir, conf_diskfile);
206 if(read_diskfile(conf_diskfile, &origq) < 0) {
207 error("could not load disklist %s", conf_diskfile);
209 match_disklist(&origq, argc-1, argv+1);
210 amfree(conf_diskfile);
213 * Make sure we are running as the dump user.
215 dumpuser = getconf_str(CNF_DUMPUSER);
216 if ((pw = getpwnam(dumpuser)) == NULL) {
217 error("cannot look up dump user \"%s\"", dumpuser);
219 uid_dumpuser = pw->pw_uid;
220 if ((pw = getpwuid(uid_me)) == NULL) {
221 error("cannot look up my own uid (%ld)", (long)uid_me);
223 if (uid_me != uid_dumpuser) {
224 error("running as user \"%s\" instead of \"%s\"",
229 displayunit = getconf_str(CNF_DISPLAYUNIT);
230 unitdivisor = getconf_unit_divisor();
233 * If both server and client side checks are being done, the server
234 * check output goes to the main output, while the client check output
235 * goes to a temporary file and is copied to the main output when done.
237 * If the output is to be mailed, the main output is also a disk file,
238 * otherwise it is stdout.
240 if(do_clientchk && (do_localchk || do_tapechk)) {
241 /* we need the temp file */
242 tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
243 if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1)
244 error("could not open %s: %s", tempfname, strerror(errno));
245 unlink(tempfname); /* so it goes away on close */
250 /* the main fd is a file too */
251 mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
252 if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1)
253 error("could not open %s: %s", mainfname, strerror(errno));
254 unlink(mainfname); /* so it goes away on close */
258 /* just use stdout */
261 /* start server side checks */
263 if(do_localchk || do_tapechk)
264 serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
268 /* start client side checks */
271 clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
276 /* wait for child processes and note any problems */
279 if((pid = wait(&retstat)) == -1) {
280 if(errno == EINTR) continue;
282 } else if(pid == clientchk_pid) {
283 client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
285 } else if(pid == serverchk_pid) {
286 server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
289 char number[NUM_STR_SIZE];
290 char *wait_msg = NULL;
292 snprintf(number, sizeof(number), "%ld", (long)pid);
293 wait_msg = vstralloc("parent: reaped bogus pid ", number, "\n",
295 if (fullwrite(mainfd, wait_msg, strlen(wait_msg)) < 0)
296 error("write main file: %s", strerror(errno));
301 /* copy temp output to main output and write tagline */
303 if(do_clientchk && (do_localchk || do_tapechk)) {
304 if(lseek(tempfd, 0, 0) == -1)
305 error("seek temp file: %s", strerror(errno));
307 while((size=fullread(tempfd, buffer, sizeof(buffer))) > 0) {
308 if (fullwrite(mainfd, buffer, size) < 0)
309 error("write main file: %s", strerror(errno));
312 error("read temp file: %s", strerror(errno));
316 version_string = vstralloc("\n",
317 "(brought to you by Amanda ", version(), ")\n",
319 if (fullwrite(mainfd, version_string, strlen(version_string)) < 0)
320 error("write main file: %s", strerror(errno));
321 amfree(version_string);
324 amfree(our_feature_string);
325 am_release_feature_set(our_features);
328 malloc_size_2 = malloc_inuse(&malloc_hist_2);
330 if(malloc_size_1 != malloc_size_2) {
331 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
334 /* send mail if requested, but only if there were problems */
337 #define MAILTO_LIMIT 10
339 if((server_probs || client_probs || alwaysmail) && mailout) {
352 char *extra_info = NULL;
357 char number[NUM_STR_SIZE];
360 if(lseek(mainfd, (off_t)0, SEEK_SET) == -1) {
361 error("lseek main file: %s", strerror(errno));
363 if(alwaysmail && !(server_probs || client_probs)) {
364 subject = stralloc2(getconf_str(CNF_ORG),
365 " AMCHECK REPORT: NO PROBLEMS FOUND");
367 subject = stralloc2(getconf_str(CNF_ORG),
368 " AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE");
371 * Variable arg lists are hard to deal with when we do not know
372 * ourself how many args are involved. Split the address list
373 * and hope there are not more than 9 entries.
375 * Remember that split() returns the original input string in
376 * argv[0], so we have to skip over that.
378 a = (char **) alloc((MAILTO_LIMIT + 1) * sizeof(char *));
379 memset(a, 0, (MAILTO_LIMIT + 1) * sizeof(char *));
384 r = split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " ");
387 if((nullfd = open("/dev/null", O_RDWR)) < 0) {
388 error("nullfd: /dev/null: %s", strerror(errno));
390 mailpid = pipespawn(MAILER, STDIN_PIPE | STDERR_PIPE,
391 &mailfd, &nullfd, &errfd,
394 a[1], a[2], a[3], a[4],
395 a[5], a[6], a[7], a[8], a[9],
399 * There is the potential for a deadlock here since we are writing
400 * to the process and then reading stderr, but in the normal case,
401 * nothing should be coming back to us, and hopefully in error
402 * cases, the pipe will break and we will exit out of the loop.
404 signal(SIGPIPE, SIG_IGN);
405 while((r = fullread(mainfd, buffer, sizeof(buffer))) > 0) {
406 if((w = fullwrite(mailfd, buffer, r)) != r) {
407 if(w < 0 && errno == EPIPE) {
408 strappend(extra_info, "EPIPE writing to mail process\n");
411 error("mailfd write: %s", strerror(errno));
413 error("mailfd write: wrote %d instead of %d", w, r);
418 ferr = fdopen(errfd, "r");
419 for(; (line = agets(ferr)) != NULL; free(line)) {
420 strappend(extra_info, line);
421 strappend(extra_info, "\n");
426 while ((wpid = wait(&retstat)) != -1) {
427 if (WIFSIGNALED(retstat)) {
429 rc = sig = WTERMSIG(retstat);
432 rc = ret = WEXITSTATUS(retstat);
436 strappend(err, "got signal ");
439 strappend(err, "returned ");
441 snprintf(number, sizeof(number), "%d", ret);
442 strappend(err, number);
447 fputs(extra_info, stderr);
450 error("error running mailer %s: %s", MAILER, err);
455 return (server_probs || client_probs);
458 /* --------------------------------------------------- */
460 int nslots, backwards, found, got_match, tapedays;
462 char *first_match_label = NULL, *first_match = NULL, *found_device = NULL;
464 char *searchlabel, *labelstr;
468 int test_server_pgm(outf, dir, pgm, suid, dumpuid)
478 pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL);
479 if(stat(pgm, &statbuf) == -1) {
480 fprintf(outf, "ERROR: program %s: does not exist\n",
483 } else if (!S_ISREG(statbuf.st_mode)) {
484 fprintf(outf, "ERROR: program %s: not a file\n",
487 } else if (access(pgm, X_OK) == -1) {
488 fprintf(outf, "ERROR: program %s: not executable\n",
493 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
494 fprintf(outf, "WARNING: program %s: not setuid-root\n",
501 int start_server_check(fd, do_localchk, do_tapechk)
503 int do_localchk, do_tapechk;
506 generic_fs_stats_t fs;
510 int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
511 int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
512 int testtape = do_tapechk;
513 tapetype_t *tp = NULL;
515 switch(pid = fork()) {
516 case -1: error("could not fork server check: %s", strerror(errno));
525 set_pname("amcheck-server");
529 if((outf = fdopen(fd, "w")) == NULL)
530 error("fdopen %d: %s", fd, strerror(errno));
533 fprintf(outf, "Amanda Tape Server Host Check\n");
534 fprintf(outf, "-----------------------------\n");
536 if (do_localchk || testtape) {
537 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
541 * Check various server side config file settings.
548 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
549 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
550 fprintf(outf, "ERROR: %s\n", errstr);
554 lbl_templ = tp->lbl_templ;
555 if(strcmp(lbl_templ, "") != 0) {
556 if(strchr(lbl_templ, '/') == NULL) {
557 lbl_templ = stralloc2(config_dir, lbl_templ);
559 lbl_templ = stralloc(lbl_templ);
561 if(access(lbl_templ, R_OK) == -1) {
563 "ERROR: cannot access lbl_templ file %s: %s\n",
569 fprintf(outf, "ERROR: lbl_templ set but no LPRCMD defined, you should reconfigure amanda\n and make sure it find a lpr or lp command.\n");
576 * Look up the programs used on the server side.
583 if(access(libexecdir, X_OK) == -1) {
584 fprintf(outf, "ERROR: program dir %s: not accessible\n",
589 || test_server_pgm(outf, libexecdir, "planner",
590 suid_check, uid_dumpuser);
592 || test_server_pgm(outf, libexecdir, "dumper",
593 suid_check, uid_dumpuser);
595 || test_server_pgm(outf, libexecdir, "driver",
598 || test_server_pgm(outf, libexecdir, "taper",
601 || test_server_pgm(outf, libexecdir, "amtrmidx",
604 || test_server_pgm(outf, libexecdir, "amlogroll",
607 if(access(sbindir, X_OK) == -1) {
608 fprintf(outf, "ERROR: program dir %s: not accessible\n",
613 || test_server_pgm(outf, sbindir, "amgetconf",
616 || test_server_pgm(outf, sbindir, "amcheck",
619 || test_server_pgm(outf, sbindir, "amdump",
622 || test_server_pgm(outf, sbindir, "amreport",
625 if(access(COMPRESS_PATH, X_OK) == -1) {
626 fprintf(outf, "WARNING: %s is not executable, server-compression and indexing will not work\n",
632 * Check that the directory for the tapelist file is writable, as well
633 * as the tapelist file itself (if it already exists). Also, check for
634 * a "hold" file (just because it is convenient to do it here) and warn
635 * if tapedev is set to the null device.
638 if(do_localchk || do_tapechk) {
646 conf_tapelist=getconf_str(CNF_TAPELIST);
647 if (*conf_tapelist == '/') {
648 tapefile = stralloc(conf_tapelist);
650 tapefile = stralloc2(config_dir, conf_tapelist);
653 * XXX There Really Ought to be some error-checking here... dhw
655 tape_dir = stralloc(tapefile);
656 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
659 * else whine Really Loudly about a path with no slashes??!?
662 if(access(tape_dir, W_OK) == -1) {
663 fprintf(outf, "ERROR: tapelist dir %s: not writable.\n", tape_dir);
666 else if(stat(tapefile, &statbuf) == -1) {
667 fprintf(outf, "ERROR: tapefile %s: %s, you must create an empty file.\n",
668 tapefile, strerror(errno));
671 else if(!S_ISREG(statbuf.st_mode)) {
672 fprintf(outf, "ERROR: tapefile %s: should be a regular file.\n", tapefile);
675 else if(access(tapefile, F_OK) != 0) {
676 fprintf(outf, "ERROR: can't access tape list %s\n", tapefile);
678 } else if(access(tapefile, F_OK) == 0 && access(tapefile, W_OK) != 0) {
679 fprintf(outf, "ERROR: tape list %s: not writable\n", tapefile);
681 } else if(read_tapelist(tapefile)) {
682 fprintf(outf, "ERROR: tape list %s: parse error\n", tapefile);
685 holdfile = vstralloc(config_dir, "/", "hold", NULL);
686 if(access(holdfile, F_OK) != -1) {
687 fprintf(outf, "WARNING: hold file %s exists\n", holdfile);
692 tapename = getconf_str(CNF_TAPEDEV);
693 if (strncmp(tapename, "null:", 5) == 0) {
695 "WARNING: tapedev is %s, dumps will be thrown away\n",
702 /* check available disk space */
705 for(hdp = holdingdisks; hdp != NULL; hdp = hdp->next) {
706 if(get_fs_stats(hdp->diskdir, &fs) == -1) {
707 fprintf(outf, "ERROR: holding dir %s: %s, you must create a directory.\n",
708 hdp->diskdir, strerror(errno));
711 else if(access(hdp->diskdir, W_OK) == -1) {
712 fprintf(outf, "ERROR: holding disk %s: not writable: %s.\n",
713 hdp->diskdir, strerror(errno));
716 else if(fs.avail == -1) {
718 "WARNING: holding disk %s: available space unknown (%ld KB requested)\n",
719 hdp->diskdir, (long)hdp->disksize);
722 else if(hdp->disksize > 0) {
723 if(fs.avail < hdp->disksize) {
725 "WARNING: holding disk %s: only %ld %sB free (%ld %sB requested)\n",
726 hdp->diskdir, (long)fs.avail/unitdivisor, displayunit,
727 (long)hdp->disksize/unitdivisor, displayunit);
732 "Holding disk %s: %ld %sB disk space available, that's plenty\n",
733 hdp->diskdir, fs.avail/unitdivisor, displayunit);
736 if(fs.avail < -hdp->disksize) {
738 "WARNING: holding disk %s: only %ld %sB free, using nothing\n",
739 hdp->diskdir, fs.avail/unitdivisor, displayunit);
744 "Holding disk %s: %ld %sB disk space available, using %ld %sB\n",
745 hdp->diskdir, fs.avail/unitdivisor, displayunit,
746 (fs.avail + hdp->disksize)/unitdivisor, displayunit);
751 /* check that the log file is writable if it already exists */
757 struct stat stat_old;
760 conf_logdir = getconf_str(CNF_LOGDIR);
761 if (*conf_logdir == '/') {
762 conf_logdir = stralloc(conf_logdir);
764 conf_logdir = stralloc2(config_dir, conf_logdir);
766 logfile = vstralloc(conf_logdir, "/log", NULL);
768 if(stat(conf_logdir, &statbuf) == -1) {
769 fprintf(outf, "ERROR: logdir %s: %s, you must create a directory.\n",
770 conf_logdir, strerror(errno));
773 else if(access(conf_logdir, W_OK) == -1) {
774 fprintf(outf, "ERROR: log dir %s: not writable\n", conf_logdir);
778 if(access(logfile, F_OK) == 0) {
781 if(access(logfile, W_OK) != 0)
782 fprintf(outf, "ERROR: log file %s: not writable\n",
786 olddir = vstralloc(conf_logdir, "/oldlog", NULL);
787 if (stat(olddir,&stat_old) == 0) { /* oldlog exist */
788 if(!(S_ISDIR(stat_old.st_mode))) {
789 fprintf(outf, "ERROR: oldlog directory \"%s\" is not a directory\n", olddir);
791 if(access(olddir, W_OK) == -1) {
792 fprintf(outf, "ERROR: oldlog dir %s: not writable\n", olddir);
795 else if(lstat(olddir,&stat_old) == 0) {
796 fprintf(outf, "ERROR: oldlog directory \"%s\" is not a directory\n", olddir);
800 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
801 if (access(logfile, F_OK) == 0) {
812 /* check that the tape is a valid amanda tape */
816 tapedays = getconf_int(CNF_TAPECYCLE);
817 labelstr = getconf_str(CNF_LABELSTR);
818 tapename = getconf_str(CNF_TAPEDEV);
820 if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
822 "WARNING: if a tape changer is not available, runtapes must be set to 1\n");
825 tape_status = taper_scan(NULL, &label, &datestamp, &error_msg,
827 fprintf(outf, "%s\n", error_msg);
829 if (tape_status < 0) {
830 tape_t *exptape = lookup_last_reusable_tape(0);
831 fprintf(outf, " (expecting ");
832 if(exptape != NULL) fprintf(outf, "tape %s or ", exptape->label);
833 fprintf(outf, "a new tape)\n");
836 char *wrlabel_status;
837 wrlabel_status = tape_wrlabel(tapename, "X", label,
838 tp->blocksize * 1024);
839 if (wrlabel_status != NULL) {
840 if (tape_status == 3) {
842 "ERROR: Could not label brand new tape: %s\n",
846 "ERROR: tape %s label ok, but is not writable (%s)\n",
847 label, wrlabel_status);
851 if (tape_status != 3) {
852 fprintf(outf, "Tape %s is writable; rewrote label.\n", label);
854 fprintf(outf, "Wrote label %s to brand new tape.\n", label);
858 fprintf(outf, "NOTE: skipping tape-writable test\n");
859 if (tape_status == 3) {
861 "Found a brand new tape, will label it %s.\n",
864 fprintf(outf, "Tape %s label ok\n", label);
868 } else if (do_tapechk) {
869 fprintf(outf, "WARNING: skipping tape test because amdump or amflush seem to be running\n");
870 fprintf(outf, "WARNING: if they are not, you must run amcleanup\n");
872 fprintf(outf, "NOTE: skipping tape checks\n");
876 * See if the information file and index directory for each client
877 * and disk is OK. Since we may be seeing clients and/or disks for
878 * the first time, these are just warnings, not errors.
883 char *hostinfodir = NULL;
884 char *hostindexdir = NULL;
885 char *diskdir = NULL;
886 char *infofile = NULL;
890 int indexdir_checked = 0;
891 int hostindexdir_checked = 0;
894 int conf_tapecycle, conf_runspercycle;
896 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
897 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
899 if(conf_tapecycle <= conf_runspercycle) {
900 fprintf(outf, "WARNING: tapecycle (%d) <= runspercycle (%d).\n",
901 conf_tapecycle, conf_runspercycle);
904 conf_infofile = getconf_str(CNF_INFOFILE);
905 if (*conf_infofile == '/') {
906 conf_infofile = stralloc(conf_infofile);
908 conf_infofile = stralloc2(config_dir, conf_infofile);
911 conf_indexdir = getconf_str(CNF_INDEXDIR);
912 if (*conf_indexdir == '/') {
913 conf_indexdir = stralloc(conf_indexdir);
915 conf_indexdir = stralloc2(config_dir, conf_indexdir);
919 if(stat(conf_infofile, &statbuf) == -1) {
920 fprintf(outf, "NOTE: info dir %s: does not exist\n", conf_infofile);
921 fprintf(outf, "NOTE: it will be created on the next run.\n");
922 amfree(conf_infofile);
923 } else if (!S_ISDIR(statbuf.st_mode)) {
924 fprintf(outf, "ERROR: info dir %s: not a directory\n", conf_infofile);
925 amfree(conf_infofile);
927 } else if (access(conf_infofile, W_OK) == -1) {
928 fprintf(outf, "ERROR: info dir %s: not writable\n", conf_infofile);
929 amfree(conf_infofile);
932 strappend(conf_infofile, "/");
936 while(!empty(origq)) {
937 hostp = origq.head->host;
938 host = sanitise_filename(hostp->hostname);
941 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
942 if(stat(hostinfodir, &statbuf) == -1) {
943 fprintf(outf, "NOTE: info dir %s: does not exist\n",
945 fprintf(outf, "NOTE: it will be created on the next run.\n");
947 } else if (!S_ISDIR(statbuf.st_mode)) {
948 fprintf(outf, "ERROR: info dir %s: not a directory\n",
952 } else if (access(hostinfodir, W_OK) == -1) {
953 fprintf(outf, "ERROR: info dir %s: not writable\n",
958 strappend(hostinfodir, "/");
962 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
963 disk = sanitise_filename(dp->name);
966 diskdir = newstralloc2(diskdir, hostinfodir, disk);
967 infofile = vstralloc(diskdir, "/", "info", NULL);
968 if(stat(diskdir, &statbuf) == -1) {
969 fprintf(outf, "NOTE: info dir %s: does not exist\n",
971 fprintf(outf, "NOTE: it will be created on the next run.\n");
972 } else if (!S_ISDIR(statbuf.st_mode)) {
973 fprintf(outf, "ERROR: info dir %s: not a directory\n",
976 } else if (access(diskdir, W_OK) == -1) {
977 fprintf(outf, "ERROR: info dir %s: not writable\n",
980 } else if(stat(infofile, &statbuf) == -1) {
981 fprintf(outf, "WARNING: info file %s: does not exist\n",
983 fprintf(outf, "NOTE: it will be created on the next run.\n");
984 } else if (!S_ISREG(statbuf.st_mode)) {
985 fprintf(outf, "ERROR: info file %s: not a file\n",
988 } else if (access(infofile, R_OK) == -1) {
989 fprintf(outf, "ERROR: info file %s: not readable\n",
997 if(! indexdir_checked) {
998 if(stat(conf_indexdir, &statbuf) == -1) {
999 fprintf(outf, "NOTE: index dir %s: does not exist\n",
1001 fprintf(outf, "NOTE: it will be created on the next run.\n");
1002 amfree(conf_indexdir);
1003 } else if (!S_ISDIR(statbuf.st_mode)) {
1004 fprintf(outf, "ERROR: index dir %s: not a directory\n",
1006 amfree(conf_indexdir);
1008 } else if (access(conf_indexdir, W_OK) == -1) {
1009 fprintf(outf, "ERROR: index dir %s: not writable\n",
1011 amfree(conf_indexdir);
1014 strappend(conf_indexdir, "/");
1016 indexdir_checked = 1;
1019 if(! hostindexdir_checked) {
1020 hostindexdir = stralloc2(conf_indexdir, host);
1021 if(stat(hostindexdir, &statbuf) == -1) {
1022 fprintf(outf, "NOTE: index dir %s: does not exist\n",
1024 fprintf(outf, "NOTE: it will be created on the next run.\n");
1025 amfree(hostindexdir);
1026 } else if (!S_ISDIR(statbuf.st_mode)) {
1027 fprintf(outf, "ERROR: index dir %s: not a directory\n",
1029 amfree(hostindexdir);
1031 } else if (access(hostindexdir, W_OK) == -1) {
1032 fprintf(outf, "ERROR: index dir %s: not writable\n",
1034 amfree(hostindexdir);
1037 strappend(hostindexdir, "/");
1039 hostindexdir_checked = 1;
1042 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1043 if(stat(diskdir, &statbuf) == -1) {
1044 fprintf(outf, "NOTE: index dir %s: does not exist\n",
1046 fprintf(outf, "NOTE: it will be created on the next run.\n");
1047 } else if (!S_ISDIR(statbuf.st_mode)) {
1048 fprintf(outf, "ERROR: index dir %s: not a directory\n",
1051 } else if (access(diskdir, W_OK) == -1) {
1052 fprintf(outf, "ERROR: index dir %s: is not writable\n",
1060 if ( dp->encrypt == ENCRYPT_SERV_CUST && dp->srv_encrypt ) {
1061 if(access(dp->srv_encrypt, X_OK) == -1) {
1062 fprintf(outf, "ERROR: %s is not executable, server encryption will not work\n",
1067 if ( dp->compress == COMP_SERV_CUST && dp->srvcompprog ) {
1068 if(access(dp->srvcompprog, X_OK) == -1) {
1069 fprintf(outf, "ERROR: %s is not executable, server custom compression will not work\n",
1076 remove_disk(&origq, dp);
1079 amfree(hostindexdir);
1080 hostindexdir_checked = 0;
1083 amfree(hostinfodir);
1084 amfree(conf_infofile);
1085 amfree(conf_indexdir);
1091 amfree(config_name);
1093 fprintf(outf, "Server check took %s seconds\n", walltime_str(curclock()));
1097 malloc_size_2 = malloc_inuse(&malloc_hist_2);
1099 if(malloc_size_1 != malloc_size_2) {
1100 malloc_list(fd, malloc_hist_1, malloc_hist_2);
1115 /* --------------------------------------------------- */
1120 static void handle_result P((void *, pkt_t *, security_handle_t *));
1122 #define HOST_READY ((void *)0) /* must be 0 */
1123 #define HOST_ACTIVE ((void *)1)
1124 #define HOST_DONE ((void *)2)
1126 #define DISK_READY ((void *)0) /* must be 0 */
1127 #define DISK_ACTIVE ((void *)1)
1128 #define DISK_DONE ((void *)2)
1130 void start_host(hostp)
1137 const security_driver_t *secdrv;
1138 char number[NUM_STR_SIZE];
1140 if(hostp->up != HOST_READY) {
1144 if (strncmp (hostp->hostname,"localhost",9) == 0) {
1146 "WARNING: Usage of fully qualified hostname recommended for Client %s.\n",
1151 * The first time through here we send a "noop" request. This will
1152 * return the feature list from the client if it supports that.
1153 * If it does not, handle_result() will set the feature list to an
1154 * empty structure. In either case, we do the disks on the second
1155 * (and subsequent) pass(es).
1158 if(hostp->features != NULL) { /* selfcheck service */
1159 int has_features = am_has_feature(hostp->features,
1160 fe_req_options_features);
1161 int has_hostname = am_has_feature(hostp->features,
1162 fe_req_options_hostname);
1163 int has_maxdumps = am_has_feature(hostp->features,
1164 fe_req_options_maxdumps);
1166 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1167 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1169 "ERROR: Client %s does not support selfcheck REQ packet.\n",
1172 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1174 "ERROR: Client %s does not support selfcheck REP packet.\n",
1177 if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1178 !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1179 !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1181 "ERROR: Client %s does not support sendsize REQ packet.\n",
1184 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1186 "ERROR: Client %s does not support sendsize REP packet.\n",
1189 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1190 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1192 "ERROR: Client %s does not support sendbackup REQ packet.\n",
1195 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1197 "ERROR: Client %s does not support sendbackup REP packet.\n",
1201 snprintf(number, sizeof(number), "%d", hostp->maxdumps);
1202 req = vstralloc("SERVICE ", "selfcheck", "\n",
1204 has_features ? "features=" : "",
1205 has_features ? our_feature_string : "",
1206 has_features ? ";" : "",
1207 has_maxdumps ? "maxdumps=" : "",
1208 has_maxdumps ? number : "",
1209 has_maxdumps ? ";" : "",
1210 has_hostname ? "hostname=" : "",
1211 has_hostname ? hostp->hostname : "",
1212 has_hostname ? ";" : "",
1216 req_len = strlen(req);
1217 req_len += 128; /* room for SECURITY ... */
1218 req_len += 256; /* room for non-disk answers */
1219 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1225 if(dp->up != DISK_READY || dp->todo != 1) {
1228 o = optionstr(dp, hostp->features, outf);
1231 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1233 "ERROR: %s:%s (%s): selfcheck does not support device.\n",
1234 hostp->hostname, dp->name, dp->device);
1236 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1238 "ERROR: %s:%s (%s): sendsize does not support device.\n",
1239 hostp->hostname, dp->name, dp->device);
1241 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1243 "ERROR: %s:%s (%s): sendbackup does not support device.\n",
1244 hostp->hostname, dp->name, dp->device);
1247 if(strncmp(dp->program,"DUMP",4) == 0 ||
1248 strncmp(dp->program,"GNUTAR",6) == 0) {
1249 if(strcmp(dp->program, "DUMP") == 0 &&
1250 !am_has_feature(hostp->features, fe_program_dump)) {
1251 fprintf(outf, "ERROR: %s:%s does not support DUMP.\n",
1252 hostp->hostname, dp->name);
1254 if(strcmp(dp->program, "GNUTAR") == 0 &&
1255 !am_has_feature(hostp->features, fe_program_gnutar)) {
1256 fprintf(outf, "ERROR: %s:%s does not support GNUTAR.\n",
1257 hostp->hostname, dp->name);
1259 if(dp->estimate == ES_CALCSIZE &&
1260 !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1261 fprintf(outf, "ERROR: %s:%s does not support CALCSIZE for estimate, using CLIENT.\n",
1262 hostp->hostname, dp->name);
1263 dp->estimate = ES_CLIENT;
1265 if(dp->estimate == ES_CALCSIZE &&
1266 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1267 calcsize = "CALCSIZE ";
1271 if(dp->compress == COMP_CUST &&
1272 !am_has_feature(hostp->features, fe_options_compress_cust)) {
1274 "ERROR: Client %s does not support custom compression.\n",
1277 if(dp->encrypt == ENCRYPT_CUST ) {
1278 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1280 "ERROR: Client %s does not support data encryption.\n",
1283 } else if ( dp->compress == COMP_SERV_FAST ||
1284 dp->compress == COMP_SERV_BEST ||
1285 dp->compress == COMP_SERV_CUST ) {
1287 "ERROR: %s: Client encryption with server compression is not supported. See amanda.conf(5) for detail.\n", hostp->hostname);
1292 l = vstralloc(calcsize,
1302 l = vstralloc(calcsize,
1311 if(!am_has_feature(hostp->features, fe_program_dumper_api)) {
1312 fprintf(outf, "ERROR: %s:%s does not support DUMPER-API.\n",
1313 hostp->hostname, dp->name);
1315 l = vstralloc("DUMPER ",
1329 * Allow 2X for err response.
1331 if(req_len + l_len > MAX_PACKET / 2) {
1338 dp->up = DISK_ACTIVE;
1342 else { /* noop service */
1343 req = vstralloc("SERVICE ", "noop", "\n",
1345 "features=", our_feature_string, ";",
1348 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1349 if(dp->up != DISK_READY || dp->todo != 1) {
1356 if(disk_count == 0) {
1358 hostp->up = HOST_DONE;
1362 secdrv = security_getdriver(hostp->disks->security_driver);
1363 if (secdrv == NULL) {
1364 error("could not find security driver '%s' for host '%s'",
1365 hostp->disks->security_driver, hostp->hostname);
1367 protocol_sendreq(hostp->hostname, secdrv, generic_get_security_conf,
1368 req, conf_ctimeout, handle_result, hostp);
1372 hostp->up = HOST_ACTIVE;
1375 int start_client_checks(fd)
1383 switch(pid = fork()) {
1384 case -1: error("could not fork client check: %s", strerror(errno));
1393 set_pname("amcheck-clients");
1397 if((outf = fdopen(fd, "w")) == NULL)
1398 error("fdopen %d: %s", fd, strerror(errno));
1401 fprintf(outf, "\nAmanda Backup Client Hosts Check\n");
1402 fprintf(outf, "--------------------------------\n");
1406 hostcount = remote_errors = 0;
1408 for(dp = origq.head; dp != NULL; dp = dp->next) {
1410 if(hostp->up == HOST_READY) {
1420 "Client check: %d host%s checked in %s seconds, %d problem%s found\n",
1421 hostcount, (hostcount == 1) ? "" : "s",
1422 walltime_str(curclock()),
1423 remote_errors, (remote_errors == 1) ? "" : "s");
1427 amfree(config_name);
1429 malloc_size_2 = malloc_inuse(&malloc_hist_2);
1431 if(malloc_size_1 != malloc_size_2) {
1432 malloc_list(fd, malloc_hist_1, malloc_hist_2);
1435 exit(userbad || remote_errors > 0);
1440 static void handle_result(datap, pkt, sech)
1443 security_handle_t *sech;
1453 hostp = (am_host_t *)datap;
1454 hostp->up = HOST_READY;
1458 "WARNING: %s: selfcheck request failed: %s\n", hostp->hostname,
1459 security_geterror(sech));
1461 hostp->up = HOST_DONE;
1466 fprintf(errf, "got response from %s:\n----\n%s----\n\n",
1467 hostp->hostname, pkt->body);
1475 if (s[-2] == '\n') {
1479 #define sc "OPTIONS "
1480 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1483 #define sc "features="
1484 t = strstr(line, sc);
1485 if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1488 am_release_feature_set(hostp->features);
1489 if((hostp->features = am_string_to_feature(t)) == NULL) {
1490 fprintf(outf, "ERROR: %s: bad features value: %s\n",
1491 hostp->hostname, line);
1499 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1505 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1506 t = line + sizeof(sc) - 1;
1510 skip_whitespace(t, tch);
1512 * If the "error" is that the "noop" service is unknown, it
1513 * just means the client is "old" (does not support the service).
1514 * We can ignore this.
1516 if(hostp->features == NULL
1517 && pkt->type == P_NAK
1518 && (strcmp(t - 1, "unknown service: noop") == 0
1519 || strcmp(t - 1, "noop: invalid service") == 0)) {
1521 fprintf(outf, "ERROR: %s%s: %s\n",
1522 (pkt->type == P_NAK) ? "NAK " : "",
1526 hostp->up = HOST_DONE;
1531 fprintf(outf, "ERROR: %s: unknown response: %s\n",
1532 hostp->hostname, line);
1534 hostp->up = HOST_DONE;
1536 if(hostp->up == HOST_READY && hostp->features == NULL) {
1538 * The client does not support the features list, so give it an
1541 dbprintf(("%s: no feature set from host %s\n",
1542 debug_prefix_time(NULL), hostp->hostname));
1543 hostp->features = am_set_default_feature_set();
1545 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1546 if(dp->up == DISK_ACTIVE) {