X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Famcheck.c;h=539397659ca8430d311049b52db5c737a4cbdced;hb=c6f0a88c567f8536c498f554285aed1f8150da18;hp=79f2d308447bde3dc1fe9a2323668376159ce693;hpb=2df780bff19c457b0debb7adc29972a0bc2a5dc2;p=debian%2Famanda diff --git a/server-src/amcheck.c b/server-src/amcheck.c index 79f2d30..5393976 100644 --- a/server-src/amcheck.c +++ b/server-src/amcheck.c @@ -24,175 +24,184 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: amcheck.c,v 1.50.2.19.2.7.2.20.2.12 2005/10/11 14:50:00 martinea Exp $ + * $Id: amcheck.c,v 1.149 2006/08/24 01:57:16 paddy_s Exp $ * * checks for common problems in server and clients */ #include "amanda.h" +#include "util.h" #include "conffile.h" -#include "statfs.h" +#include "columnar.h" +#include "fsusage.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 "util.h" +#include "server_util.h" #include "pipespawn.h" #include "amfeatures.h" - -/* - * If we don't have the new-style wait access functions, use our own, - * compatible with old-style BSD systems at least. Note that we don't - * care about the case w_stopval == WSTOPPED since we don't ask to see - * stopped processes, so should never get them from wait. - */ -#ifndef WEXITSTATUS -# define WEXITSTATUS(r) (((union wait *) &(r))->w_retcode) -# define WTERMSIG(r) (((union wait *) &(r))->w_termsig) - -# undef WIFSIGNALED -# define WIFSIGNALED(r) (((union wait *) &(r))->w_termsig != 0) -#endif +#include "device.h" +#include "property.h" +#include "timestamp.h" +#include "amxml.h" +#include "physmem.h" +#include #define BUFFER_SIZE 32768 -static int conf_ctimeout; +static time_t conf_ctimeout; static int overwrite; -dgram_t *msg = NULL; -static disklist_t *origqp; +static disklist_t origq; static uid_t uid_dumpuser; /* local functions */ -void usage P((void)); -int start_client_checks P((int fd)); -int start_server_check P((int fd, int do_localchk, int do_tapechk)); -int main P((int argc, char **argv)); -int scan_init P((int rc, int ns, int bk)); -int taperscan_slot P((int rc, char *slotstr, char *device)); -char *taper_scan P((void)); -int test_server_pgm P((FILE *outf, char *dir, char *pgm, - int suid, uid_t dumpuid)); - -void usage() +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 check_tapefile(FILE *outf, char *tapefile); +int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid); + +void +usage(void) { - error("Usage: amcheck%s [-M ] [-mawsclt] [host [disk]* ]*", versionsuffix()); + g_printf(_("Usage: amcheck [--version] [-am] [-w] [-sclt] [-M
] [--client-verbose] [-o configoption]* [host [disk]* ]*\n")); + exit(1); + /*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(argc, argv) -int argc; -char **argv; +static int client_verbose = FALSE; +static struct option long_options[] = { + {"client-verbose", 0, NULL, 1}, + {"version" , 0, NULL, 2}, + {NULL, 0, NULL, 0} +}; + +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, clientchk_pid, client_probs; - int do_localchk, do_tapechk, serverchk_pid, server_probs; - int chk_flag; - int opt, size, result_port, tempfd, mainfd; + int do_clientchk, client_probs; + int do_localchk, do_tapechk, server_probs; + pid_t clientchk_pid, serverchk_pid; + int tempfd, mainfd; + size_t size; amwait_t retstat; pid_t pid; extern int optind; - int l, n, s; 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; + char *errstr; + config_overrides_t *cfg_ovr; + char *mailer; + + /* + * Configure program for internationalization: + * 1) Only set the message locale for now. + * 2) Set textdomain for all amanda related programs to "amanda" + * We don't want to be forced to support dozens of message catalogs. + */ + setlocale(LC_MESSAGES, "C"); + textdomain("amanda"); safe_fd(-1, 0); safe_cd(); set_pname("amcheck"); - dbopen(); - - malloc_size_1 = malloc_inuse(&malloc_hist_1); + /* drop root privileges */ + if (!set_root_privs(0)) { + error(_("amcheck must be run setuid root")); + } - ap_snprintf(pid_str, sizeof(pid_str), "%ld", (long)getpid()); + /* Don't die when child closes pipe */ + signal(SIGPIPE, SIG_IGN); - erroutput_type = ERR_INTERACTIVE; + dbopen(DBG_SUBDIR_SERVER); - our_features = am_init_feature_set(); - our_feature_string = am_feature_to_string(our_features); + memset(buffer, 0, sizeof(buffer)); - /* set up dgram port first thing */ + g_snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid()); - msg = dgram_alloc(); + add_amanda_log_handler(amanda_log_stderr); - if(dgram_bind(msg, &result_port) == -1) - error("could not bind result datagram port: %s", strerror(errno)); + our_features = am_init_feature_set(); + our_feature_string = am_feature_to_string(our_features); - if(geteuid() == 0) { - /* set both real and effective uid's to real uid, likewise for gid */ - setgid(getgid()); - setuid(getuid()); - } - uid_me = getuid(); + uid_me = geteuid(); alwaysmail = mailout = overwrite = 0; do_localchk = do_tapechk = do_clientchk = 0; - chk_flag = 0; server_probs = client_probs = 0; tempfd = mainfd = -1; /* process arguments */ - while((opt = getopt(argc, argv, "M:mawsclt")) != EOF) { - switch(opt) { - case 'M': mailto=stralloc(optarg); + cfg_ovr = new_config_overrides(argc/2); + while (1) { + int option_index = 0; + int c; + c = getopt_long (argc, argv, "M:mawsclto:", long_options, &option_index); + if (c == -1) { + break; + } + + switch(c) { + case 1: client_verbose = TRUE; + break; + case 2: printf("amcheck-%s\n", VERSION); + return(0); + break; + case 'M': if (mailto) { + g_printf(_("Multiple -M options\n")); + exit(1); + } + mailto=stralloc(optarg); + if(!validate_mailto(mailto)){ + g_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 = 1; do_tapechk = 1; - chk_flag = 1; + case 's': do_localchk = do_tapechk = 1; break; case 'c': do_clientchk = 1; - chk_flag = 1; break; case 'l': do_localchk = 1; - chk_flag = 1; break; - case 'w': do_tapechk = 1; overwrite = 1; - chk_flag = 1; + case 'w': overwrite = 1; + break; + case 'o': add_config_override_opt(cfg_ovr, optarg); break; case 't': do_tapechk = 1; - chk_flag = 1; break; case '?': default: @@ -200,49 +209,105 @@ char **argv; } } argc -= optind, argv += optind; - if(! chk_flag) { + if(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(argc < 1) usage(); + if(overwrite) + do_tapechk = 1; + + set_config_overrides(cfg_ovr); + config_init(CONFIG_INIT_EXPLICIT_NAME, argv[0]); + dbrename(get_config_name(), DBG_SUBDIR_SERVER); + + conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE)); + read_diskfile(conf_diskfile, &origq); + disable_skip_disk(&origq); + amfree(conf_diskfile); - config_name = stralloc(*argv); + if (config_errors(NULL) >= CFGERR_WARNINGS) { + config_print_errors(); + if (config_errors(NULL) >= CFGERR_ERRORS) { + g_critical(_("errors processing config file")); + } + } - 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); + mailer = getconf_str(CNF_MAILER); + if ((!mailer || *mailer == '\0') && mailout == 1) { + if (alwaysmail == 1) { + g_printf(_("You can't use -a because a mailer is not defined\n")); + } else { + g_printf(_("You can't use -m because a mailer is not defined\n")); + } + exit(1); } - amfree(conffile); - 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(mailout && !mailto && + (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) { + g_printf(_("\nWARNING:No mail address configured in amanda.conf.\n")); + g_printf(_("To receive dump results by email configure the " + "\"mailto\" parameter in amanda.conf\n")); + if(alwaysmail) + g_printf(_("When using -a option please specify -Maddress also\n\n")); + else + g_printf(_("Use -Maddress instead of -m\n\n")); + exit(1); } - if((origqp = read_diskfile(conf_diskfile)) == NULL) { - error("could not load disklist %s", conf_diskfile); + if(mailout && !mailto) + { + if(getconf_seen(CNF_MAILTO) && + strlen(getconf_str(CNF_MAILTO)) > 0) { + if(!validate_mailto(getconf_str(CNF_MAILTO))){ + g_printf(_("\nMail address in amanda.conf has invalid characters")); + g_printf(_("\nNo email will be sent\n")); + mailout = 0; + } + } + else { + g_printf(_("\nNo mail address configured in amanda.conf\n")); + if(alwaysmail) + g_printf(_("When using -a option please specify -Maddress also\n\n")); + else + g_printf(_("Use -Maddress instead of -m\n\n")); + exit(1); + } + } + + conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT); + + errstr = match_disklist(&origq, argc-1, argv+1); + if (errstr) { + g_printf(_("%s"),errstr); + amfree(errstr); } - match_disklist(origqp, argc-1, argv+1); - amfree(conf_diskfile); /* - * Make sure we are running as the dump user. + * Make sure we are running as the dump user. Don't use + * check_running_as(..) here, because we want to produce more + * verbose error messages. */ dumpuser = getconf_str(CNF_DUMPUSER); if ((pw = getpwnam(dumpuser)) == NULL) { - error("cannot look up dump user \"%s\"", dumpuser); + error(_("amanda.conf has dump user configured to \"%s\", but that user does not exist."), dumpuser); + /*NOTREACHED*/ } uid_dumpuser = pw->pw_uid; if ((pw = getpwuid(uid_me)) == NULL) { - error("cannot look up my own uid (%ld)", (long)uid_me); + error(_("cannot get username for running user, uid %ld is not in your user database."), + (long)uid_me); + /*NOTREACHED*/ } +#ifdef CHECK_USERID if (uid_me != uid_dumpuser) { - error("running as user \"%s\" instead of \"%s\"", - pw->pw_name, - dumpuser); + error(_("running as user \"%s\" instead of \"%s\".\n" + "Change user to \"%s\" or change dump user to \"%s\" in amanda.conf"), + pw->pw_name, dumpuser, dumpuser, pw->pw_name); + /*NOTREACHED*/ } +#endif displayunit = getconf_str(CNF_DISPLAYUNIT); unitdivisor = getconf_unit_divisor(); @@ -258,8 +323,10 @@ char **argv; 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)); + if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) { + error(_("could not open temporary amcheck output file %s: %s. Check permissions"), tempfname, strerror(errno)); + /*NOTREACHED*/ + } unlink(tempfname); /* so it goes away on close */ amfree(tempfname); } @@ -267,8 +334,10 @@ char **argv; 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)); + if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) { + error(_("could not open amcheck server output file %s: %s. Check permissions"), mainfname, strerror(errno)); + /*NOTREACHED*/ + } unlink(mainfname); /* so it goes away on close */ amfree(mainfname); } @@ -278,11 +347,10 @@ char **argv; /* start server side checks */ - if(do_localchk || do_tapechk) { + if(do_localchk || do_tapechk) serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk); - } else { + else serverchk_pid = 0; - } /* start client side checks */ @@ -305,65 +373,49 @@ char **argv; server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat); serverchk_pid = 0; } else { - char number[NUM_STR_SIZE]; char *wait_msg = NULL; - ap_snprintf(number, sizeof(number), "%ld", (long)pid); - wait_msg = vstralloc("parent: reaped bogus pid ", number, "\n", - NULL); - for(l = 0, n = strlen(wait_msg); l < n; l += s) { - if((s = write(mainfd, wait_msg + l, n - l)) < 0) { - error("write main file: %s", strerror(errno)); - } + wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid); + if (full_write(mainfd, wait_msg, strlen(wait_msg)) < strlen(wait_msg)) { + error(_("write main file: %s"), strerror(errno)); + /*NOTREACHED*/ } amfree(wait_msg); } } - amfree(msg); /* copy temp output to main output and write tagline */ if(do_clientchk && (do_localchk || do_tapechk)) { - if(lseek(tempfd, 0, 0) == -1) - error("seek temp file: %s", strerror(errno)); + if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) { + error(_("seek temp file: %s"), strerror(errno)); + /*NOTREACHED*/ + } - while((size=read(tempfd, buffer, sizeof(buffer))) > 0) { - for(l = 0; l < size; l += s) { - if((s = write(mainfd, buffer + l, size - l)) < 0) { - error("write main file: %s", strerror(errno)); - } + while((size = full_read(tempfd, buffer, SIZEOF(buffer))) > 0) { + if (full_write(mainfd, buffer, size) < size) { + error(_("write main file: %s"), strerror(errno)); + /*NOTREACHED*/ } } - if(size < 0) - error("read temp file: %s", strerror(errno)); + if(errno != 0) { + error(_("read temp file: %s"), strerror(errno)); + /*NOTREACHED*/ + } aclose(tempfd); } - version_string = vstralloc("\n", - "(brought to you by Amanda ", version(), ")\n", - NULL); - for(l = 0, n = strlen(version_string); l < n; l += s) { - if((s = write(mainfd, version_string + l, n - l)) < 0) { - error("write main file: %s", strerror(errno)); - } + version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), VERSION); + if (full_write(mainfd, version_string, strlen(version_string)) < strlen(version_string)) { + 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; @@ -371,59 +423,61 @@ char **argv; int errfd; FILE *ferr; char *subject; - char **a; + char **a, **b; + GPtrArray *pipeargs; amwait_t retstat; - pid_t mailpid; - pid_t wpid; - int r; - int w; + size_t r; + size_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) == -1) { - error("lseek main file: %s", strerror(errno)); + 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"); + subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"), + getconf_str(CNF_ORG)); } else { - subject = stralloc2(getconf_str(CNF_ORG), - " AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"); + subject = vstrallocf( + _("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"), + getconf_str(CNF_ORG)); } - /* - * 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 = (char **) g_new0(char *, 2); + a[1] = stralloc(mailto); a[2] = NULL; } else { - n = split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " "); - a[n + 1] = NULL; + /* (note that validate_mailto doesn't allow any quotes, so this + * is really just splitting regular old strings) */ + a = split_quoted_strings(getconf_str(CNF_MAILTO)); } if((nullfd = open("/dev/null", O_RDWR)) < 0) { error("nullfd: /dev/null: %s", strerror(errno)); + /*NOTREACHED*/ } - mailpid = 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); + + /* assemble the command line for the mailer */ + pipeargs = g_ptr_array_sized_new(4); + g_ptr_array_add(pipeargs, mailer); + g_ptr_array_add(pipeargs, "-s"); + g_ptr_array_add(pipeargs, subject); + for (b = a; *b; b++) + g_ptr_array_add(pipeargs, *b); + g_ptr_array_add(pipeargs, NULL); + + pipespawnv(mailer, STDIN_PIPE | STDERR_PIPE, 0, + &mailfd, &nullfd, &errfd, + (char **)pipeargs->pdata); + + g_ptr_array_free(pipeargs, FALSE); amfree(subject); + amfree(mailto); + g_strfreev(a); + /* * There is the potential for a deadlock here since we are writing * to the process and then reading stderr, but in the normal case, @@ -431,44 +485,42 @@ char **argv; * 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, r)) != r) { - if(w < 0 && errno == EPIPE) { - strappend(extra_info, "EPIPE writing to mail process\n"); + while((r = full_read(mainfd, buffer, SIZEOF(buffer))) > 0) { + if((w = full_write(mailfd, buffer, r)) != r) { + if(errno == EPIPE) { + strappend(extra_info, _("EPIPE writing to mail process\n")); break; - } else if(w < 0) { - error("mailfd write: %s", strerror(errno)); + } else if(errno != 0) { + error(_("mailfd write: %s"), strerror(errno)); + /*NOTREACHED*/ } else { - error("mailfd write: wrote %d instead of %d", w, r); + 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 ((wpid = 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 "); - } - ap_snprintf(number, sizeof(number), "%d", ret); - strappend(err, number); + while (wait(&retstat) != -1) { + if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) { + char *mailer_error = str_exit_status("mailer", retstat); + strappend(err, mailer_error); + amfree(mailer_error); + + rc = 1; } } if (rc != 0) { @@ -476,214 +528,200 @@ char **argv; fputs(extra_info, stderr); amfree(extra_info); } - error("error running mailer %s: %s", MAILER, err); + error(_("error running mailer %s: %s"), mailer, err?err:"(unknown)"); + /*NOTREACHED*/ } } -#endif + 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; - -int scan_init(rc, ns, bk) -int rc, ns, bk; -{ - if(rc) - error("could not get changer info: %s", changer_resultstr); - - nslots = ns; - backwards = bk; - - return 0; -} - -int taperscan_slot(rc, slotstr, device) -int rc; -char *slotstr; -char *device; -{ - char *errstr; - - if(rc == 2) { - fprintf(errf, "%s: fatal slot %s: %s\n", - get_pname(), slotstr, changer_resultstr); - return 1; - } - else if(rc == 1) { - fprintf(errf, "%s: slot %s: %s\n", - get_pname(), slotstr, changer_resultstr); - return 0; - } - else { - if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL) { - fprintf(errf, "%s: slot %s: %s\n", get_pname(), slotstr, errstr); - } else { - /* got an amanda tape */ - fprintf(errf, "%s: slot %s: date %-8s label %s", - get_pname(), slotstr, datestamp, label); - if(searchlabel != NULL - && (strcmp(label, FAKE_LABEL) == 0 - || strcmp(label, searchlabel) == 0)) { - /* it's the one we are looking for, stop here */ - fprintf(errf, " (exact label match)\n"); - found_device = newstralloc(found_device, device); - found = 1; - return 1; - } - else if(!match(labelstr, label)) - fprintf(errf, " (no match)\n"); - else { - /* not an exact label match, but a labelstr match */ - /* check against tape list */ - tp = lookup_tapelabel(label); - if(tp == NULL) - fprintf(errf, " (Not in tapelist)\n"); - else if(!reusable_tape(tp)) - fprintf(errf, " (active tape)\n"); - else if(got_match == 0 && tp->datestamp == 0) { - got_match = 1; - first_match = newstralloc(first_match, slotstr); - first_match_label = newstralloc(first_match_label, label); - fprintf(errf, " (new tape)\n"); - found = 3; - found_device = newstralloc(found_device, device); - return 1; - } - else if(got_match) - fprintf(errf, " (labelstr match)\n"); - else { - got_match = 1; - first_match = newstralloc(first_match, slotstr); - first_match_label = newstralloc(first_match_label, label); - fprintf(errf, " (first labelstr match)\n"); - if(!backwards || !searchlabel) { - found = 2; - found_device = newstralloc(found_device, device); - return 1; - } - } - } - } - } - return 0; -} +static char *datestamp; +static FILE *errf = NULL; -char *taper_scan() +int check_tapefile( + FILE *outf, + char *tapefile) { - char *outslot = NULL; - - if((tp = lookup_last_reusable_tape(0)) == NULL) - searchlabel = NULL; - else - searchlabel = tp->label; - - found = 0; - got_match = 0; - - changer_find(scan_init, taperscan_slot, searchlabel); - - if(found == 2 || found == 3) - searchlabel = first_match_label; - else if(!found && got_match) { - searchlabel = first_match_label; - amfree(found_device); - if(changer_loadslot(first_match, &outslot, &found_device) == 0) { - found = 1; - } - } else if(!found) { - if(searchlabel) { - changer_resultstr = newvstralloc(changer_resultstr, - "label ", searchlabel, - " or new tape not found in rack", - NULL); - } else { - changer_resultstr = newstralloc(changer_resultstr, - "new tape not found in rack"); + struct stat statbuf; + char *quoted; + int tapebad = 0; + + if (stat(tapefile, &statbuf) == 0) { + if (!S_ISREG(statbuf.st_mode)) { + quoted = quote_string(tapefile); + g_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); + g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted); + tapebad = 1; + amfree(quoted); + } else if (access(tapefile, W_OK) != 0) { + quoted = quote_string(tapefile); + g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted); + tapebad = 1; + amfree(quoted); } } - amfree(outslot); - - return found ? found_device : NULL; + return tapebad; } -int test_server_pgm(outf, dir, pgm, suid, dumpuid) -FILE *outf; -char *dir; -char *pgm; -int suid; -uid_t dumpuid; +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); + pgm = vstralloc(dir, "/", pgm, NULL); + quoted = quote_string(pgm); if(stat(pgm, &statbuf) == -1) { - fprintf(outf, "ERROR: program %s: does not exist\n", - pgm); + g_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", - pgm); + g_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", - pgm); + g_fprintf(outf, _("ERROR: program %s: not executable\n"), + quoted); pgmbad = 1; +#ifndef SINGLE_USERID } else if (suid \ && dumpuid != 0 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) { - fprintf(outf, "WARNING: program %s: not setuid-root\n", - pgm); + g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"), + quoted); + pgmbad = 1; +#else + /* Quiet unused parameter warnings */ + (void)suid; + (void)dumpuid; +#endif /* SINGLE_USERID */ } + amfree(quoted); amfree(pgm); return pgmbad; } -int start_server_check(fd, do_localchk, do_tapechk) - int fd; - int do_localchk, do_tapechk; +/* check that the tape is a valid amanda tape + Returns TRUE if all tests passed; FALSE otherwise. */ +static gboolean test_tape_status(FILE * outf) { + int outfd; + int nullfd = -1; + pid_t devpid; + char *amcheck_device = NULL; + gchar **args; + amwait_t wait_status; + gboolean success; + + if ((nullfd = open("/dev/null", O_RDWR)) == -1) { + return FALSE; + } + + fflush(outf); + outfd = fileno(outf); + + amcheck_device = vstralloc(amlibexecdir, "/", "amcheck-device", NULL); + args = get_config_options(overwrite? 3 : 2); + args[0] = amcheck_device; /* steal the reference */ + args[1] = g_strdup(get_config_name()); + if (overwrite) + args[2] = g_strdup("-w"); + + /* run libexecdir/amcheck-device.pl, capturing STDERR and STDOUT to outf */ + devpid = pipespawnv(amcheck_device, 0, 0, + &nullfd, &outfd, &outfd, + (char **)args); + + /* and immediately wait for it to die */ + waitpid(devpid, &wait_status, 0); + + if (WIFSIGNALED(wait_status)) { + g_fprintf(outf, _("amcheck-device terminated with signal %d"), + WTERMSIG(wait_status)); + success = FALSE; + } else if (WIFEXITED(wait_status)) { + success = (WEXITSTATUS(wait_status) == 0); + } else { + success = FALSE; + } + + g_strfreev(args); + close(nullfd); + + return success; +} + +pid_t +start_server_check( + int fd, + int do_localchk, + int do_tapechk) { - char *errstr, *tapename; - generic_fs_stats_t fs; - tape_t *tp; - FILE *outf; - holdingdisk_t *hdp; - int pid; + struct fs_usage fsusage; + FILE *outf = NULL; + pid_t pid G_GNUC_UNUSED; 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; + int res; + intmax_t kb_avail, kb_needed; + off_t tape_size; + gboolean printed_small_part_size_warning = FALSE; + char *small_part_size_warning = + _(" This may create > 1000 parts, severely degrading backup/restore performance.\n" + " See http://wiki.zmanda.com/index.php/Splitsize_too_small for more information.\n"); switch(pid = fork()) { - case -1: error("could not fork server check: %s", strerror(errno)); - case 0: break; + case -1: + error(_("could not spawn a process for checking the server: %s"), strerror(errno)); + g_assert_not_reached(); + + case 0: + break; + default: return pid; } - + dup2(fd, 1); dup2(fd, 2); - + set_pname("amcheck-server"); - - amfree(msg); - + startclock(); - if((outf = fdopen(fd, "w")) == NULL) - error("fdopen %d: %s", fd, strerror(errno)); + /* server does not need root privileges, and the access() calls below use the real userid, + * so totally drop privileges at this point (making the userid equal to the dumpuser) */ + set_root_privs(-1); + + 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"); + g_fprintf(outf, _("Amanda Tape Server Host Check\n")); + g_fprintf(outf, "-----------------------------\n"); + + if (do_localchk || testtape) { + tp = lookup_tapetype(getconf_str(CNF_TAPETYPE)); + } /* * Check various server side config file settings. @@ -691,86 +729,115 @@ int start_server_check(fd, do_localchk, do_tapechk) if(do_localchk) { char *ColumnSpec; char *errstr = NULL; - tapetype_t *tp; char *lbl_templ; ColumnSpec = getconf_str(CNF_COLUMNSPEC); - if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) { - fprintf(outf, "ERROR: %s\n", errstr); + if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) { + g_fprintf(outf, _("ERROR: %s\n"), errstr); amfree(errstr); confbad = 1; } - tp = lookup_tapetype(getconf_str(CNF_TAPETYPE)); - lbl_templ = tp->lbl_templ; + 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); - } + lbl_templ = config_dir_relative(lbl_templ); if(access(lbl_templ, R_OK) == -1) { - fprintf(outf, - "ERROR: cannot access lbl_templ file %s: %s\n", + g_fprintf(outf, + _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\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"); +#if !defined(HAVE_LPR_CMD) + g_fprintf(outf, _("ERROR: lbl-templ set but no LPR command defined. You should reconfigure amanda\n and make sure it finds a lpr or lp command.\n")); confbad = 1; #endif } + + if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) < + getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) { + g_fprintf(outf, _("WARNING: flush-threshold-dumped (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"), + getconf_int(CNF_FLUSH_THRESHOLD_DUMPED), + getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED)); + } + + if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) < + getconf_int(CNF_TAPERFLUSH)) { + g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"), + getconf_int(CNF_TAPERFLUSH), + getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED)); + } + + if (getconf_int(CNF_TAPERFLUSH) > 0 && + !getconf_no_yes_all(CNF_AUTOFLUSH)) { + g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' or 'all' if taperflush (%d) is greater that 0.\n"), + getconf_int(CNF_TAPERFLUSH)); + } + + /* Double-check that 'localhost' resolves properly */ + if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) { + g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res)); + confbad = 1; + } + + if (!getconf_seen(CNF_TAPETYPE)) { + g_fprintf(outf, + _("ERROR: no tapetype specified; you must give a value for " + "the 'tapetype' parameter\n")); + confbad = 1; + } } /* * Look up the programs used on the server side. */ if(do_localchk) { - if(access(libexecdir, X_OK) == -1) { - fprintf(outf, "ERROR: program dir %s: not accessible\n", - libexecdir); + /* + * entreprise version will do planner/dumper suid check + */ + if(access(amlibexecdir, X_OK) == -1) { + quoted = quote_string(amlibexecdir); + g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."), + quoted); + g_fprintf(outf, _("Check permissions\n")); pgmbad = 1; + amfree(quoted); } else { - pgmbad = pgmbad \ - || test_server_pgm(outf, libexecdir, "planner", - 1, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, libexecdir, "dumper", - 1, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, libexecdir, "driver", - 0, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, libexecdir, "taper", - 0, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, libexecdir, "amtrmidx", - 0, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, libexecdir, "amlogroll", - 0, uid_dumpuser); + if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser)) + pgmbad = 1; + if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser)) + pgmbad = 1; + if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser)) + pgmbad = 1; + if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser)) + pgmbad = 1; + if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser)) + pgmbad = 1; + if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser)) + pgmbad = 1; } if(access(sbindir, X_OK) == -1) { - fprintf(outf, "ERROR: program dir %s: not accessible\n", + quoted = quote_string(sbindir); + g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"), sbindir); + g_fprintf(outf, _("Check permissions\n")); pgmbad = 1; + amfree(quoted); } else { - pgmbad = pgmbad \ - || test_server_pgm(outf, sbindir, "amgetconf", - 0, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, sbindir, "amcheck", - 1, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, sbindir, "amdump", - 0, uid_dumpuser); - pgmbad = pgmbad \ - || test_server_pgm(outf, sbindir, "amreport", - 0, uid_dumpuser); + 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) { - fprintf(outf, "WARNING: %s is not executable, server-compression and indexing will not work\n", - COMPRESS_PATH); + quoted = quote_string(COMPRESS_PATH); + g_fprintf(outf, _("WARNING: %s is not executable, server-compression " + "and indexing will not work. \n"),quoted); + g_fprintf(outf, _("Check permissions\n")); + amfree(quoted); } } @@ -782,19 +849,18 @@ int start_server_check(fd, do_localchk, do_tapechk) */ if(do_localchk || do_tapechk) { - char *conf_tapelist; char *tapefile; + char *newtapefile; char *tape_dir; char *lastslash; char *holdfile; + char * tapename; struct stat statbuf; - - conf_tapelist=getconf_str(CNF_TAPELIST); - if (*conf_tapelist == '/') { - tapefile = stralloc(conf_tapelist); - } else { - tapefile = stralloc2(config_dir, conf_tapelist); - } + guint64 part_size, part_cache_max_size, tape_size; + part_cache_type_t part_cache_type; + char *part_cache_dir; + + tapefile = config_dir_relative(getconf_str(CNF_TAPELIST)); /* * XXX There Really Ought to be some error-checking here... dhw */ @@ -806,87 +872,272 @@ int start_server_check(fd, do_localchk, do_tapechk) */ } if(access(tape_dir, W_OK) == -1) { - fprintf(outf, "ERROR: tapelist dir %s: not writable.\n", tape_dir); + quoted = quote_string(tape_dir); + g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"), + quoted); tapebad = 1; + amfree(quoted); } else if(stat(tapefile, &statbuf) == -1) { - fprintf(outf, "ERROR: tapefile %s: %s, you must create an empty file.\n", - tapefile, strerror(errno)); - tapebad = 1; - } - else if(access(tapefile, F_OK) != 0) { - fprintf(outf, "ERROR: can't access tape list %s\n", tapefile); - tapebad = 1; - } else if(access(tapefile, F_OK) == 0 && access(tapefile, W_OK) != 0) { - fprintf(outf, "ERROR: tape list %s: not writable\n", tapefile); - tapebad = 1; - } else if(read_tapelist(tapefile)) { - fprintf(outf, "ERROR: tape list %s: parse error\n", tapefile); - tapebad = 1; + if (errno != ENOENT) { + quoted = quote_string(tape_dir); + g_fprintf(outf, _("ERROR: tapelist %s (%s), " + "you must create an empty file.\n"), + quoted, strerror(errno)); + tapebad = 1; + amfree(quoted); + } else { + g_fprintf(outf, _("NOTE: tapelist will be created on the next run.\n")); + } + } else { + tapebad |= check_tapefile(outf, tapefile); + if (tapebad == 0 && read_tapelist(tapefile)) { + quoted = quote_string(tapefile); + g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted); + tapebad = 1; + amfree(quoted); + } + newtapefile = stralloc2(tapefile, ".new"); + tapebad |= check_tapefile(outf, newtapefile); + amfree(newtapefile); + newtapefile = stralloc2(tapefile, ".amlabel"); + tapebad |= check_tapefile(outf, newtapefile); + amfree(newtapefile); + newtapefile = stralloc2(tapefile, ".amlabel.new"); + tapebad |= check_tapefile(outf, newtapefile); + amfree(newtapefile); + newtapefile = stralloc2(tapefile, ".yesterday"); + tapebad |= check_tapefile(outf, newtapefile); + amfree(newtapefile); + newtapefile = stralloc2(tapefile, ".yesterday.new"); + tapebad |= check_tapefile(outf, newtapefile); + amfree(newtapefile); } - holdfile = vstralloc(config_dir, "/", "hold", NULL); + holdfile = config_dir_relative("hold"); if(access(holdfile, F_OK) != -1) { - fprintf(outf, "WARNING: hold file %s exists\n", holdfile); + quoted = quote_string(holdfile); + g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile); + g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n")); + g_fprintf(outf, _("You might want to delete the existing hold file\n")); + amfree(quoted); } amfree(tapefile); amfree(tape_dir); amfree(holdfile); tapename = getconf_str(CNF_TAPEDEV); - if (strncmp(tapename, "null:", 5) == 0) { - fprintf(outf, - "WARNING: tapedev is %s, dumps will be thrown away\n", - tapename); - testtape = 0; - do_tapechk = 0; + if (tapename == NULL) { + if (getconf_str(CNF_TPCHANGER) == NULL) { + g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n")); + testtape = 0; + do_tapechk = 0; + } + } + + /* check tapetype-based splitting parameters */ + part_size = tapetype_get_part_size(tp); + part_cache_type = tapetype_get_part_cache_type(tp); + part_cache_dir = tapetype_get_part_cache_dir(tp); + part_cache_max_size = tapetype_get_part_cache_max_size(tp); + + if (!tapetype_seen(tp, TAPETYPE_PART_SIZE)) { + if (tapetype_seen(tp, TAPETYPE_PART_CACHE_TYPE)) { + g_fprintf(outf, "ERROR: part-cache-type specified, but no part-size\n"); + tapebad = 1; + } + if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) { + g_fprintf(outf, "ERROR: part-cache-dir specified, but no part-size\n"); + tapebad = 1; + } + if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) { + g_fprintf(outf, "ERROR: part-cache-max-size specified, but no part-size\n"); + tapebad = 1; + } + } else { + switch (part_cache_type) { + case PART_CACHE_TYPE_DISK: + if (!tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR) + || !part_cache_dir || !*part_cache_dir) { + g_fprintf(outf, + "ERROR: part-cache-type is DISK, but no part-cache-dir specified\n"); + tapebad = 1; + } else { + if(get_fs_usage(part_cache_dir, NULL, &fsusage) == -1) { + g_fprintf(outf, "ERROR: part-cache-dir '%s': %s\n", + part_cache_dir, strerror(errno)); + tapebad = 1; + } else { + kb_avail = fsusage.fsu_bavail_top_bit_set? + 0 : fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize; + kb_needed = part_size; + if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) { + kb_needed = part_cache_max_size; + } + if (kb_avail < kb_needed) { + g_fprintf(outf, + "ERROR: part-cache-dir has %ju %sB available, but needs %ju %sB\n", + kb_avail/(uintmax_t)unitdivisor, displayunit, + kb_needed/(uintmax_t)unitdivisor, displayunit); + tapebad = 1; + } + } + } + break; + + case PART_CACHE_TYPE_MEMORY: + kb_avail = physmem_total() / 1024; + kb_needed = part_size; + if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) { + kb_needed = part_cache_max_size; + } + if (kb_avail < kb_needed) { + g_fprintf(outf, + "ERROR: system has %ju %sB memory, but part cache needs %ju %sB\n", + kb_avail/(uintmax_t)unitdivisor, displayunit, + kb_needed/(uintmax_t)unitdivisor, displayunit); + tapebad = 1; + } + + /* FALL THROUGH */ + + case PART_CACHE_TYPE_NONE: + if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) { + g_fprintf(outf, + "ERROR: part-cache-dir specified, but part-cache-type is not DISK\n"); + tapebad = 1; + } + break; + } + } + + if (tapetype_seen(tp, TAPETYPE_PART_SIZE) && part_size == 0 + && part_cache_type != PART_CACHE_TYPE_NONE) { + g_fprintf(outf, + "ERROR: part_size is zero, but part-cache-type is not 'none'\n"); + tapebad = 1; + } + + if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) { + if (part_cache_type == PART_CACHE_TYPE_NONE) { + g_fprintf(outf, + "ERROR: part-cache-max-size is specified but no part cache is in use\n"); + tapebad = 1; + } + + if (part_cache_max_size > part_size) { + g_fprintf(outf, + "WARNING: part-cache-max-size is greater than part-size\n"); + } + } + + tape_size = tapetype_get_length(tp); + if (part_size && part_size * 1000 < tape_size) { + g_fprintf(outf, + _("WARNING: part-size of %ju %sB < 0.1%% of tape length.\n"), + (uintmax_t)part_size/(uintmax_t)unitdivisor, displayunit); + if (!printed_small_part_size_warning) { + printed_small_part_size_warning = TRUE; + g_fprintf(outf, "%s", small_part_size_warning); + } + } else if (part_cache_max_size && part_cache_max_size * 1000 < tape_size) { + g_fprintf(outf, + _("WARNING: part-cache-max-size of %ju %sB < 0.1%% of tape length.\n"), + (uintmax_t)part_cache_max_size/(uintmax_t)unitdivisor, displayunit); + if (!printed_small_part_size_warning) { + printed_small_part_size_warning = TRUE; + g_fprintf(outf, "%s", small_part_size_warning); + } } } /* check available disk space */ if(do_localchk) { - for(hdp = holdingdisks; hdp != NULL; hdp = hdp->next) { - if(get_fs_stats(hdp->diskdir, &fs) == -1) { - fprintf(outf, "ERROR: holding dir %s: %s, you must create a directory.\n", - hdp->diskdir, strerror(errno)); + identlist_t il; + holdingdisk_t *hdp; + + for (il = getconf_identlist(CNF_HOLDINGDISK); + il != NULL; + il = il->next) { + hdp = lookup_holdingdisk(il->data); + quoted = quote_string(holdingdisk_get_diskdir(hdp)); + if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) { + g_fprintf(outf, _("ERROR: holding dir %s (%s), " + "you must create a directory.\n"), + quoted, strerror(errno)); disklow = 1; + amfree(quoted); + continue; } - else if(access(hdp->diskdir, W_OK) == -1) { - fprintf(outf, "ERROR: holding disk %s: not writable: %s.\n", - hdp->diskdir, strerror(errno)); + + /* do the division first to avoid potential integer overflow */ + if (fsusage.fsu_bavail_top_bit_set) + kb_avail = 0; + else + kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize; + + if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) { + g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"), + quoted, strerror(errno)); + g_fprintf(outf, _("Check permissions\n")); disklow = 1; } - else if(fs.avail == -1) { - fprintf(outf, - "WARNING: holding disk %s: available space unknown (%ld KB requested)\n", - hdp->diskdir, (long)hdp->disksize); + else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) { + g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"), + quoted, strerror(errno)); + g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted); disklow = 1; } - else if(hdp->disksize > 0) { - if(fs.avail < hdp->disksize) { - fprintf(outf, - "WARNING: holding disk %s: only %ld %sB free (%ld %sB requested)\n", - hdp->diskdir, (long)fs.avail/unitdivisor, displayunit, - (long)hdp->disksize/unitdivisor, displayunit); + else if(holdingdisk_get_disksize(hdp) > (off_t)0) { + if(kb_avail == 0) { + g_fprintf(outf, + _("WARNING: holding disk %s: " + "no space available (%lld %sB requested)\n"), quoted, + (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor), + displayunit); disklow = 1; } - else - fprintf(outf, - "Holding disk %s: %ld %sB disk space available, that's plenty\n", - hdp->diskdir, fs.avail/unitdivisor, displayunit); + else if(kb_avail < holdingdisk_get_disksize(hdp)) { + g_fprintf(outf, + _("WARNING: holding disk %s: " + "only %lld %sB available (%lld %sB requested)\n"), quoted, + (long long)(kb_avail / (off_t)unitdivisor), + displayunit, + (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor), + displayunit); + disklow = 1; + } + else { + g_fprintf(outf, + _("Holding disk %s: %lld %sB disk space available," + " using %lld %sB as requested\n"), + quoted, + (long long)(kb_avail / (off_t)unitdivisor), + displayunit, + (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor), + displayunit); + } } else { - if(fs.avail < -hdp->disksize) { - fprintf(outf, - "WARNING: holding disk %s: only %ld %sB free, using nothing\n", - hdp->diskdir, fs.avail/unitdivisor, displayunit); + if(kb_avail < -holdingdisk_get_disksize(hdp)) { + g_fprintf(outf, + _("WARNING: holding disk %s: " + "only %lld %sB free, using nothing\n"), + quoted, (long long)(kb_avail / (off_t)unitdivisor), + displayunit); + g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n")); disklow = 1; } - else - fprintf(outf, - "Holding disk %s: %ld %sB disk space available, using %ld %sB\n", - hdp->diskdir, fs.avail/unitdivisor, displayunit, - (fs.avail + hdp->disksize)/unitdivisor, displayunit); + else { + g_fprintf(outf, + _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"), + quoted, + (long long)(kb_avail/(off_t)unitdivisor), + displayunit, + (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor), + displayunit); + } } + amfree(quoted); } } @@ -899,124 +1150,81 @@ int start_server_check(fd, do_localchk, do_tapechk) 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); - } + conf_logdir = config_dir_relative(getconf_str(CNF_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 a directory.\n", - conf_logdir, strerror(errno)); + g_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", conf_logdir); + g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted); logbad = 1; } + amfree(quoted); - if(access(logfile, F_OK) == 0) { + if(logbad == 0 && access(logfile, F_OK) == 0) { testtape = 0; - logbad = 1; - if(access(logfile, W_OK) != 0) - fprintf(outf, "ERROR: log file %s: not writable\n", - logfile); + logbad = 2; + if(access(logfile, W_OK) != 0) { + quoted = quote_string(logfile); + g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted); + amfree(quoted); + } } olddir = vstralloc(conf_logdir, "/oldlog", NULL); - if (stat(olddir,&stat_old) == 0) { /* oldlog exist */ + quoted = quote_string(olddir); + if (logbad == 0 && 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", olddir); + g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"), + quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); + logbad = 1; } - if(access(olddir, W_OK) == -1) { - fprintf(outf, "ERROR: oldlog dir %s: not writable\n", olddir); + if(logbad == 0 && access(olddir, W_OK) == -1) { + g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted); + g_fprintf(outf, _("Check permissions\n")); + logbad = 1; } } - else if(lstat(olddir,&stat_old) == 0) { - fprintf(outf, "ERROR: oldlog directory \"%s\" is not a directory\n", olddir); + else if(logbad == 0 && lstat(olddir,&stat_old) == 0) { + g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"), + quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); + logbad = 1; } + amfree(quoted); - if (testtape) { + if (logbad == 0 && testtape) { logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL); if (access(logfile, F_OK) == 0) { testtape = 0; - logbad = 1; + logbad = 2; } } + amfree(olddir); amfree(logfile); amfree(conf_logdir); } if (testtape) { - /* check that the tape is a valid amanda tape */ - - 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"); - } - - if(changer_init() && (tapename = taper_scan()) == NULL) { - fprintf(outf, "ERROR: %s\n", changer_resultstr); - tapebad = 1; - } else if(tape_access(tapename,F_OK|R_OK|W_OK) == -1) { - fprintf(outf, "ERROR: %s: %s\n", tapename, strerror(errno)); - tapebad = 1; - } else if((errstr = tape_rdlabel(tapename, &datestamp, &label)) != NULL) { - fprintf(outf, "ERROR: %s: %s\n", tapename, errstr); - tapebad = 1; - } else if(strcmp(label, FAKE_LABEL) != 0) { - if(!match(labelstr, label)) { - fprintf(outf, "ERROR: label %s doesn't match labelstr \"%s\"\n", - label, labelstr); - tapebad = 1; - } - else { - tp = lookup_tapelabel(label); - if(tp == NULL) { - fprintf(outf, "ERROR: label %s match labelstr but it not listed in the tapelist file.\n", label); - tapebad = 1; - } - else if(tp != NULL && !reusable_tape(tp)) { - fprintf(outf, "ERROR: cannot overwrite active tape %s\n", - label); - tapebad = 1; - } - } - - } - - if(tapebad) { - 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"); - } - - if(!tapebad && overwrite) { - if((errstr = tape_writable(tapename)) != NULL) { - fprintf(outf, - "ERROR: tape %s label ok, but is not writable\n", - label); - tapebad = 1; - } - else fprintf(outf, "Tape %s is writable\n", label); - } - else fprintf(outf, "NOTE: skipping tape-writable test\n"); - - if(!tapebad) - fprintf(outf, "Tape %s label ok\n", label); + tapebad = !test_tape_status(outf); } 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"); + g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n")); + g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n")); + } else if (logbad == 2) { + g_fprintf(outf, _("NOTE: amdump or amflush seem to be running\n")); + g_fprintf(outf, _("NOTE: if they are not, you must run amcleanup\n")); + + /* we skipped the tape checks, but this is just a NOTE and + * should not result in a nonzero exit status, so reset logbad to 0 */ + logbad = 0; } else { - fprintf(outf, "NOTE: skipping tape checks\n"); + g_fprintf(outf, _("NOTE: skipping tape checks\n")); } /* @@ -1039,169 +1247,358 @@ int start_server_check(fd, do_localchk, do_tapechk) char *host; char *disk; int conf_tapecycle, conf_runspercycle; + identlist_t pp_scriptlist; 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", + g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"), conf_tapecycle, conf_runspercycle); } - conf_infofile = stralloc(getconf_str(CNF_INFOFILE)); - if (*conf_infofile != '/') { - char *ci = stralloc2(config_dir, conf_infofile); - amfree(conf_infofile); - conf_infofile = ci; - } - conf_indexdir = stralloc(getconf_str(CNF_INDEXDIR)); - if (*conf_indexdir != '/') { - char *ci = stralloc2(config_dir, conf_indexdir); - amfree(conf_indexdir); - conf_indexdir = ci; - } -#if TEXTDB + conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE)); + conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR)); + + quoted = quote_string(conf_infofile); if(stat(conf_infofile, &statbuf) == -1) { - fprintf(outf, "NOTE: info dir %s: does not exist\n", conf_infofile); - fprintf(outf, "NOTE: it will be created on the next run.\n"); + if (errno == ENOENT) { + g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"), + quoted); + g_fprintf(outf, _("NOTE: it will be created on the next run.\n")); + } else { + g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"), + quoted, strerror(errno)); + infobad = 1; + } amfree(conf_infofile); } else if (!S_ISDIR(statbuf.st_mode)) { - fprintf(outf, "ERROR: info dir %s: not a directory\n", conf_infofile); + g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); amfree(conf_infofile); infobad = 1; } else if (access(conf_infofile, W_OK) == -1) { - fprintf(outf, "ERROR: info dir %s: not writable\n", conf_infofile); + g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted); + g_fprintf(outf, _("Check permissions\n")); amfree(conf_infofile); infobad = 1; } else { + char *errmsg = NULL; + if (check_infofile(conf_infofile, &origq, &errmsg) == -1) { + g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg); + infobad = 1; + amfree(errmsg); + } strappend(conf_infofile, "/"); } -#endif - while(!empty(*origqp)) { - hostp = origqp->head->host; + amfree(quoted); + + 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) { - fprintf(outf, "NOTE: info dir %s: does not exist\n", - hostinfodir); - fprintf(outf, "NOTE: it will be created on the next run.\n"); + if (errno == ENOENT) { + g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"), + quoted); + g_fprintf(outf, + _("NOTE: it will be created on the next run.\n")); + } else { + g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"), + quoted, strerror(errno)); + infobad = 1; + } amfree(hostinfodir); } else if (!S_ISDIR(statbuf.st_mode)) { - fprintf(outf, "ERROR: info dir %s: not a directory\n", - hostinfodir); + g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), + quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); amfree(hostinfodir); infobad = 1; } else if (access(hostinfodir, W_OK) == -1) { - fprintf(outf, "ERROR: info dir %s: not writable\n", - hostinfodir); + g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted); + g_fprintf(outf, _("Check permissions\n")); 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) { - fprintf(outf, "NOTE: info dir %s: does not exist\n", - diskdir); - fprintf(outf, "NOTE: it will be created on the next run.\n"); + if (errno == ENOENT) { + g_fprintf(outf, _("NOTE: info dir %s does not exist\n"), + quoted); + g_fprintf(outf, + _("NOTE: it will be created on the next run.\n")); + } else { + g_fprintf(outf, _("ERROR: info dir %s (%s)\n"), + quoted, strerror(errno)); + infobad = 1; + } } else if (!S_ISDIR(statbuf.st_mode)) { - fprintf(outf, "ERROR: info dir %s: not a directory\n", - diskdir); + g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), + quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); infobad = 1; } else if (access(diskdir, W_OK) == -1) { - fprintf(outf, "ERROR: info dir %s: not writable\n", - diskdir); + g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), + quoted); + g_fprintf(outf,_("Check permissions\n")); infobad = 1; } else if(stat(infofile, &statbuf) == -1) { - fprintf(outf, "WARNING: info file %s: does not exist\n", - infofile); - fprintf(outf, "NOTE: it will be created on the next run.\n"); + if (errno == ENOENT) { + g_fprintf(outf, _("NOTE: info file %s does not exist\n"), + quotedif); + g_fprintf(outf, _("NOTE: it will be created on the next run.\n")); + } else { + g_fprintf(outf, _("ERROR: info dir %s (%s)\n"), + quoted, strerror(errno)); + infobad = 1; + } } else if (!S_ISREG(statbuf.st_mode)) { - fprintf(outf, "ERROR: info file %s: not a file\n", - infofile); + g_fprintf(outf, _("ERROR: info file %s: not a file\n"), + quotedif); + g_fprintf(outf, _("Remove the entry and create a new file\n")); infobad = 1; } else if (access(infofile, R_OK) == -1) { - fprintf(outf, "ERROR: info file %s: not readable\n", - infofile); + g_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) { - fprintf(outf, "NOTE: index dir %s: does not exist\n", - conf_indexdir); - fprintf(outf, "NOTE: it will be created on the next run.\n"); + if (errno == ENOENT) { + g_fprintf(outf, _("NOTE: index dir %s does not exist\n"), + quoted); + g_fprintf(outf, _("NOTE: it will be created on the next run.\n")); + } else { + g_fprintf(outf, _("ERROR: index dir %s (%s)\n"), + quoted, strerror(errno)); + indexbad = 1; + } amfree(conf_indexdir); } else if (!S_ISDIR(statbuf.st_mode)) { - fprintf(outf, "ERROR: index dir %s: not a directory\n", - conf_indexdir); + g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"), + quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); amfree(conf_indexdir); indexbad = 1; } else if (access(conf_indexdir, W_OK) == -1) { - fprintf(outf, "ERROR: index dir %s: not writable\n", - conf_indexdir); + g_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) { - fprintf(outf, "NOTE: index dir %s: does not exist\n", - hostindexdir); - fprintf(outf, "NOTE: it will be created on the next run.\n"); + if (errno == ENOENT) { + g_fprintf(outf, _("NOTE: index dir %s does not exist\n"), + quoted); + g_fprintf(outf, _("NOTE: it will be created on the next run.\n")); + } else { + g_fprintf(outf, _("ERROR: index dir %s (%s)\n"), + quoted, strerror(errno)); + indexbad = 1; + } amfree(hostindexdir); } else if (!S_ISDIR(statbuf.st_mode)) { - fprintf(outf, "ERROR: index dir %s: not a directory\n", - hostindexdir); + g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"), + quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); amfree(hostindexdir); indexbad = 1; } else if (access(hostindexdir, W_OK) == -1) { - fprintf(outf, "ERROR: index dir %s: not writable\n", - hostindexdir); + g_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) { - fprintf(outf, "NOTE: index dir %s: does not exist\n", - diskdir); - fprintf(outf, "NOTE: it will be created on the next run.\n"); + if (errno == ENOENT) { + g_fprintf(outf, _("NOTE: index dir %s does not exist\n"), + quoted); + g_fprintf(outf, _("NOTE: it will be created on the next run.\n")); + } else { + g_fprintf(outf, _("ERROR: index dir %s (%s)\n"), + quoted, strerror(errno)); + indexbad = 1; + } } else if (!S_ISDIR(statbuf.st_mode)) { - fprintf(outf, "ERROR: index dir %s: not a directory\n", - diskdir); + g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"), + quoted); + g_fprintf(outf, _("Remove the entry and create a new directory\n")); indexbad = 1; } else if (access(diskdir, W_OK) == -1) { - fprintf(outf, "ERROR: index dir %s: is not writable\n", - diskdir); + g_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' ) { + g_fprintf(outf, _("ERROR: server encryption program not specified\n")); + g_fprintf(outf, _("Specify \"server-custom-encrypt\" in the dumptype\n")); + pgmbad = 1; + } + else if(access(dp->srv_encrypt, X_OK) == -1) { + g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"), + dp->srv_encrypt ); + g_fprintf(outf, _("Check file type\n")); + pgmbad = 1; + } + } + if ( dp->compress == COMP_SERVER_CUST ) { + if ( dp->srvcompprog[0] == '\0' ) { + g_fprintf(outf, _("ERROR: server custom compression program " + "not specified\n")); + g_fprintf(outf, _("Specify \"server-custom-compress\" in " + "the dumptype\n")); + pgmbad = 1; + } + else if(access(dp->srvcompprog, X_OK) == -1) { + quoted = quote_string(dp->srvcompprog); + + g_fprintf(outf, _("ERROR: %s is not executable, server custom " + "compression will not work\n"), + quoted); + amfree(quoted); + g_fprintf(outf, _("Check file type\n")); + pgmbad = 1; + } + } + + /* check deprecated splitting parameters */ + if (dumptype_seen(dp->config, DUMPTYPE_TAPE_SPLITSIZE) + || dumptype_seen(dp->config, DUMPTYPE_SPLIT_DISKBUFFER) + || dumptype_seen(dp->config, DUMPTYPE_FALLBACK_SPLITSIZE)) { + tape_size = tapetype_get_length(tp); + if (dp->tape_splitsize > tape_size) { + g_fprintf(outf, + _("ERROR: %s %s: tape-splitsize > tape size\n"), + hostp->hostname, dp->name); + pgmbad = 1; + } + if (dp->tape_splitsize && dp->fallback_splitsize * 1024 > physmem_total()) { + g_fprintf(outf, + _("ERROR: %s %s: fallback-splitsize > total available memory\n"), + hostp->hostname, dp->name); + pgmbad = 1; + } + if (dp->tape_splitsize && dp->fallback_splitsize > tape_size) { + g_fprintf(outf, + _("ERROR: %s %s: fallback-splitsize > tape size\n"), + hostp->hostname, dp->name); + pgmbad = 1; + } + + /* also check for part sizes that are too small */ + if (dp->tape_splitsize && dp->tape_splitsize * 1000 < tape_size) { + g_fprintf(outf, + _("WARNING: %s %s: tape-splitsize of %ju %sB < 0.1%% of tape length.\n"), + hostp->hostname, dp->name, + (uintmax_t)dp->tape_splitsize/(uintmax_t)unitdivisor, + displayunit); + if (!printed_small_part_size_warning) { + printed_small_part_size_warning = TRUE; + g_fprintf(outf, "%s", small_part_size_warning); + } + } + + /* fallback splitsize will be used if split_diskbuffer is empty or NULL */ + if (dp->tape_splitsize != 0 && dp->fallback_splitsize != 0 && + (dp->split_diskbuffer == NULL || + dp->split_diskbuffer[0] == '\0') && + dp->fallback_splitsize * 1000 < tape_size) { + g_fprintf(outf, + _("WARNING: %s %s: fallback-splitsize of %ju %sB < 0.1%% of tape length.\n"), + hostp->hostname, dp->name, + (uintmax_t)dp->fallback_splitsize/(uintmax_t)unitdivisor, + displayunit); + if (!printed_small_part_size_warning) { + printed_small_part_size_warning = TRUE; + g_fprintf(outf, "%s", small_part_size_warning); + } + } + } + + if (dp->data_path == DATA_PATH_DIRECTTCP) { + if (dp->compress != COMP_NONE) { + g_fprintf(outf, + _("ERROR: %s %s: Can't compress directtcp data-path\n"), + hostp->hostname, dp->name); + pgmbad = 1; + } + if (dp->encrypt != ENCRYPT_NONE) { + g_fprintf(outf, + _("ERROR: %s %s: Can't encrypt directtcp data-path\n"), + hostp->hostname, dp->name); + pgmbad = 1; + } + if (dp->to_holdingdisk == HOLD_REQUIRED) { + g_fprintf(outf, + _("ERROR: %s %s: Holding disk can't be use for directtcp data-path\n"), + hostp->hostname, dp->name); + pgmbad = 1; + } + } + + for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL; + pp_scriptlist = pp_scriptlist->next) { + pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data); + g_assert(pp_script != NULL); + if (pp_script_get_execute_where(pp_script) == ES_CLIENT && + pp_script_get_execute_on(pp_script) & EXECUTE_ON_PRE_HOST_BACKUP) { + g_fprintf(outf, + _("ERROR: %s %s: Can't run pre-host-backup script on client\n"), + hostp->hostname, dp->name); + } else if (pp_script_get_execute_where(pp_script) == ES_CLIENT && + pp_script_get_execute_on(pp_script) & EXECUTE_ON_POST_HOST_BACKUP) { + g_fprintf(outf, + _("ERROR: %s %s: Can't run post-host-backup script on client\n"), + hostp->hostname, dp->name); + } + } + amfree(disk); - remove_disk(origqp, dp); + remove_disk(&origq, dp); } amfree(host); amfree(hostindexdir); @@ -1214,19 +1611,18 @@ int start_server_check(fd, do_localchk, do_tapechk) } amfree(datestamp); - amfree(label); - amfree(config_dir); - amfree(config_name); - fprintf(outf, "Server check took %s seconds\n", walltime_str(curclock())); + g_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); - } + g_debug("userbad: %d", userbad); + g_debug("confbad: %d", confbad); + g_debug("tapebad: %d", tapebad); + g_debug("disklow: %d", disklow); + g_debug("logbad: %d", logbad); + g_debug("infobad: %d", infobad); + g_debug("indexbad: %d", indexbad); + g_debug("pgmbad: %d", pgmbad); exit(userbad \ || confbad \ @@ -1236,7 +1632,7 @@ int start_server_check(fd, do_localchk, do_tapechk) || infobad \ || indexbad \ || pgmbad); - /* NOTREACHED */ + /*NOTREACHED*/ return 0; } @@ -1244,13 +1640,9 @@ int start_server_check(fd, do_localchk, do_tapechk) int remote_errors; FILE *outf; -int amanda_port; - -#ifdef KRB4_SECURITY -int kamanda_port; -#endif -static void handle_response P((proto_t *p, pkt_t *pkt)); +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) @@ -1260,24 +1652,20 @@ static void handle_response P((proto_t *p, pkt_t *pkt)); #define DISK_ACTIVE ((void *)1) #define DISK_DONE ((void *)2) -int start_host(hostp) - am_host_t *hostp; +void +start_host( + am_host_t *hostp) { disk_t *dp; char *req = NULL; - int req_len = 0; - int rc; + size_t req_len = 0; int disk_count; + const security_driver_t *secdrv; char number[NUM_STR_SIZE]; + estimate_t estimate; if(hostp->up != HOST_READY) { - return 0; - } - - if (strncmp (hostp->hostname,"localhost",9) == 0) { - fprintf(outf, - "WARNING: Usage of fully qualified hostname recommended for Client %s.\n", - hostp->hostname); + return; } /* @@ -1295,43 +1683,51 @@ int start_host(hostp) 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", + g_fprintf(outf, + _("ERROR: Client %s does not support selfcheck REQ packet.\n"), hostp->hostname); + g_fprintf(outf, _("Client might be of a very old version\n")); } if(!am_has_feature(hostp->features, fe_selfcheck_rep)) { - fprintf(outf, - "ERROR: Client %s does not support selfcheck REP packet.\n", + g_fprintf(outf, + _("ERROR: Client %s does not support selfcheck REP packet.\n"), hostp->hostname); + g_fprintf(outf, _("Client might be of a very old version\n")); } 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", + g_fprintf(outf, + _("ERROR: Client %s does not support sendsize REQ packet.\n"), hostp->hostname); + g_fprintf(outf, _("Client might be of a very old version\n")); } if(!am_has_feature(hostp->features, fe_sendsize_rep)) { - fprintf(outf, - "ERROR: Client %s does not support sendsize REP packet.\n", + g_fprintf(outf, + _("ERROR: Client %s does not support sendsize REP packet.\n"), hostp->hostname); + g_fprintf(outf, _("Client might be of a very old version\n")); } 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", + g_fprintf(outf, + _("ERROR: Client %s does not support sendbackup REQ packet.\n"), hostp->hostname); + g_fprintf(outf, _("Client might be of a very old version\n")); } if(!am_has_feature(hostp->features, fe_sendbackup_rep)) { - fprintf(outf, - "ERROR: Client %s does not support sendbackup REP packet.\n", + g_fprintf(outf, + _("ERROR: Client %s does not support sendbackup REP packet.\n"), hostp->hostname); + g_fprintf(outf, _("Client might be of a very old version\n")); } - ap_snprintf(number, sizeof(number), "%d", hostp->maxdumps); + g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps); req = vstralloc("SERVICE ", "selfcheck", "\n", "OPTIONS ", has_features ? "features=" : "", @@ -1343,89 +1739,269 @@ int start_host(hostp) has_hostname ? "hostname=" : "", has_hostname ? hostp->hostname : "", has_hostname ? ";" : "", + has_config ? "config=" : "", + has_config ? get_config_name() : "", + has_config ? ";" : "", "\n", NULL); req_len = strlen(req); - req_len += 128; /* room for SECURITY ... */ - req_len += 256; /* room for non-disk answers */ + 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; - int l_len; - char *o; - char* calcsize; - - if(dp->todo == 0) continue; - - if(dp->up != DISK_READY) { + char *es; + size_t l_len; + char *o = NULL; + char *calcsize; + char *qname, *b64disk; + char *qdevice, *b64device = NULL; + GPtrArray *errarray; + guint i; + + if(dp->up != DISK_READY || dp->todo != 1) { + continue; + } + qname = quote_string(dp->name); + + errarray = validate_optionstr(dp); + if (errarray->len > 0) { + for (i=0; i < errarray->len; i++) { + g_fprintf(outf, _("ERROR: %s:%s %s\n"), + hostp->hostname, qname, + (char *)g_ptr_array_index(errarray, i)); + } + g_ptr_array_free(errarray, TRUE); + amfree(qname); + remote_errors++; continue; + } else if (am_has_feature(hostp->features, fe_req_xml)) { + o = xml_optionstr(dp, 0); + } else { + o = optionstr(dp); + } + + b64disk = amxml_format_tag("disk", dp->name); + qdevice = quote_string(dp->device); + if (dp->device) + b64device = amxml_format_tag("diskdevice", dp->device); + if ((dp->name && qname[0] == '"') || + (dp->device && qdevice[0] == '"')) { + if(!am_has_feature(hostp->features, fe_interface_quoted_text)) { + g_fprintf(outf, + _("WARNING: %s:%s:%s host does not support quoted text\n"), + hostp->hostname, qname, qdevice); + g_fprintf(outf, _("You must upgrade amanda on the client to " + "specify a quoted text/device in the disklist, " + "or don't use quoted text for the device.\n")); + } } - o = optionstr(dp, hostp->features, outf); 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, dp->name, dp->device); + g_fprintf(outf, + _("ERROR: %s:%s (%s): selfcheck does not support device.\n"), + hostp->hostname, qname, dp->device); + g_fprintf(outf, _("You must upgrade amanda on the client to " + "specify a diskdevice in the disklist " + "or don't specify a diskdevice in the disklist.\n")); } if(!am_has_feature(hostp->features, fe_sendsize_req_device)) { - fprintf(outf, - "ERROR: %s:%s (%s): sendsize does not support device.\n", - hostp->hostname, dp->name, dp->device); + g_fprintf(outf, + _("ERROR: %s:%s (%s): sendsize does not support device.\n"), + hostp->hostname, qname, dp->device); + g_fprintf(outf, _("You must upgrade amanda on the client to " + "specify a diskdevice in the disklist" + " or don't specify a diskdevice in the disklist.\n")); } if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) { - fprintf(outf, - "ERROR: %s:%s (%s): sendbackup does not support device.\n", - hostp->hostname, dp->name, dp->device); + g_fprintf(outf, + _("ERROR: %s:%s (%s): sendbackup does not support device.\n"), + hostp->hostname, qname, dp->device); + g_fprintf(outf, _("You must upgrade amanda on the client to " + "specify a diskdevice in the disklist" + " or don't specify a diskdevice in the disklist.\n")); + } + + if (dp->data_path != DATA_PATH_AMANDA && + !am_has_feature(hostp->features, fe_xml_data_path)) { + g_fprintf(outf, + _("ERROR: Client %s does not support %s data-path\n"), + hostp->hostname, data_path_to_string(dp->data_path)); + } else if (dp->data_path == DATA_PATH_DIRECTTCP && + !am_has_feature(hostp->features, fe_xml_directtcp_list)) { + g_fprintf(outf, + _("ERROR: Client %s does not support directtcp data-path\n"), + hostp->hostname); } } - 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, dp->name); - } - 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, dp->name); - } - 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, dp->name); - dp->estimate = ES_CLIENT; + if (dp->program && + (strcmp(dp->program,"DUMP") == 0 || + strcmp(dp->program,"GNUTAR") == 0)) { + if(strcmp(dp->program, "DUMP") == 0 && + !am_has_feature(hostp->features, fe_program_dump)) { + g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"), + hostp->hostname, qname); + g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP " + "or you can use another program.\n")); + } + if(strcmp(dp->program, "GNUTAR") == 0 && + !am_has_feature(hostp->features, fe_program_gnutar)) { + g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"), + hostp->hostname, qname); + g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR " + "or you can use another program.\n")); + } + estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data); + if(estimate == ES_CALCSIZE && + !am_has_feature(hostp->features, fe_calcsize_estimate)) { + g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for " + "estimate, using CLIENT.\n"), + hostp->hostname, qname); + g_fprintf(outf, _("You must upgrade amanda on the client to use " + "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n")); + estimate = ES_CLIENT; + } + if(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)) { + g_fprintf(outf, + _("ERROR: Client %s does not support custom compression.\n"), + hostp->hostname); + g_fprintf(outf, _("You must upgrade amanda on the client to " + "use custom compression\n")); + g_fprintf(outf, _("Otherwise you can use the default client " + "compression program.\n")); + } + if(dp->encrypt == ENCRYPT_CUST ) { + if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) { + g_fprintf(outf, + _("ERROR: Client %s does not support data encryption.\n"), + hostp->hostname); + g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n")); + remote_errors++; + } else if ( dp->compress == COMP_SERVER_FAST || + dp->compress == COMP_SERVER_BEST || + dp->compress == COMP_SERVER_CUST ) { + g_fprintf(outf, + _("ERROR: %s: Client encryption with server compression " + "is not supported. See amanda.conf(5) for detail.\n"), + hostp->hostname); + remote_errors++; + } + } + if (am_has_feature(hostp->features, fe_req_xml)) { + l = vstralloc("\n" + " ", + dp->program, + "\n", NULL); + es = xml_estimate(dp->estimatelist, hostp->features); + vstrextend(&l, es, "\n", NULL); + amfree(es); + vstrextend(&l, " ", b64disk, "\n", NULL); + if (dp->device) + vstrextend(&l, " ", b64device, "\n", NULL); + vstrextend(&l, o, "\n", NULL); + } else { + if (dp->device) { + l = vstralloc(calcsize, + dp->program, " ", + qname, " ", + qdevice, + " 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_application_api) || + !am_has_feature(hostp->features, fe_req_xml)) { + g_fprintf(outf, _("ERROR: %s:%s does not support APPLICATION-API.\n"), + hostp->hostname, qname); + g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP." + " It is case sensitive\n")); + remote_errors++; + l = stralloc(""); + } else { + l = vstralloc("\n" + " APPLICATION\n", NULL); + if (dp->application) { + application_t *application; + char *xml_app; + + application = lookup_application(dp->application); + if (!application) { + g_fprintf(outf, + _("ERROR: application '%s' not found.\n"), dp->application); + } else { + char *client_name = application_get_client_name(application); + if (client_name && strlen(client_name) > 0 && + !am_has_feature(hostp->features, fe_application_client_name)) { + g_fprintf(outf, + _("WARNING: %s:%s does not support client-name in application.\n"), + hostp->hostname, qname); + } + xml_app = xml_application(dp, application, hostp->features); + vstrextend(&l, xml_app, NULL); + amfree(xml_app); + } + } + if (dp->pp_scriptlist) { + if (!am_has_feature(hostp->features, fe_pp_script)) { + g_fprintf(outf, + _("ERROR: %s:%s does not support SCRIPT-API.\n"), + hostp->hostname, qname); + } else { + identlist_t pp_scriptlist; + for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL; + pp_scriptlist = pp_scriptlist->next) { + pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data); + char *client_name = pp_script_get_client_name(pp_script);; + if (client_name && strlen(client_name) > 0 && + !am_has_feature(hostp->features, fe_script_client_name)) { + g_fprintf(outf, + _("WARNING: %s:%s does not support client-name in script.\n"), + hostp->hostname, dp->name); + } + } + } + } + es = xml_estimate(dp->estimatelist, hostp->features); + vstrextend(&l, es, "\n", NULL); + amfree(es); + vstrextend(&l, " ", b64disk, "\n", NULL); + if (dp->device) + vstrextend(&l, " ", b64device, "\n", NULL); + vstrextend(&l, o, "\n", NULL); + } } - - if(dp->estimate == ES_CALCSIZE && - am_has_feature(hostp->features, fe_selfcheck_calcsize)) - calcsize = "CALCSIZE "; - else - calcsize = ""; - - l = vstralloc(calcsize, - dp->program, " ", - dp->name, " ", - dp->device ? dp->device : "", - " 0 OPTIONS |", - o, - "\n", - NULL); + amfree(qname); + amfree(qdevice); + amfree(b64disk); + amfree(b64device); l_len = strlen(l); amfree(o); - /* - * Allow 2X for error response in return packet. - */ - if(req_len + l_len > MAX_DGRAM / 2) { - amfree(l); - break; - } + strappend(req, l); req_len += l_len; amfree(l); dp->up = DISK_ACTIVE; disk_count++; } - } else { /* noop service */ req = vstralloc("SERVICE ", "noop", "\n", @@ -1434,55 +2010,51 @@ int start_host(hostp) "\n", NULL); for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { - if(dp->todo == 0) continue; - - if(dp->up != DISK_READY) { + if(dp->up != DISK_READY || dp->todo != 1) { continue; } disk_count++; } } + if(disk_count == 0) { amfree(req); hostp->up = HOST_DONE; - return 0; + return; } -#ifdef KRB4_SECURITY - if(hostp->disks->auth == AUTH_KRB4) - rc = make_krb_request(hostp->hostname, kamanda_port, req, - hostp, conf_ctimeout, handle_response); - else -#endif - rc = make_request(hostp->hostname, amanda_port, req, - hostp, conf_ctimeout, handle_response); - - req = NULL; /* do not own this any more */ - - if(rc) { - /* couldn't resolve hostname */ - fprintf(outf, - "ERROR: %s: could not resolve hostname\n", hostp->hostname); - remote_errors++; - hostp->up = HOST_DONE; + secdrv = security_getdriver(hostp->disks->auth); + if (secdrv == NULL) { + fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"), + hostp->disks->auth, hostp->hostname); } else { - hostp->up = HOST_ACTIVE; + protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, + req, conf_ctimeout, handle_result, hostp); } - return 1; + + amfree(req); + + hostp->up = HOST_ACTIVE; } -int start_client_checks(fd) -int fd; +pid_t +start_client_checks( + int fd) { am_host_t *hostp; - disk_t *dp; - int hostcount, pid; - struct servent *amandad; + disk_t *dp, *dp1; + int hostcount; + pid_t pid; int userbad = 0; switch(pid = fork()) { - case -1: error("could not fork client check: %s", strerror(errno)); - case 0: break; + case -1: + error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno)); + /*NOTREACHED*/ + + case 0: + break; + default: return pid; } @@ -1494,71 +2066,57 @@ int fd; startclock(); - if((outf = fdopen(fd, "w")) == NULL) - error("fdopen %d: %s", fd, strerror(errno)); + 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"); - -#ifdef KRB4_SECURITY - kerberos_service_init(); -#endif - - proto_init(msg->socket, time(0), 1024); - - /* get remote service port */ - if((amandad = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL) - amanda_port = AMANDA_SERVICE_DEFAULT; - else - amanda_port = ntohs(amandad->s_port); + g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n")); + g_fprintf(outf, "--------------------------------\n"); -#ifdef KRB4_SECURITY - if((amandad = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL) - kamanda_port = KAMANDA_SERVICE_DEFAULT; - else - kamanda_port = ntohs(amandad->s_port); -#endif + run_server_global_scripts(EXECUTE_ON_PRE_AMCHECK, get_config_name()); + protocol_init(); hostcount = remote_errors = 0; - for(dp = origqp->head; dp != NULL; dp = dp->next) { + for(dp = origq.head; dp != NULL; dp = dp->next) { hostp = dp->host; - if(hostp->up == HOST_READY) { - if(start_host(hostp) == 1) { - hostcount++; - check_protocol(); + if(hostp->up == HOST_READY && dp->todo == 1) { + run_server_host_scripts(EXECUTE_ON_PRE_HOST_AMCHECK, + get_config_name(), hostp); + for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) { + run_server_dle_scripts(EXECUTE_ON_PRE_DLE_AMCHECK, + get_config_name(), dp1, -1); } + start_host(hostp); + hostcount++; + protocol_check(); } } - run_protocol(); - amfree(msg); + protocol_run(); + run_server_global_scripts(EXECUTE_ON_POST_AMCHECK, get_config_name()); - 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"); + g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."), + _("Client check: %d hosts checked in %s seconds."), + hostcount), + hostcount, walltime_str(curclock())); + g_fprintf(outf, plural(_(" %d problem found.\n"), + _(" %d problems found.\n"), remote_errors), + remote_errors); 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 */ + /*NOTREACHED*/ return 0; } -static void handle_response(p, pkt) -proto_t *p; -pkt_t *pkt; +static void +handle_result( + void * datap, + pkt_t * pkt, + security_handle_t * sech) { am_host_t *hostp; disk_t *dp; @@ -1567,104 +2125,91 @@ pkt_t *pkt; char *t; int ch; int tch; + gboolean printed_hostname = FALSE; - hostp = (am_host_t *) p->datap; + hostp = (am_host_t *)datap; hostp->up = HOST_READY; - if(p->state == S_FAILED && pkt == NULL) { - if(p->prevstate == S_REPWAIT) { - fprintf(outf, - "WARNING: %s: selfcheck reply timed out.\n", - hostp->hostname); - } - else { - fprintf(outf, - "WARNING: %s: selfcheck request timed out. Host down?\n", - hostp->hostname); - } - remote_errors++; - hostp->up = HOST_DONE; - return; - } - -#ifdef KRB4_SECURITY - if(hostp->disks->auth == AUTH_KRB4 && - !check_mutual_authenticator(host2key(hostp->hostname), pkt, p)) { - fprintf(outf, "ERROR: %s [mutual-authentication failed]\n", - hostp->hostname); + if (pkt == NULL) { + g_fprintf(outf, + _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname, + security_geterror(sech)); remote_errors++; hostp->up = HOST_DONE; return; } -#endif #if 0 - fprintf(errf, "got %sresponse from %s:\n----\n%s----\n\n", - (p->state == S_FAILED) ? "NAK " : "", hostp->hostname, pkt->body); + g_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_line(s, ch); + 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 + if(strncmp_const(line, "OPTIONS ") == 0) { -#define sc "features=" - t = strstr(line, sc); - if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) { - t += sizeof(sc)-1; -#undef sc + t = strstr(line, "features="); + if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) { + char *u = strchr(t, ';'); + if (u) + *u = '\0'; + t += SIZEOF("features=")-1; 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); + g_fprintf(outf, _("ERROR: %s: bad features value: '%s'\n"), + hostp->hostname, t); + g_fprintf(outf, _("The amfeature in the reply packet is invalid\n")); + remote_errors++; + hostp->up = HOST_DONE; } + if (u) + *u = ';'; } continue; } -#define sc "OK " - if(strncmp(line, sc, sizeof(sc)-1) == 0) { -#undef sc - continue; + if (client_verbose && !printed_hostname) { + g_fprintf(outf, "HOST %s\n", hostp->hostname); + printed_hostname = TRUE; } -#define sc "ERROR " - if(strncmp(line, sc, sizeof(sc)-1) == 0) { - t = line + sizeof(sc)-1; - tch = t[-1]; -#undef sc + if(strncmp_const(line, "OK ") == 0) { + if (client_verbose) { + g_fprintf(outf, "%s\n", line); + } + continue; + } + t = line; + if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) { 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 - && p->state == S_FAILED - && (strcmp(t - 1, "unknown service: noop") == 0 - || strcmp(t - 1, "noop: invalid service") == 0)) { - } else { - fprintf(outf, "ERROR: %s%s: %s\n", - (p->state == S_FAILED) ? "NAK " : "", - hostp->hostname, - t - 1); + if(!((hostp->features == NULL) && (pkt->type == P_NAK) + && ((strcmp(t - 1, "unknown service: noop") == 0) + || (strcmp(t - 1, "noop: invalid service") == 0)))) { + g_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", + g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"), hostp->hostname, line); remote_errors++; hostp->up = HOST_DONE; @@ -1674,8 +2219,7 @@ pkt_t *pkt; * 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)); + dbprintf(_("no feature set from host %s\n"), hostp->hostname); hostp->features = am_set_default_feature_set(); } for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { @@ -1684,4 +2228,16 @@ pkt_t *pkt; } } start_host(hostp); + if(hostp->up == HOST_DONE) { + security_close_connection(sech, hostp->hostname); + for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { + run_server_dle_scripts(EXECUTE_ON_POST_DLE_AMCHECK, + get_config_name(), dp, -1); + } + run_server_host_scripts(EXECUTE_ON_POST_HOST_AMCHECK, + get_config_name(), hostp); + } + /* try to clean up any defunct processes, since Amanda doesn't wait() for + them explicitly */ + while(waitpid(-1, NULL, WNOHANG)> 0); }