/* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1991-2000 University of Maryland at College Park * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. U.M. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: the Amanda Development Team. Its members are listed in a * file named AUTHORS, in the root directory of this distribution. */ /* * $Id: amcheck.c,v 1.149.2.10 2007/02/05 18:54:13 martinea Exp $ * * checks for common problems in server and clients */ #include "amanda.h" #include "util.h" #include "conffile.h" #include "statfs.h" #include "diskfile.h" #include "tapefile.h" #include "tapeio.h" #include "changer.h" #include "packet.h" #include "security.h" #include "protocol.h" #include "clock.h" #include "version.h" #include "amindex.h" #include "token.h" #include "taperscan.h" #include "server_util.h" #include "pipespawn.h" #include "amfeatures.h" #define BUFFER_SIZE 32768 static time_t conf_ctimeout; static int overwrite; static disklist_t origq; static uid_t uid_dumpuser; /* local functions */ void usage(void); pid_t start_client_checks(int fd); pid_t start_server_check(int fd, int do_localchk, int do_tapechk); int main(int argc, char **argv); int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid); void usage(void) { error("Usage: amcheck%s [-am] [-w] [-sclt] [-M
] [host [disk]* ]* [-o configoption]*", versionsuffix()); /*NOTREACHED*/ } static unsigned long malloc_hist_1, malloc_size_1; static unsigned long malloc_hist_2, malloc_size_2; static am_feature_t *our_features = NULL; static char *our_feature_string = NULL; static char *displayunit; static long int unitdivisor; int main( int argc, char ** argv) { char buffer[BUFFER_SIZE]; char *version_string; char *mainfname = NULL; char pid_str[NUM_STR_SIZE]; int do_clientchk, client_probs; int do_localchk, do_tapechk, server_probs; pid_t clientchk_pid, serverchk_pid; int opt, tempfd, mainfd; ssize_t size; amwait_t retstat; pid_t pid; extern int optind; char *mailto = NULL; extern char *optarg; int mailout; int alwaysmail; char *tempfname = NULL; char *conffile; char *conf_diskfile; char *dumpuser; struct passwd *pw; uid_t uid_me; int new_argc, my_argc; char **new_argv, **my_argv; char *errstr; safe_fd(-1, 0); safe_cd(); set_pname("amcheck"); /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); dbopen(DBG_SUBDIR_SERVER); memset(buffer, 0, sizeof(buffer)); malloc_size_1 = malloc_inuse(&malloc_hist_1); snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid()); erroutput_type = ERR_INTERACTIVE; our_features = am_init_feature_set(); our_feature_string = am_feature_to_string(our_features); if(geteuid() == 0) { seteuid(getuid()); } uid_me = getuid(); alwaysmail = mailout = overwrite = 0; do_localchk = do_tapechk = do_clientchk = 0; server_probs = client_probs = 0; tempfd = mainfd = -1; parse_server_conf(argc, argv, &new_argc, &new_argv); my_argc = new_argc; my_argv = new_argv; /* process arguments */ while((opt = getopt(my_argc, my_argv, "M:mawsclt")) != EOF) { switch(opt) { case 'M': mailto=stralloc(optarg); if(!validate_mailto(mailto)){ printf("Invalid characters in mail address\n"); exit(1); } /*FALLTHROUGH*/ case 'm': #ifdef MAILER mailout = 1; #else printf("You can't use -%c because configure didn't find a mailer.\n", opt); exit(1); #endif break; case 'a': #ifdef MAILER mailout = 1; alwaysmail = 1; #else printf("You can't use -%c because configure didn't find a mailer.\n", opt); exit(1); #endif break; case 's': do_localchk = do_tapechk = 1; break; case 'c': do_clientchk = 1; break; case 'l': do_localchk = 1; break; case 'w': overwrite = 1; break; case 't': do_tapechk = 1; break; case '?': default: usage(); } } my_argc -= optind, my_argv += optind; if(my_argc < 1) usage(); if ((do_localchk | do_clientchk | do_tapechk) == 0) { /* Check everything if individual checks were not asked for */ do_localchk = do_clientchk = do_tapechk = 1; } if(overwrite) do_tapechk = 1; config_name = stralloc(*my_argv); config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL); conffile = stralloc2(config_dir, CONFFILE_NAME); if(read_conffile(conffile)) { error("errors processing config file \"%s\"", conffile); /*NOTREACHED*/ } dbrename(config_name, DBG_SUBDIR_SERVER); report_bad_conf_arg(); amfree(conffile); if(mailout && !mailto && (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) { printf("\nNo mail address configured in amanda.conf\n"); if(alwaysmail) printf("When using -a option please specify -Maddress also\n\n"); else printf("Use -Maddress instead of -m\n\n"); exit(1); } if(mailout && !mailto) { if(getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0) { if(!validate_mailto(getconf_str(CNF_MAILTO))){ printf("\nMail address in amanda.conf has invalid characters"); printf("\nNo email will be sent\n"); mailout = 0; } } else { printf("\nNo mail address configured in amanda.conf\n"); if(alwaysmail) printf("When using -a option please specify -Maddress also\n\n"); else printf("Use -Maddress instead of -m\n\n"); exit(1); } } conf_ctimeout = getconf_int(CNF_CTIMEOUT); conf_diskfile = getconf_str(CNF_DISKFILE); if (*conf_diskfile == '/') { conf_diskfile = stralloc(conf_diskfile); } else { conf_diskfile = stralloc2(config_dir, conf_diskfile); } if(read_diskfile(conf_diskfile, &origq) < 0) { error("could not load disklist %s", conf_diskfile); /*NOTREACHED*/ } errstr = match_disklist(&origq, my_argc-1, my_argv+1); if (errstr) { printf("%s",errstr); amfree(errstr); } amfree(conf_diskfile); /* * Make sure we are running as the dump user. */ dumpuser = getconf_str(CNF_DUMPUSER); if ((pw = getpwnam(dumpuser)) == NULL) { error("cannot look up dump user \"%s\"", dumpuser); /*NOTREACHED*/ } uid_dumpuser = pw->pw_uid; if ((pw = getpwuid(uid_me)) == NULL) { error("cannot look up my own uid (%ld)", (long)uid_me); /*NOTREACHED*/ } if (uid_me != uid_dumpuser) { error("running as user \"%s\" instead of \"%s\"", pw->pw_name, dumpuser); /*NOTREACHED*/ } displayunit = getconf_str(CNF_DISPLAYUNIT); unitdivisor = getconf_unit_divisor(); /* * If both server and client side checks are being done, the server * check output goes to the main output, while the client check output * goes to a temporary file and is copied to the main output when done. * * If the output is to be mailed, the main output is also a disk file, * otherwise it is stdout. */ if(do_clientchk && (do_localchk || do_tapechk)) { /* we need the temp file */ tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL); if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) { error("could not open %s: %s", tempfname, strerror(errno)); /*NOTREACHED*/ } unlink(tempfname); /* so it goes away on close */ amfree(tempfname); } if(mailout) { /* the main fd is a file too */ mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL); if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) { error("could not open %s: %s", mainfname, strerror(errno)); /*NOTREACHED*/ } unlink(mainfname); /* so it goes away on close */ amfree(mainfname); } else /* just use stdout */ mainfd = 1; /* start server side checks */ if(do_localchk || do_tapechk) serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk); else serverchk_pid = 0; /* start client side checks */ if(do_clientchk) { clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd); } else { clientchk_pid = 0; } /* wait for child processes and note any problems */ while(1) { if((pid = wait(&retstat)) == -1) { if(errno == EINTR) continue; else break; } else if(pid == clientchk_pid) { client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat); clientchk_pid = 0; } else if(pid == serverchk_pid) { server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat); serverchk_pid = 0; } else { char number[NUM_STR_SIZE]; char *wait_msg = NULL; snprintf(number, SIZEOF(number), "%ld", (long)pid); wait_msg = vstralloc("parent: reaped bogus pid ", number, "\n", NULL); if (fullwrite(mainfd, wait_msg, strlen(wait_msg)) < 0) { error("write main file: %s", strerror(errno)); /*NOTREACHED*/ } amfree(wait_msg); } } /* copy temp output to main output and write tagline */ if(do_clientchk && (do_localchk || do_tapechk)) { if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) { error("seek temp file: %s", strerror(errno)); /*NOTREACHED*/ } while((size = fullread(tempfd, buffer, SIZEOF(buffer))) > 0) { if (fullwrite(mainfd, buffer, (size_t)size) < 0) { error("write main file: %s", strerror(errno)); /*NOTREACHED*/ } } if(size < 0) { error("read temp file: %s", strerror(errno)); /*NOTREACHED*/ } aclose(tempfd); } version_string = vstralloc("\n", "(brought to you by Amanda ", version(), ")\n", NULL); if (fullwrite(mainfd, version_string, strlen(version_string)) < 0) { error("write main file: %s", strerror(errno)); /*NOTREACHED*/ } amfree(version_string); amfree(config_dir); amfree(config_name); amfree(our_feature_string); am_release_feature_set(our_features); our_features = NULL; malloc_size_2 = malloc_inuse(&malloc_hist_2); if(malloc_size_1 != malloc_size_2) { malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2); } /* send mail if requested, but only if there were problems */ #ifdef MAILER #define MAILTO_LIMIT 10 if((server_probs || client_probs || alwaysmail) && mailout) { int mailfd; int nullfd; int errfd; FILE *ferr; char *subject; char **a; amwait_t retstat; ssize_t r; ssize_t w; char *err = NULL; char *extra_info = NULL; char *line = NULL; int ret; int rc; int sig; char number[NUM_STR_SIZE]; fflush(stdout); if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) { error("lseek main file: %s", strerror(errno)); /*NOTREACHED*/ } if(alwaysmail && !(server_probs || client_probs)) { subject = stralloc2(getconf_str(CNF_ORG), " AMCHECK REPORT: NO PROBLEMS FOUND"); } else { subject = stralloc2(getconf_str(CNF_ORG), " AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"); } /* * Variable arg lists are hard to deal with when we do not know * ourself how many args are involved. Split the address list * and hope there are not more than 9 entries. * * Remember that split() returns the original input string in * argv[0], so we have to skip over that. */ a = (char **) alloc((MAILTO_LIMIT + 1) * SIZEOF(char *)); memset(a, 0, (MAILTO_LIMIT + 1) * SIZEOF(char *)); if(mailto) { a[1] = mailto; a[2] = NULL; } else { r = (ssize_t)split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " "); a[r + 1] = NULL; } if((nullfd = open("/dev/null", O_RDWR)) < 0) { error("nullfd: /dev/null: %s", strerror(errno)); /*NOTREACHED*/ } pipespawn(MAILER, STDIN_PIPE | STDERR_PIPE, &mailfd, &nullfd, &errfd, MAILER, "-s", subject, a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], NULL); amfree(subject); /* * There is the potential for a deadlock here since we are writing * to the process and then reading stderr, but in the normal case, * nothing should be coming back to us, and hopefully in error * cases, the pipe will break and we will exit out of the loop. */ signal(SIGPIPE, SIG_IGN); while((r = fullread(mainfd, buffer, SIZEOF(buffer))) > 0) { if((w = fullwrite(mailfd, buffer, (size_t)r)) != (ssize_t)r) { if(w < 0 && errno == EPIPE) { strappend(extra_info, "EPIPE writing to mail process\n"); break; } else if(w < 0) { error("mailfd write: %s", strerror(errno)); /*NOTREACHED*/ } else { error("mailfd write: wrote %zd instead of %zd", w, r); /*NOTREACHED*/ } } } aclose(mailfd); ferr = fdopen(errfd, "r"); if (!ferr) { error("Can't fdopen: %s", strerror(errno)); /*NOTREACHED*/ } for(; (line = agets(ferr)) != NULL; free(line)) { if (line[0] == '\0') continue; strappend(extra_info, line); strappend(extra_info, "\n"); } afclose(ferr); errfd = -1; rc = 0; while (wait(&retstat) != -1) { if (WIFSIGNALED(retstat)) { ret = 0; rc = sig = WTERMSIG(retstat); } else { sig = 0; rc = ret = WEXITSTATUS(retstat); } if (rc != 0) { if (ret == 0) { strappend(err, "got signal "); ret = sig; } else { strappend(err, "returned "); } snprintf(number, SIZEOF(number), "%d", ret); strappend(err, number); } } if (rc != 0) { if(extra_info) { fputs(extra_info, stderr); amfree(extra_info); } error("error running mailer %s: %s", MAILER, err); /*NOTREACHED*/ } } #endif free_new_argv(new_argc, new_argv); free_server_config(); dbclose(); return (server_probs || client_probs); } /* --------------------------------------------------- */ int nslots, backwards, found, got_match, tapedays; char *datestamp; char *first_match_label = NULL, *first_match = NULL, *found_device = NULL; char *label; char *searchlabel, *labelstr; tape_t *tp; FILE *errf = NULL; int test_server_pgm( FILE * outf, char * dir, char * pgm, int suid, uid_t dumpuid) { struct stat statbuf; int pgmbad = 0; char *quoted; pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL); quoted = quote_string(pgm); if(stat(pgm, &statbuf) == -1) { fprintf(outf, "ERROR: program %s: does not exist\n", quoted); pgmbad = 1; } else if (!S_ISREG(statbuf.st_mode)) { fprintf(outf, "ERROR: program %s: not a file\n", quoted); pgmbad = 1; } else if (access(pgm, X_OK) == -1) { fprintf(outf, "ERROR: program %s: not executable\n", quoted); pgmbad = 1; } else if (suid \ && dumpuid != 0 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) { fprintf(outf, "ERROR: program %s: not setuid-root\n", quoted); pgmbad = 1; } amfree(quoted); amfree(pgm); return pgmbad; } pid_t start_server_check( int fd, int do_localchk, int do_tapechk) { char *tapename; generic_fs_stats_t fs; FILE *outf = NULL; holdingdisk_t *hdp; pid_t pid; int confbad = 0, tapebad = 0, disklow = 0, logbad = 0; int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0; int testtape = do_tapechk; tapetype_t *tp = NULL; char *quoted; switch(pid = fork()) { case -1: error("could not fork server check: %s", strerror(errno)); /*NOTREACHED*/ case 0: break; default: return pid; } dup2(fd, 1); dup2(fd, 2); set_pname("amcheck-server"); startclock(); if((outf = fdopen(fd, "w")) == NULL) { error("fdopen %d: %s", fd, strerror(errno)); /*NOTREACHED*/ } errf = outf; fprintf(outf, "Amanda Tape Server Host Check\n"); fprintf(outf, "-----------------------------\n"); if (do_localchk || testtape) { tp = lookup_tapetype(getconf_str(CNF_TAPETYPE)); } /* * Check various server side config file settings. */ if(do_localchk) { char *ColumnSpec; char *errstr = NULL; char *lbl_templ; ColumnSpec = getconf_str(CNF_COLUMNSPEC); if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) { fprintf(outf, "ERROR: %s\n", errstr); amfree(errstr); confbad = 1; } lbl_templ = tapetype_get_lbl_templ(tp); if(strcmp(lbl_templ, "") != 0) { if(strchr(lbl_templ, '/') == NULL) { lbl_templ = stralloc2(config_dir, lbl_templ); } else { lbl_templ = stralloc(lbl_templ); } if(access(lbl_templ, R_OK) == -1) { fprintf(outf, "ERROR: cannot access lbl_templ file %s: %s\n", lbl_templ, strerror(errno)); confbad = 1; } #if !defined(LPRCMD) 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"); confbad = 1; #endif } /* check that localhost is resolvable */ if ((gethostbyname("localhost")) == NULL) { fprintf(outf, "ERROR: Cannot resolve `localhost'.\n"); } } /* * Look up the programs used on the server side. */ if(do_localchk) { /* * entreprise version will do planner/dumper suid check */ if(access(libexecdir, X_OK) == -1) { quoted = quote_string(libexecdir); fprintf(outf, "ERROR: program dir %s: not accessible\n", quoted); pgmbad = 1; amfree(quoted); } else { if(test_server_pgm(outf, libexecdir, "planner", 1, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, libexecdir, "dumper", 1, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, libexecdir, "driver", 0, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, libexecdir, "taper", 0, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, libexecdir, "amtrmidx", 0, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, libexecdir, "amlogroll", 0, uid_dumpuser)) pgmbad = 1; } if(access(sbindir, X_OK) == -1) { quoted = quote_string(sbindir); fprintf(outf, "ERROR: program dir %s: not accessible\n", sbindir); pgmbad = 1; amfree(quoted); } else { if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser)) pgmbad = 1; if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser)) pgmbad = 1; } if(access(COMPRESS_PATH, X_OK) == -1) { quoted = quote_string(COMPRESS_PATH); fprintf(outf, "WARNING: %s is not executable, server-compression and indexing will not work\n", quoted); amfree(quoted); } } /* * Check that the directory for the tapelist file is writable, as well * as the tapelist file itself (if it already exists). Also, check for * a "hold" file (just because it is convenient to do it here) and warn * if tapedev is set to the null device. */ if(do_localchk || do_tapechk) { char *conf_tapelist; char *tapefile; char *tape_dir; char *lastslash; char *holdfile; struct stat statbuf; conf_tapelist=getconf_str(CNF_TAPELIST); if (*conf_tapelist == '/') { tapefile = stralloc(conf_tapelist); } else { tapefile = stralloc2(config_dir, conf_tapelist); } /* * XXX There Really Ought to be some error-checking here... dhw */ tape_dir = stralloc(tapefile); if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) { *lastslash = '\0'; /* * else whine Really Loudly about a path with no slashes??!? */ } if(access(tape_dir, W_OK) == -1) { quoted = quote_string(tape_dir); fprintf(outf, "ERROR: tapelist dir %s: not writable.\n", quoted); tapebad = 1; amfree(quoted); } else if(stat(tapefile, &statbuf) == -1) { quoted = quote_string(tape_dir); fprintf(outf, "ERROR: tapelist %s (%s), " "you must create an empty file.\n", quoted, strerror(errno)); tapebad = 1; amfree(quoted); } else if(!S_ISREG(statbuf.st_mode)) { quoted = quote_string(tapefile); fprintf(outf, "ERROR: tapelist %s: should be a regular file.\n", quoted); tapebad = 1; amfree(quoted); } else if(access(tapefile, F_OK) != 0) { quoted = quote_string(tapefile); fprintf(outf, "ERROR: can't access tapelist %s\n", quoted); tapebad = 1; amfree(quoted); } else if(access(tapefile, F_OK) == 0 && access(tapefile, W_OK) != 0) { quoted = quote_string(tapefile); fprintf(outf, "ERROR: tapelist %s: not writable\n", quoted); tapebad = 1; amfree(quoted); } else if(read_tapelist(tapefile)) { quoted = quote_string(tapefile); fprintf(outf, "ERROR: tapelist %s: parse error\n", quoted); tapebad = 1; amfree(quoted); } holdfile = vstralloc(config_dir, "/", "hold", NULL); if(access(holdfile, F_OK) != -1) { quoted = quote_string(holdfile); fprintf(outf, "WARNING: hold file %s exists\n", holdfile); amfree(quoted); } amfree(tapefile); amfree(tape_dir); amfree(holdfile); tapename = getconf_str(CNF_TAPEDEV); if (tapename == NULL) { if (getconf_str(CNF_TPCHANGER) == NULL) { fprintf(outf, "WARNING: No tapedev or tpchanger specified\n"); testtape = 0; do_tapechk = 0; } } else if (strncmp(tapename, "null:", 5) == 0) { fprintf(outf, "WARNING: tapedev is %s, dumps will be thrown away\n", tapename); testtape = 0; do_tapechk = 0; } } /* check available disk space */ if(do_localchk) { for(hdp = holdingdisks; hdp != NULL; hdp = hdp->next) { quoted = quote_string(holdingdisk_get_diskdir(hdp)); if(get_fs_stats(holdingdisk_get_diskdir(hdp), &fs) == -1) { fprintf(outf, "ERROR: holding dir %s (%s), " "you must create a directory.\n", quoted, strerror(errno)); disklow = 1; } else if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) { fprintf(outf, "ERROR: holding disk %s: not writable: %s.\n", quoted, strerror(errno)); disklow = 1; } else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) { fprintf(outf, "ERROR: holding disk %s: not searcheable: %s.\n", quoted, strerror(errno)); disklow = 1; } else if(fs.avail == (off_t)-1) { fprintf(outf, "WARNING: holding disk %s: " "available space unknown (" OFF_T_FMT" KB requested)\n", quoted, (OFF_T_FMT_TYPE)holdingdisk_get_disksize(hdp)); disklow = 1; } else if(holdingdisk_get_disksize(hdp) > (off_t)0) { if(fs.avail < holdingdisk_get_disksize(hdp)) { fprintf(outf, "WARNING: holding disk %s: " "only " OFF_T_FMT " %sB free (" OFF_T_FMT " %sB requested)\n", quoted, (OFF_T_FMT_TYPE)(fs.avail / (off_t)unitdivisor), displayunit, (OFF_T_FMT_TYPE)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor), displayunit); disklow = 1; } else { fprintf(outf, "Holding disk %s: " OFF_T_FMT " %sB disk space available," " using " OFF_T_FMT " %sB as requested\n", quoted, (OFF_T_FMT_TYPE)(fs.avail/(off_t)unitdivisor), displayunit, (OFF_T_FMT_TYPE)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor), displayunit); } } else { if((fs.avail + holdingdisk_get_disksize(hdp)) < (off_t)0) { fprintf(outf, "WARNING: holding disk %s: " "only " OFF_T_FMT " %sB free, using nothing\n", quoted, (OFF_T_FMT_TYPE)(fs.avail/(off_t)unitdivisor), displayunit); disklow = 1; } else { fprintf(outf, "Holding disk %s: " OFF_T_FMT " %sB disk space available, using " OFF_T_FMT " %sB\n", quoted, (OFF_T_FMT_TYPE)(fs.avail/(off_t)unitdivisor), displayunit, (OFF_T_FMT_TYPE)((fs.avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor), displayunit); } } amfree(quoted); } } /* check that the log file is writable if it already exists */ if(do_localchk) { char *conf_logdir; char *logfile; char *olddir; struct stat stat_old; struct stat statbuf; conf_logdir = getconf_str(CNF_LOGDIR); if (*conf_logdir == '/') { conf_logdir = stralloc(conf_logdir); } else { conf_logdir = stralloc2(config_dir, conf_logdir); } logfile = vstralloc(conf_logdir, "/log", NULL); quoted = quote_string(conf_logdir); if(stat(conf_logdir, &statbuf) == -1) { fprintf(outf, "ERROR: logdir %s (%s), you must create directory.\n", quoted, strerror(errno)); disklow = 1; } else if(access(conf_logdir, W_OK) == -1) { fprintf(outf, "ERROR: log dir %s: not writable\n", quoted); logbad = 1; } amfree(quoted); if(access(logfile, F_OK) == 0) { testtape = 0; logbad = 1; if(access(logfile, W_OK) != 0) { quoted = quote_string(logfile); fprintf(outf, "ERROR: log file %s: not writable\n", quoted); amfree(quoted); } } olddir = vstralloc(conf_logdir, "/oldlog", NULL); quoted = quote_string(olddir); if (stat(olddir,&stat_old) == 0) { /* oldlog exist */ if(!(S_ISDIR(stat_old.st_mode))) { fprintf(outf, "ERROR: oldlog directory %s is not a directory\n", quoted); } if(access(olddir, W_OK) == -1) { fprintf(outf, "ERROR: oldlog dir %s: not writable\n", quoted); } } else if(lstat(olddir,&stat_old) == 0) { fprintf(outf, "ERROR: oldlog directory %s is not a directory\n", quoted); } amfree(quoted); if (testtape) { logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL); if (access(logfile, F_OK) == 0) { testtape = 0; logbad = 1; } } amfree(olddir); amfree(logfile); amfree(conf_logdir); } if (testtape) { /* check that the tape is a valid amanda tape */ int tape_status; tapedays = getconf_int(CNF_TAPECYCLE); labelstr = getconf_str(CNF_LABELSTR); tapename = getconf_str(CNF_TAPEDEV); if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) { fprintf(outf, "WARNING: if a tape changer is not available, runtapes must be set to 1\n"); } tape_status = taper_scan(NULL, &label, &datestamp, &tapename, FILE_taperscan_output_callback, outf); if (tapename) { if (tape_access(tapename,F_OK) == -1) { fprintf(outf, "ERROR: Can't access device %s: %s\n", tapename, strerror(errno)); } if (tape_access(tapename,R_OK) == -1) { fprintf(outf, "ERROR: Can't read device %s: %s\n", tapename, strerror(errno)); } if (tape_access(tapename,W_OK) == -1) { fprintf(outf, "ERROR: Can't write to device %s: %s\n", tapename, strerror(errno)); } } if (tape_status < 0) { tape_t *exptape = lookup_last_reusable_tape(0); fprintf(outf, " (expecting "); if(exptape != NULL) fprintf(outf, "tape %s or ", exptape->label); fprintf(outf, "a new tape)\n"); tapebad = 1; } else { if (overwrite) { char *wrlabel_status; wrlabel_status = tape_wrlabel(tapename, "X", label, (unsigned)(tapetype_get_blocksize(tp) * 1024)); if (wrlabel_status != NULL) { if (tape_status == 3) { fprintf(outf, "ERROR: Could not label brand new tape: %s\n", wrlabel_status); } else { fprintf(outf, "ERROR: tape %s label ok, but is not writable (%s)\n", label, wrlabel_status); } tapebad = 1; } else { if (tape_status != 3) { fprintf(outf, "Tape %s is writable; rewrote label.\n", label); } else { fprintf(outf, "Wrote label %s to brand new tape.\n", label); } } } else { fprintf(outf, "NOTE: skipping tape-writable test\n"); if (tape_status == 3) { fprintf(outf, "Found a brand new tape, will label it %s.\n", label); } else { fprintf(outf, "Tape %s label ok\n", label); } } } amfree(tapename); } else if (do_tapechk) { fprintf(outf, "WARNING: skipping tape test because amdump or amflush seem to be running\n"); fprintf(outf, "WARNING: if they are not, you must run amcleanup\n"); } else { fprintf(outf, "NOTE: skipping tape checks\n"); } /* * See if the information file and index directory for each client * and disk is OK. Since we may be seeing clients and/or disks for * the first time, these are just warnings, not errors. */ if(do_localchk) { char *conf_infofile; char *conf_indexdir; char *hostinfodir = NULL; char *hostindexdir = NULL; char *diskdir = NULL; char *infofile = NULL; struct stat statbuf; disk_t *dp; am_host_t *hostp; int indexdir_checked = 0; int hostindexdir_checked = 0; char *host; char *disk; int conf_tapecycle, conf_runspercycle; conf_tapecycle = getconf_int(CNF_TAPECYCLE); conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE); if(conf_tapecycle <= conf_runspercycle) { fprintf(outf, "WARNING: tapecycle (%d) <= runspercycle (%d).\n", conf_tapecycle, conf_runspercycle); } conf_infofile = getconf_str(CNF_INFOFILE); if (*conf_infofile == '/') { conf_infofile = stralloc(conf_infofile); } else { conf_infofile = stralloc2(config_dir, conf_infofile); } conf_indexdir = getconf_str(CNF_INDEXDIR); if (*conf_indexdir == '/') { conf_indexdir = stralloc(conf_indexdir); } else { conf_indexdir = stralloc2(config_dir, conf_indexdir); } #if TEXTDB quoted = quote_string(conf_infofile); if(stat(conf_infofile, &statbuf) == -1) { if (errno == ENOENT) { fprintf(outf, "NOTE: conf info dir %s does not exist\n", quoted); fprintf(outf, "NOTE: it will be created on the next run.\n"); } else { fprintf(outf, "ERROR: conf info dir %s (%s)\n", quoted, strerror(errno)); } amfree(conf_infofile); } else if (!S_ISDIR(statbuf.st_mode)) { fprintf(outf, "ERROR: info dir %s: not a directory\n", quoted); amfree(conf_infofile); infobad = 1; } else if (access(conf_infofile, W_OK) == -1) { fprintf(outf, "ERROR: info dir %s: not writable\n", quoted); amfree(conf_infofile); infobad = 1; } else { char *errmsg = NULL; if (check_infofile(conf_infofile, &origq, &errmsg) == -1) { fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg); amfree(errmsg); } strappend(conf_infofile, "/"); } amfree(quoted); #endif while(!empty(origq)) { hostp = origq.head->host; host = sanitise_filename(hostp->hostname); #if TEXTDB if(conf_infofile) { hostinfodir = newstralloc2(hostinfodir, conf_infofile, host); quoted = quote_string(hostinfodir); if(stat(hostinfodir, &statbuf) == -1) { if (errno == ENOENT) { fprintf(outf, "NOTE: host info dir %s does not exist\n", quoted); fprintf(outf, "NOTE: it will be created on the next run.\n"); } else { fprintf(outf, "ERROR: host info dir %s (%s)\n", quoted, strerror(errno)); } amfree(hostinfodir); } else if (!S_ISDIR(statbuf.st_mode)) { fprintf(outf, "ERROR: info dir %s: not a directory\n", quoted); amfree(hostinfodir); infobad = 1; } else if (access(hostinfodir, W_OK) == -1) { fprintf(outf, "ERROR: info dir %s: not writable\n", quoted); amfree(hostinfodir); infobad = 1; } else { strappend(hostinfodir, "/"); } amfree(quoted); } #endif for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { disk = sanitise_filename(dp->name); #if TEXTDB if(hostinfodir) { char *quotedif; diskdir = newstralloc2(diskdir, hostinfodir, disk); infofile = vstralloc(diskdir, "/", "info", NULL); quoted = quote_string(diskdir); quotedif = quote_string(infofile); if(stat(diskdir, &statbuf) == -1) { if (errno == ENOENT) { fprintf(outf, "NOTE: info dir %s does not exist\n", quoted); fprintf(outf, "NOTE: it will be created on the next run.\n"); } else { fprintf(outf, "ERROR: info dir %s (%s)\n", quoted, strerror(errno)); } } else if (!S_ISDIR(statbuf.st_mode)) { fprintf(outf, "ERROR: info dir %s: not a directory\n", quoted); infobad = 1; } else if (access(diskdir, W_OK) == -1) { fprintf(outf, "ERROR: info dir %s: not writable\n", quoted); infobad = 1; } else if(stat(infofile, &statbuf) == -1) { if (errno == ENOENT) { fprintf(outf, "NOTE: info file %s does not exist\n", quotedif); fprintf(outf, "NOTE: it will be created on the next run.\n"); } else { fprintf(outf, "ERROR: info dir %s (%s)\n", quoted, strerror(errno)); } } else if (!S_ISREG(statbuf.st_mode)) { fprintf(outf, "ERROR: info file %s: not a file\n", quotedif); infobad = 1; } else if (access(infofile, R_OK) == -1) { fprintf(outf, "ERROR: info file %s: not readable\n", quotedif); infobad = 1; } amfree(quotedif); amfree(quoted); amfree(infofile); } #endif if(dp->index) { if(! indexdir_checked) { quoted = quote_string(conf_indexdir); if(stat(conf_indexdir, &statbuf) == -1) { if (errno == ENOENT) { fprintf(outf, "NOTE: index dir %s does not exist\n", quoted); fprintf(outf, "NOTE: it will be created on the next run.\n"); } else { fprintf(outf, "ERROR: index dir %s (%s)\n", quoted, strerror(errno)); } amfree(conf_indexdir); } else if (!S_ISDIR(statbuf.st_mode)) { fprintf(outf, "ERROR: index dir %s: not a directory\n", quoted); amfree(conf_indexdir); indexbad = 1; } else if (access(conf_indexdir, W_OK) == -1) { fprintf(outf, "ERROR: index dir %s: not writable\n", quoted); amfree(conf_indexdir); indexbad = 1; } else { strappend(conf_indexdir, "/"); } indexdir_checked = 1; amfree(quoted); } if(conf_indexdir) { if(! hostindexdir_checked) { hostindexdir = stralloc2(conf_indexdir, host); quoted = quote_string(hostindexdir); if(stat(hostindexdir, &statbuf) == -1) { if (errno == ENOENT) { fprintf(outf, "NOTE: index dir %s does not exist\n", quoted); fprintf(outf, "NOTE: it will be created on the next run.\n"); } else { fprintf(outf, "ERROR: index dir %s (%s)\n", quoted, strerror(errno)); } amfree(hostindexdir); } else if (!S_ISDIR(statbuf.st_mode)) { fprintf(outf, "ERROR: index dir %s: not a directory\n", quoted); amfree(hostindexdir); indexbad = 1; } else if (access(hostindexdir, W_OK) == -1) { fprintf(outf, "ERROR: index dir %s: not writable\n", quoted); amfree(hostindexdir); indexbad = 1; } else { strappend(hostindexdir, "/"); } hostindexdir_checked = 1; amfree(quoted); } if(hostindexdir) { diskdir = newstralloc2(diskdir, hostindexdir, disk); quoted = quote_string(diskdir); if(stat(diskdir, &statbuf) == -1) { if (errno == ENOENT) { fprintf(outf, "NOTE: index dir %s does not exist\n", quoted); fprintf(outf, "NOTE: it will be created on the next run.\n"); } else { fprintf(outf, "ERROR: index dir %s (%s)\n", quoted, strerror(errno)); } } else if (!S_ISDIR(statbuf.st_mode)) { fprintf(outf, "ERROR: index dir %s: not a directory\n", quoted); indexbad = 1; } else if (access(diskdir, W_OK) == -1) { fprintf(outf, "ERROR: index dir %s: is not writable\n", quoted); indexbad = 1; } amfree(quoted); } } } if ( dp->encrypt == ENCRYPT_SERV_CUST ) { if ( dp->srv_encrypt[0] == '\0' ) { fprintf(outf, "ERROR: server encryption program not specified\n"); pgmbad = 1; } else if(access(dp->srv_encrypt, X_OK) == -1) { fprintf(outf, "ERROR: %s is not executable, server encryption will not work\n", dp->srv_encrypt ); pgmbad = 1; } } if ( dp->compress == COMP_SERV_CUST ) { if ( dp->srvcompprog[0] == '\0' ) { fprintf(outf, "ERROR: server custom compression program not specified\n"); pgmbad = 1; } else if(access(dp->srvcompprog, X_OK) == -1) { quoted = quote_string(dp->srvcompprog); fprintf(outf, "ERROR: %s is not executable, server custom compression will not work\n", quoted); amfree(quoted); pgmbad = 1; } } amfree(disk); remove_disk(&origq, dp); } amfree(host); amfree(hostindexdir); hostindexdir_checked = 0; } amfree(diskdir); amfree(hostinfodir); amfree(conf_infofile); amfree(conf_indexdir); } amfree(datestamp); amfree(label); amfree(config_dir); amfree(config_name); fprintf(outf, "Server check took %s seconds\n", walltime_str(curclock())); fflush(outf); malloc_size_2 = malloc_inuse(&malloc_hist_2); if(malloc_size_1 != malloc_size_2) { malloc_list(fd, malloc_hist_1, malloc_hist_2); } exit(userbad \ || confbad \ || tapebad \ || disklow \ || logbad \ || infobad \ || indexbad \ || pgmbad); /*NOTREACHED*/ return 0; } /* --------------------------------------------------- */ int remote_errors; FILE *outf; static void handle_result(void *, pkt_t *, security_handle_t *); void start_host(am_host_t *hostp); #define HOST_READY ((void *)0) /* must be 0 */ #define HOST_ACTIVE ((void *)1) #define HOST_DONE ((void *)2) #define DISK_READY ((void *)0) /* must be 0 */ #define DISK_ACTIVE ((void *)1) #define DISK_DONE ((void *)2) void start_host( am_host_t *hostp) { disk_t *dp; char *req = NULL; size_t req_len = 0; int disk_count; const security_driver_t *secdrv; char number[NUM_STR_SIZE]; if(hostp->up != HOST_READY) { return; } if (strncmp (hostp->hostname,"localhost",9) == 0) { fprintf(outf, "WARNING: Usage of fully qualified hostname recommended for Client %s.\n", hostp->hostname); } /* * The first time through here we send a "noop" request. This will * return the feature list from the client if it supports that. * If it does not, handle_result() will set the feature list to an * empty structure. In either case, we do the disks on the second * (and subsequent) pass(es). */ disk_count = 0; if(hostp->features != NULL) { /* selfcheck service */ int has_features = am_has_feature(hostp->features, fe_req_options_features); int has_hostname = am_has_feature(hostp->features, fe_req_options_hostname); int has_maxdumps = am_has_feature(hostp->features, fe_req_options_maxdumps); int has_config = am_has_feature(hostp->features, fe_req_options_config); if(!am_has_feature(hostp->features, fe_selfcheck_req) && !am_has_feature(hostp->features, fe_selfcheck_req_device)) { fprintf(outf, "ERROR: Client %s does not support selfcheck REQ packet.\n", hostp->hostname); } if(!am_has_feature(hostp->features, fe_selfcheck_rep)) { fprintf(outf, "ERROR: Client %s does not support selfcheck REP packet.\n", hostp->hostname); } if(!am_has_feature(hostp->features, fe_sendsize_req_options) && !am_has_feature(hostp->features, fe_sendsize_req_no_options) && !am_has_feature(hostp->features, fe_sendsize_req_device)) { fprintf(outf, "ERROR: Client %s does not support sendsize REQ packet.\n", hostp->hostname); } if(!am_has_feature(hostp->features, fe_sendsize_rep)) { fprintf(outf, "ERROR: Client %s does not support sendsize REP packet.\n", hostp->hostname); } if(!am_has_feature(hostp->features, fe_sendbackup_req) && !am_has_feature(hostp->features, fe_sendbackup_req_device)) { fprintf(outf, "ERROR: Client %s does not support sendbackup REQ packet.\n", hostp->hostname); } if(!am_has_feature(hostp->features, fe_sendbackup_rep)) { fprintf(outf, "ERROR: Client %s does not support sendbackup REP packet.\n", hostp->hostname); } snprintf(number, SIZEOF(number), "%d", hostp->maxdumps); req = vstralloc("SERVICE ", "selfcheck", "\n", "OPTIONS ", has_features ? "features=" : "", has_features ? our_feature_string : "", has_features ? ";" : "", has_maxdumps ? "maxdumps=" : "", has_maxdumps ? number : "", has_maxdumps ? ";" : "", has_hostname ? "hostname=" : "", has_hostname ? hostp->hostname : "", has_hostname ? ";" : "", has_config ? "config=" : "", has_config ? config_name : "", has_config ? ";" : "", "\n", NULL); req_len = strlen(req); req_len += 128; /* room for SECURITY ... */ req_len += 256; /* room for non-disk answers */ for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { char *l; size_t l_len; char *o; char *calcsize; char *qname; char *qdevice; if(dp->up != DISK_READY || dp->todo != 1) { continue; } o = optionstr(dp, hostp->features, outf); if (o == NULL) { remote_errors++; continue; } qname = quote_string(dp->name); qdevice = quote_string(dp->device); if ((dp->name && qname[0] == '"') || (dp->device && qdevice[0] == '"')) { if(!am_has_feature(hostp->features, fe_interface_quoted_text)) { fprintf(outf, "WARNING: %s:%s:%s host does not support quoted text\n", hostp->hostname, qname, qdevice); } } if(dp->device) { if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) { fprintf(outf, "ERROR: %s:%s (%s): selfcheck does not support device.\n", hostp->hostname, qname, dp->device); } if(!am_has_feature(hostp->features, fe_sendsize_req_device)) { fprintf(outf, "ERROR: %s:%s (%s): sendsize does not support device.\n", hostp->hostname, qname, dp->device); } if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) { fprintf(outf, "ERROR: %s:%s (%s): sendbackup does not support device.\n", hostp->hostname, qname, dp->device); } } if(strncmp(dp->program,"DUMP",4) == 0 || strncmp(dp->program,"GNUTAR",6) == 0) { if(strcmp(dp->program, "DUMP") == 0 && !am_has_feature(hostp->features, fe_program_dump)) { fprintf(outf, "ERROR: %s:%s does not support DUMP.\n", hostp->hostname, qname); } if(strcmp(dp->program, "GNUTAR") == 0 && !am_has_feature(hostp->features, fe_program_gnutar)) { fprintf(outf, "ERROR: %s:%s does not support GNUTAR.\n", hostp->hostname, qname); } if(dp->estimate == ES_CALCSIZE && !am_has_feature(hostp->features, fe_calcsize_estimate)) { fprintf(outf, "ERROR: %s:%s does not support CALCSIZE for estimate, using CLIENT.\n", hostp->hostname, qname); dp->estimate = ES_CLIENT; } if(dp->estimate == ES_CALCSIZE && am_has_feature(hostp->features, fe_selfcheck_calcsize)) calcsize = "CALCSIZE "; else calcsize = ""; if(dp->compress == COMP_CUST && !am_has_feature(hostp->features, fe_options_compress_cust)) { fprintf(outf, "ERROR: Client %s does not support custom compression.\n", hostp->hostname); } if(dp->encrypt == ENCRYPT_CUST ) { if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) { fprintf(outf, "ERROR: Client %s does not support data encryption.\n", hostp->hostname); remote_errors++; } else if ( dp->compress == COMP_SERV_FAST || dp->compress == COMP_SERV_BEST || dp->compress == COMP_SERV_CUST ) { fprintf(outf, "ERROR: %s: Client encryption with server compression is not supported. See amanda.conf(5) for detail.\n", hostp->hostname); remote_errors++; } } if(dp->device) { l = vstralloc(calcsize, dp->program, " ", qname, " ", dp->device, " 0 OPTIONS |", o, "\n", NULL); } else { l = vstralloc(calcsize, dp->program, " ", qname, " 0 OPTIONS |", o, "\n", NULL); } } else { if(!am_has_feature(hostp->features, fe_program_dumper_api)) { fprintf(outf, "ERROR: %s:%s does not support DUMPER-API.\n", hostp->hostname, qname); } l = vstralloc("DUMPER ", dp->program, " ", qname, " ", dp->device, " 0 OPTIONS |", o, "\n", NULL); } amfree(qname); amfree(qdevice); l_len = strlen(l); amfree(o); strappend(req, l); req_len += l_len; amfree(l); dp->up = DISK_ACTIVE; disk_count++; } } else { /* noop service */ req = vstralloc("SERVICE ", "noop", "\n", "OPTIONS ", "features=", our_feature_string, ";", "\n", NULL); for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { if(dp->up != DISK_READY || dp->todo != 1) { continue; } disk_count++; } } if(disk_count == 0) { amfree(req); hostp->up = HOST_DONE; return; } secdrv = security_getdriver(hostp->disks->security_driver); if (secdrv == NULL) { error("could not find security driver '%s' for host '%s'", hostp->disks->security_driver, hostp->hostname); /*NOTREACHED*/ } protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, req, conf_ctimeout, handle_result, hostp); amfree(req); hostp->up = HOST_ACTIVE; } pid_t start_client_checks( int fd) { am_host_t *hostp; disk_t *dp; int hostcount; pid_t pid; int userbad = 0; switch(pid = fork()) { case -1: error("could not fork client check: %s", strerror(errno)); /*NOTREACHED*/ case 0: break; default: return pid; } dup2(fd, 1); dup2(fd, 2); set_pname("amcheck-clients"); startclock(); if((outf = fdopen(fd, "w")) == NULL) { error("fdopen %d: %s", fd, strerror(errno)); /*NOTREACHED*/ } errf = outf; fprintf(outf, "\nAmanda Backup Client Hosts Check\n"); fprintf(outf, "--------------------------------\n"); protocol_init(); hostcount = remote_errors = 0; for(dp = origq.head; dp != NULL; dp = dp->next) { hostp = dp->host; if(hostp->up == HOST_READY && dp->todo == 1) { start_host(hostp); hostcount++; protocol_check(); } } protocol_run(); fprintf(outf, "Client check: %d host%s checked in %s seconds, %d problem%s found\n", hostcount, (hostcount == 1) ? "" : "s", walltime_str(curclock()), remote_errors, (remote_errors == 1) ? "" : "s"); fflush(outf); amfree(config_dir); amfree(config_name); malloc_size_2 = malloc_inuse(&malloc_hist_2); if(malloc_size_1 != malloc_size_2) { malloc_list(fd, malloc_hist_1, malloc_hist_2); } exit(userbad || remote_errors > 0); /*NOTREACHED*/ return 0; } static void handle_result( void * datap, pkt_t * pkt, security_handle_t * sech) { am_host_t *hostp; disk_t *dp; char *line; char *s; char *t; int ch; int tch; hostp = (am_host_t *)datap; hostp->up = HOST_READY; if (pkt == NULL) { fprintf(outf, "WARNING: %s: selfcheck request failed: %s\n", hostp->hostname, security_geterror(sech)); remote_errors++; hostp->up = HOST_DONE; return; } #if 0 fprintf(errf, "got response from %s:\n----\n%s----\n\n", hostp->hostname, pkt->body); #endif s = pkt->body; ch = *s++; while(ch) { line = s - 1; skip_quoted_line(s, ch); if (s[-2] == '\n') { s[-2] = '\0'; } #define sc "OPTIONS " if(strncmp(line, sc, SIZEOF(sc)-1) == 0) { #undef sc #define sc "features=" t = strstr(line, sc); if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) { t += SIZEOF(sc)-1; #undef sc am_release_feature_set(hostp->features); if((hostp->features = am_string_to_feature(t)) == NULL) { fprintf(outf, "ERROR: %s: bad features value: %s\n", hostp->hostname, line); } } continue; } #define sc "OK " if(strncmp(line, sc, SIZEOF(sc)-1) == 0) { continue; #undef sc } #define sc "ERROR " if(strncmp(line, sc, SIZEOF(sc)-1) == 0) { t = line + SIZEOF(sc) - 1; tch = t[-1]; #undef sc skip_whitespace(t, tch); /* * If the "error" is that the "noop" service is unknown, it * just means the client is "old" (does not support the service). * We can ignore this. */ if(!((hostp->features == NULL) && (pkt->type == P_NAK) && ((strcmp(t - 1, "unknown service: noop") == 0) || (strcmp(t - 1, "noop: invalid service") == 0)))) { fprintf(outf, "ERROR: %s%s: %s\n", (pkt->type == P_NAK) ? "NAK " : "", hostp->hostname, t - 1); remote_errors++; hostp->up = HOST_DONE; } continue; } fprintf(outf, "ERROR: %s: unknown response: %s\n", hostp->hostname, line); remote_errors++; hostp->up = HOST_DONE; } if(hostp->up == HOST_READY && hostp->features == NULL) { /* * The client does not support the features list, so give it an * empty one. */ dbprintf(("%s: no feature set from host %s\n", debug_prefix_time(NULL), hostp->hostname)); hostp->features = am_set_default_feature_set(); } for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { if(dp->up == DISK_ACTIVE) { dp->up = DISK_DONE; } } start_host(hostp); if(hostp->up == HOST_DONE) security_close_connection(sech, hostp->hostname); }