2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-2000 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: amcheck.c,v 1.149 2006/08/24 01:57:16 paddy_s Exp $
29 * checks for common problems in server and clients
43 #include "server_util.h"
44 #include "pipespawn.h"
45 #include "amfeatures.h"
48 #include "timestamp.h"
53 #define BUFFER_SIZE 32768
55 static time_t conf_ctimeout;
58 static disklist_t origq;
60 static uid_t uid_dumpuser;
65 pid_t start_client_checks(int fd);
66 pid_t start_server_check(int fd, int do_localchk, int do_tapechk);
67 int main(int argc, char **argv);
68 int check_tapefile(FILE *outf, char *tapefile);
69 int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
74 g_printf(_("Usage: amcheck [--version] [-am] [-w] [-sclt] [-M <address>] [--client-verbose] [-o configoption]* <conf> [host [disk]* ]*\n"));
79 static am_feature_t *our_features = NULL;
80 static char *our_feature_string = NULL;
81 static char *displayunit;
82 static long int unitdivisor;
84 static int client_verbose = FALSE;
85 static struct option long_options[] = {
86 {"client-verbose", 0, NULL, 1},
87 {"version" , 0, NULL, 2},
96 char buffer[BUFFER_SIZE];
98 char *mainfname = NULL;
99 char pid_str[NUM_STR_SIZE];
100 int do_clientchk, client_probs;
101 int do_localchk, do_tapechk, server_probs;
102 pid_t clientchk_pid, serverchk_pid;
112 char *tempfname = NULL;
118 config_overrides_t *cfg_ovr;
122 * Configure program for internationalization:
123 * 1) Only set the message locale for now.
124 * 2) Set textdomain for all amanda related programs to "amanda"
125 * We don't want to be forced to support dozens of message catalogs.
127 setlocale(LC_MESSAGES, "C");
128 textdomain("amanda");
133 set_pname("amcheck");
134 /* drop root privileges */
135 if (!set_root_privs(0)) {
136 error(_("amcheck must be run setuid root"));
139 /* Don't die when child closes pipe */
140 signal(SIGPIPE, SIG_IGN);
142 dbopen(DBG_SUBDIR_SERVER);
144 memset(buffer, 0, sizeof(buffer));
146 g_snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid());
148 add_amanda_log_handler(amanda_log_stderr);
150 our_features = am_init_feature_set();
151 our_feature_string = am_feature_to_string(our_features);
155 alwaysmail = mailout = overwrite = 0;
156 do_localchk = do_tapechk = do_clientchk = 0;
157 server_probs = client_probs = 0;
158 tempfd = mainfd = -1;
160 /* process arguments */
162 cfg_ovr = new_config_overrides(argc/2);
164 int option_index = 0;
166 c = getopt_long (argc, argv, "M:mawsclto:", long_options, &option_index);
172 case 1: client_verbose = TRUE;
174 case 2: printf("amcheck-%s\n", VERSION);
177 case 'M': if (mailto) {
178 g_printf(_("Multiple -M options\n"));
181 mailto=stralloc(optarg);
182 if(!validate_mailto(mailto)){
183 g_printf(_("Invalid characters in mail address\n"));
194 case 's': do_localchk = do_tapechk = 1;
196 case 'c': do_clientchk = 1;
198 case 'l': do_localchk = 1;
200 case 'w': overwrite = 1;
202 case 'o': add_config_override_opt(cfg_ovr, optarg);
204 case 't': do_tapechk = 1;
211 argc -= optind, argv += optind;
212 if(argc < 1) usage();
215 if ((do_localchk | do_clientchk | do_tapechk) == 0) {
216 /* Check everything if individual checks were not asked for */
217 do_localchk = do_clientchk = do_tapechk = 1;
223 set_config_overrides(cfg_ovr);
224 config_init(CONFIG_INIT_EXPLICIT_NAME, argv[0]);
225 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
227 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
228 read_diskfile(conf_diskfile, &origq);
229 disable_skip_disk(&origq);
230 amfree(conf_diskfile);
232 if (config_errors(NULL) >= CFGERR_WARNINGS) {
233 config_print_errors();
234 if (config_errors(NULL) >= CFGERR_ERRORS) {
235 g_critical(_("errors processing config file"));
239 mailer = getconf_str(CNF_MAILER);
240 if ((!mailer || *mailer == '\0') && mailout == 1) {
241 if (alwaysmail == 1) {
242 g_printf(_("You can't use -a because a mailer is not defined\n"));
244 g_printf(_("You can't use -m because a mailer is not defined\n"));
248 if(mailout && !mailto &&
249 (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
250 g_printf(_("\nWARNING:No mail address configured in amanda.conf.\n"));
251 g_printf(_("To receive dump results by email configure the "
252 "\"mailto\" parameter in amanda.conf\n"));
254 g_printf(_("When using -a option please specify -Maddress also\n\n"));
256 g_printf(_("Use -Maddress instead of -m\n\n"));
259 if(mailout && !mailto)
261 if(getconf_seen(CNF_MAILTO) &&
262 strlen(getconf_str(CNF_MAILTO)) > 0) {
263 if(!validate_mailto(getconf_str(CNF_MAILTO))){
264 g_printf(_("\nMail address in amanda.conf has invalid characters"));
265 g_printf(_("\nNo email will be sent\n"));
270 g_printf(_("\nNo mail address configured in amanda.conf\n"));
272 g_printf(_("When using -a option please specify -Maddress also\n\n"));
274 g_printf(_("Use -Maddress instead of -m\n\n"));
279 conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
281 errstr = match_disklist(&origq, argc-1, argv+1);
283 g_printf(_("%s"),errstr);
288 * Make sure we are running as the dump user. Don't use
289 * check_running_as(..) here, because we want to produce more
290 * verbose error messages.
292 dumpuser = getconf_str(CNF_DUMPUSER);
293 if ((pw = getpwnam(dumpuser)) == NULL) {
294 error(_("amanda.conf has dump user configured to \"%s\", but that user does not exist."), dumpuser);
297 uid_dumpuser = pw->pw_uid;
298 if ((pw = getpwuid(uid_me)) == NULL) {
299 error(_("cannot get username for running user, uid %ld is not in your user database."),
304 if (uid_me != uid_dumpuser) {
305 error(_("running as user \"%s\" instead of \"%s\".\n"
306 "Change user to \"%s\" or change dump user to \"%s\" in amanda.conf"),
307 pw->pw_name, dumpuser, dumpuser, pw->pw_name);
312 displayunit = getconf_str(CNF_DISPLAYUNIT);
313 unitdivisor = getconf_unit_divisor();
316 * If both server and client side checks are being done, the server
317 * check output goes to the main output, while the client check output
318 * goes to a temporary file and is copied to the main output when done.
320 * If the output is to be mailed, the main output is also a disk file,
321 * otherwise it is stdout.
323 if(do_clientchk && (do_localchk || do_tapechk)) {
324 /* we need the temp file */
325 tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
326 if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
327 error(_("could not open temporary amcheck output file %s: %s. Check permissions"), tempfname, strerror(errno));
330 unlink(tempfname); /* so it goes away on close */
335 /* the main fd is a file too */
336 mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
337 if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
338 error(_("could not open amcheck server output file %s: %s. Check permissions"), mainfname, strerror(errno));
341 unlink(mainfname); /* so it goes away on close */
345 /* just use stdout */
348 /* start server side checks */
350 if(do_localchk || do_tapechk)
351 serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
355 /* start client side checks */
358 clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
363 /* wait for child processes and note any problems */
366 if((pid = wait(&retstat)) == -1) {
367 if(errno == EINTR) continue;
369 } else if(pid == clientchk_pid) {
370 client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
372 } else if(pid == serverchk_pid) {
373 server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
376 char *wait_msg = NULL;
378 wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid);
379 if (full_write(mainfd, wait_msg, strlen(wait_msg)) < strlen(wait_msg)) {
380 error(_("write main file: %s"), strerror(errno));
387 /* copy temp output to main output and write tagline */
389 if(do_clientchk && (do_localchk || do_tapechk)) {
390 if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) {
391 error(_("seek temp file: %s"), strerror(errno));
395 while((size = full_read(tempfd, buffer, SIZEOF(buffer))) > 0) {
396 if (full_write(mainfd, buffer, size) < size) {
397 error(_("write main file: %s"), strerror(errno));
402 error(_("read temp file: %s"), strerror(errno));
408 version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), VERSION);
409 if (full_write(mainfd, version_string, strlen(version_string)) < strlen(version_string)) {
410 error(_("write main file: %s"), strerror(errno));
413 amfree(version_string);
414 amfree(our_feature_string);
415 am_release_feature_set(our_features);
418 /* send mail if requested, but only if there were problems */
420 if((server_probs || client_probs || alwaysmail) && mailout) {
432 char *extra_info = NULL;
437 if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
438 error(_("lseek main file: %s"), strerror(errno));
441 if(alwaysmail && !(server_probs || client_probs)) {
442 subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"),
443 getconf_str(CNF_ORG));
445 subject = vstrallocf(
446 _("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"),
447 getconf_str(CNF_ORG));
450 a = (char **) g_new0(char *, 2);
451 a[1] = stralloc(mailto);
454 /* (note that validate_mailto doesn't allow any quotes, so this
455 * is really just splitting regular old strings) */
456 a = split_quoted_strings(getconf_str(CNF_MAILTO));
458 if((nullfd = open("/dev/null", O_RDWR)) < 0) {
459 error("nullfd: /dev/null: %s", strerror(errno));
463 /* assemble the command line for the mailer */
464 pipeargs = g_ptr_array_sized_new(4);
465 g_ptr_array_add(pipeargs, mailer);
466 g_ptr_array_add(pipeargs, "-s");
467 g_ptr_array_add(pipeargs, subject);
469 g_ptr_array_add(pipeargs, *b);
470 g_ptr_array_add(pipeargs, NULL);
472 pipespawnv(mailer, STDIN_PIPE | STDERR_PIPE, 0,
473 &mailfd, &nullfd, &errfd,
474 (char **)pipeargs->pdata);
476 g_ptr_array_free(pipeargs, FALSE);
482 * There is the potential for a deadlock here since we are writing
483 * to the process and then reading stderr, but in the normal case,
484 * nothing should be coming back to us, and hopefully in error
485 * cases, the pipe will break and we will exit out of the loop.
487 signal(SIGPIPE, SIG_IGN);
488 while((r = full_read(mainfd, buffer, SIZEOF(buffer))) > 0) {
489 if((w = full_write(mailfd, buffer, r)) != r) {
491 strappend(extra_info, _("EPIPE writing to mail process\n"));
493 } else if(errno != 0) {
494 error(_("mailfd write: %s"), strerror(errno));
497 error(_("mailfd write: wrote %zd instead of %zd"), w, r);
503 ferr = fdopen(errfd, "r");
505 error(_("Can't fdopen: %s"), strerror(errno));
508 for(; (line = agets(ferr)) != NULL; free(line)) {
511 strappend(extra_info, line);
512 strappend(extra_info, "\n");
517 while (wait(&retstat) != -1) {
518 if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) {
519 char *mailer_error = str_exit_status("mailer", retstat);
520 strappend(err, mailer_error);
521 amfree(mailer_error);
528 fputs(extra_info, stderr);
531 error(_("error running mailer %s: %s"), mailer, err?err:"(unknown)");
537 return (server_probs || client_probs);
540 /* --------------------------------------------------- */
542 static char *datestamp;
543 static FILE *errf = NULL;
553 if (stat(tapefile, &statbuf) == 0) {
554 if (!S_ISREG(statbuf.st_mode)) {
555 quoted = quote_string(tapefile);
556 g_fprintf(outf, _("ERROR: tapelist %s: should be a regular file.\n"),
560 } else if (access(tapefile, F_OK) != 0) {
561 quoted = quote_string(tapefile);
562 g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted);
565 } else if (access(tapefile, W_OK) != 0) {
566 quoted = quote_string(tapefile);
567 g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted);
587 pgm = vstralloc(dir, "/", pgm, NULL);
588 quoted = quote_string(pgm);
589 if(stat(pgm, &statbuf) == -1) {
590 g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
593 } else if (!S_ISREG(statbuf.st_mode)) {
594 g_fprintf(outf, _("ERROR: program %s: not a file\n"),
597 } else if (access(pgm, X_OK) == -1) {
598 g_fprintf(outf, _("ERROR: program %s: not executable\n"),
601 #ifndef SINGLE_USERID
604 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
605 g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"),
609 /* Quiet unused parameter warnings */
612 #endif /* SINGLE_USERID */
619 /* check that the tape is a valid amanda tape
620 Returns TRUE if all tests passed; FALSE otherwise. */
621 static gboolean test_tape_status(FILE * outf) {
625 char *amcheck_device = NULL;
627 amwait_t wait_status;
630 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
635 outfd = fileno(outf);
637 amcheck_device = vstralloc(amlibexecdir, "/", "amcheck-device", NULL);
638 args = get_config_options(overwrite? 3 : 2);
639 args[0] = amcheck_device; /* steal the reference */
640 args[1] = g_strdup(get_config_name());
642 args[2] = g_strdup("-w");
644 /* run libexecdir/amcheck-device.pl, capturing STDERR and STDOUT to outf */
645 devpid = pipespawnv(amcheck_device, 0, 0,
646 &nullfd, &outfd, &outfd,
649 /* and immediately wait for it to die */
650 waitpid(devpid, &wait_status, 0);
652 if (WIFSIGNALED(wait_status)) {
653 g_fprintf(outf, _("amcheck-device terminated with signal %d"),
654 WTERMSIG(wait_status));
656 } else if (WIFEXITED(wait_status)) {
657 success = (WEXITSTATUS(wait_status) == 0);
674 struct fs_usage fsusage;
676 pid_t pid G_GNUC_UNUSED;
677 int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
678 int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
679 int testtape = do_tapechk;
680 tapetype_t *tp = NULL;
683 intmax_t kb_avail, kb_needed;
685 gboolean printed_small_part_size_warning = FALSE;
686 char *small_part_size_warning =
687 _(" This may create > 1000 parts, severely degrading backup/restore performance.\n"
688 " See http://wiki.zmanda.com/index.php/Splitsize_too_small for more information.\n");
690 switch(pid = fork()) {
692 error(_("could not spawn a process for checking the server: %s"), strerror(errno));
693 g_assert_not_reached();
705 set_pname("amcheck-server");
709 /* server does not need root privileges, and the access() calls below use the real userid,
710 * so totally drop privileges at this point (making the userid equal to the dumpuser) */
713 if((outf = fdopen(fd, "w")) == NULL) {
714 error(_("fdopen %d: %s"), fd, strerror(errno));
719 g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
720 g_fprintf(outf, "-----------------------------\n");
722 if (do_localchk || testtape) {
723 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
727 * Check various server side config file settings.
734 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
735 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
736 g_fprintf(outf, _("ERROR: %s\n"), errstr);
740 lbl_templ = tapetype_get_lbl_templ(tp);
741 if(strcmp(lbl_templ, "") != 0) {
742 lbl_templ = config_dir_relative(lbl_templ);
743 if(access(lbl_templ, R_OK) == -1) {
745 _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
750 #if !defined(HAVE_LPR_CMD)
751 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"));
756 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
757 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) {
758 g_fprintf(outf, _("WARNING: flush-threshold-dumped (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"),
759 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED),
760 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
763 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
764 getconf_int(CNF_TAPERFLUSH)) {
765 g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"),
766 getconf_int(CNF_TAPERFLUSH),
767 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
770 if (getconf_int(CNF_TAPERFLUSH) > 0 &&
771 !getconf_no_yes_all(CNF_AUTOFLUSH)) {
772 g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' or 'all' if taperflush (%d) is greater that 0.\n"),
773 getconf_int(CNF_TAPERFLUSH));
776 /* Double-check that 'localhost' resolves properly */
777 if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) {
778 g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
782 if (!getconf_seen(CNF_TAPETYPE)) {
784 _("ERROR: no tapetype specified; you must give a value for "
785 "the 'tapetype' parameter\n"));
791 * Look up the programs used on the server side.
795 * entreprise version will do planner/dumper suid check
797 if(access(amlibexecdir, X_OK) == -1) {
798 quoted = quote_string(amlibexecdir);
799 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
801 g_fprintf(outf, _("Check permissions\n"));
805 if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
807 if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
809 if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
811 if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
813 if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
815 if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
818 if(access(sbindir, X_OK) == -1) {
819 quoted = quote_string(sbindir);
820 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
822 g_fprintf(outf, _("Check permissions\n"));
826 if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
828 if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
830 if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
832 if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
835 if(access(COMPRESS_PATH, X_OK) == -1) {
836 quoted = quote_string(COMPRESS_PATH);
837 g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
838 "and indexing will not work. \n"),quoted);
839 g_fprintf(outf, _("Check permissions\n"));
845 * Check that the directory for the tapelist file is writable, as well
846 * as the tapelist file itself (if it already exists). Also, check for
847 * a "hold" file (just because it is convenient to do it here) and warn
848 * if tapedev is set to the null device.
851 if(do_localchk || do_tapechk) {
859 guint64 part_size, part_cache_max_size, tape_size;
860 part_cache_type_t part_cache_type;
861 char *part_cache_dir;
863 tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
865 * XXX There Really Ought to be some error-checking here... dhw
867 tape_dir = stralloc(tapefile);
868 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
871 * else whine Really Loudly about a path with no slashes??!?
874 if(access(tape_dir, W_OK) == -1) {
875 quoted = quote_string(tape_dir);
876 g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"),
881 else if(stat(tapefile, &statbuf) == -1) {
882 if (errno != ENOENT) {
883 quoted = quote_string(tape_dir);
884 g_fprintf(outf, _("ERROR: tapelist %s (%s), "
885 "you must create an empty file.\n"),
886 quoted, strerror(errno));
890 g_fprintf(outf, _("NOTE: tapelist will be created on the next run.\n"));
893 tapebad |= check_tapefile(outf, tapefile);
894 if (tapebad == 0 && read_tapelist(tapefile)) {
895 quoted = quote_string(tapefile);
896 g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
900 newtapefile = stralloc2(tapefile, ".new");
901 tapebad |= check_tapefile(outf, newtapefile);
903 newtapefile = stralloc2(tapefile, ".amlabel");
904 tapebad |= check_tapefile(outf, newtapefile);
906 newtapefile = stralloc2(tapefile, ".amlabel.new");
907 tapebad |= check_tapefile(outf, newtapefile);
909 newtapefile = stralloc2(tapefile, ".yesterday");
910 tapebad |= check_tapefile(outf, newtapefile);
912 newtapefile = stralloc2(tapefile, ".yesterday.new");
913 tapebad |= check_tapefile(outf, newtapefile);
916 holdfile = config_dir_relative("hold");
917 if(access(holdfile, F_OK) != -1) {
918 quoted = quote_string(holdfile);
919 g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
920 g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
921 g_fprintf(outf, _("You might want to delete the existing hold file\n"));
927 tapename = getconf_str(CNF_TAPEDEV);
928 if (tapename == NULL) {
929 if (getconf_str(CNF_TPCHANGER) == NULL) {
930 g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
936 /* check tapetype-based splitting parameters */
937 part_size = tapetype_get_part_size(tp);
938 part_cache_type = tapetype_get_part_cache_type(tp);
939 part_cache_dir = tapetype_get_part_cache_dir(tp);
940 part_cache_max_size = tapetype_get_part_cache_max_size(tp);
942 if (!tapetype_seen(tp, TAPETYPE_PART_SIZE)) {
943 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_TYPE)) {
944 g_fprintf(outf, "ERROR: part-cache-type specified, but no part-size\n");
947 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
948 g_fprintf(outf, "ERROR: part-cache-dir specified, but no part-size\n");
951 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
952 g_fprintf(outf, "ERROR: part-cache-max-size specified, but no part-size\n");
956 switch (part_cache_type) {
957 case PART_CACHE_TYPE_DISK:
958 if (!tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)
959 || !part_cache_dir || !*part_cache_dir) {
961 "ERROR: part-cache-type is DISK, but no part-cache-dir specified\n");
964 if(get_fs_usage(part_cache_dir, NULL, &fsusage) == -1) {
965 g_fprintf(outf, "ERROR: part-cache-dir '%s': %s\n",
966 part_cache_dir, strerror(errno));
969 kb_avail = fsusage.fsu_bavail_top_bit_set?
970 0 : fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
971 kb_needed = part_size;
972 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
973 kb_needed = part_cache_max_size;
975 if (kb_avail < kb_needed) {
977 "ERROR: part-cache-dir has %ju %sB available, but needs %ju %sB\n",
978 kb_avail/(uintmax_t)unitdivisor, displayunit,
979 kb_needed/(uintmax_t)unitdivisor, displayunit);
986 case PART_CACHE_TYPE_MEMORY:
987 kb_avail = physmem_total() / 1024;
988 kb_needed = part_size;
989 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
990 kb_needed = part_cache_max_size;
992 if (kb_avail < kb_needed) {
994 "ERROR: system has %ju %sB memory, but part cache needs %ju %sB\n",
995 kb_avail/(uintmax_t)unitdivisor, displayunit,
996 kb_needed/(uintmax_t)unitdivisor, displayunit);
1002 case PART_CACHE_TYPE_NONE:
1003 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
1005 "ERROR: part-cache-dir specified, but part-cache-type is not DISK\n");
1012 if (tapetype_seen(tp, TAPETYPE_PART_SIZE) && part_size == 0
1013 && part_cache_type != PART_CACHE_TYPE_NONE) {
1015 "ERROR: part_size is zero, but part-cache-type is not 'none'\n");
1019 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
1020 if (part_cache_type == PART_CACHE_TYPE_NONE) {
1022 "ERROR: part-cache-max-size is specified but no part cache is in use\n");
1026 if (part_cache_max_size > part_size) {
1028 "WARNING: part-cache-max-size is greater than part-size\n");
1032 tape_size = tapetype_get_length(tp);
1033 if (part_size && part_size * 1000 < tape_size) {
1035 _("WARNING: part-size of %ju %sB < 0.1%% of tape length.\n"),
1036 (uintmax_t)part_size/(uintmax_t)unitdivisor, displayunit);
1037 if (!printed_small_part_size_warning) {
1038 printed_small_part_size_warning = TRUE;
1039 g_fprintf(outf, "%s", small_part_size_warning);
1041 } else if (part_cache_max_size && part_cache_max_size * 1000 < tape_size) {
1043 _("WARNING: part-cache-max-size of %ju %sB < 0.1%% of tape length.\n"),
1044 (uintmax_t)part_cache_max_size/(uintmax_t)unitdivisor, displayunit);
1045 if (!printed_small_part_size_warning) {
1046 printed_small_part_size_warning = TRUE;
1047 g_fprintf(outf, "%s", small_part_size_warning);
1052 /* check available disk space */
1058 for (il = getconf_identlist(CNF_HOLDINGDISK);
1061 hdp = lookup_holdingdisk(il->data);
1062 quoted = quote_string(holdingdisk_get_diskdir(hdp));
1063 if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
1064 g_fprintf(outf, _("ERROR: holding dir %s (%s), "
1065 "you must create a directory.\n"),
1066 quoted, strerror(errno));
1072 /* do the division first to avoid potential integer overflow */
1073 if (fsusage.fsu_bavail_top_bit_set)
1076 kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
1078 if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
1079 g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
1080 quoted, strerror(errno));
1081 g_fprintf(outf, _("Check permissions\n"));
1084 else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
1085 g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
1086 quoted, strerror(errno));
1087 g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
1090 else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
1093 _("WARNING: holding disk %s: "
1094 "no space available (%lld %sB requested)\n"), quoted,
1095 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1099 else if(kb_avail < holdingdisk_get_disksize(hdp)) {
1101 _("WARNING: holding disk %s: "
1102 "only %lld %sB available (%lld %sB requested)\n"), quoted,
1103 (long long)(kb_avail / (off_t)unitdivisor),
1105 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1111 _("Holding disk %s: %lld %sB disk space available,"
1112 " using %lld %sB as requested\n"),
1114 (long long)(kb_avail / (off_t)unitdivisor),
1116 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1121 if(kb_avail < -holdingdisk_get_disksize(hdp)) {
1123 _("WARNING: holding disk %s: "
1124 "only %lld %sB free, using nothing\n"),
1125 quoted, (long long)(kb_avail / (off_t)unitdivisor),
1127 g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
1132 _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
1134 (long long)(kb_avail/(off_t)unitdivisor),
1136 (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
1144 /* check that the log file is writable if it already exists */
1150 struct stat stat_old;
1151 struct stat statbuf;
1153 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
1154 logfile = vstralloc(conf_logdir, "/log", NULL);
1156 quoted = quote_string(conf_logdir);
1157 if(stat(conf_logdir, &statbuf) == -1) {
1158 g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
1159 quoted, strerror(errno));
1162 else if(access(conf_logdir, W_OK) == -1) {
1163 g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
1168 if(logbad == 0 && access(logfile, F_OK) == 0) {
1171 if(access(logfile, W_OK) != 0) {
1172 quoted = quote_string(logfile);
1173 g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
1178 olddir = vstralloc(conf_logdir, "/oldlog", NULL);
1179 quoted = quote_string(olddir);
1180 if (logbad == 0 && stat(olddir,&stat_old) == 0) { /* oldlog exist */
1181 if(!(S_ISDIR(stat_old.st_mode))) {
1182 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1184 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1187 if(logbad == 0 && access(olddir, W_OK) == -1) {
1188 g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
1189 g_fprintf(outf, _("Check permissions\n"));
1193 else if(logbad == 0 && lstat(olddir,&stat_old) == 0) {
1194 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1196 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1201 if (logbad == 0 && testtape) {
1202 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
1203 if (access(logfile, F_OK) == 0) {
1211 amfree(conf_logdir);
1215 tapebad = !test_tape_status(outf);
1216 } else if (do_tapechk) {
1217 g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
1218 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1219 } else if (logbad == 2) {
1220 g_fprintf(outf, _("NOTE: amdump or amflush seem to be running\n"));
1221 g_fprintf(outf, _("NOTE: if they are not, you must run amcleanup\n"));
1223 /* we skipped the tape checks, but this is just a NOTE and
1224 * should not result in a nonzero exit status, so reset logbad to 0 */
1227 g_fprintf(outf, _("NOTE: skipping tape checks\n"));
1231 * See if the information file and index directory for each client
1232 * and disk is OK. Since we may be seeing clients and/or disks for
1233 * the first time, these are just warnings, not errors.
1236 char *conf_infofile;
1237 char *conf_indexdir;
1238 char *hostinfodir = NULL;
1239 char *hostindexdir = NULL;
1240 char *diskdir = NULL;
1241 char *infofile = NULL;
1242 struct stat statbuf;
1245 int indexdir_checked = 0;
1246 int hostindexdir_checked = 0;
1249 int conf_tapecycle, conf_runspercycle;
1250 identlist_t pp_scriptlist;
1252 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1253 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1255 if(conf_tapecycle <= conf_runspercycle) {
1256 g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
1257 conf_tapecycle, conf_runspercycle);
1260 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
1261 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
1263 quoted = quote_string(conf_infofile);
1264 if(stat(conf_infofile, &statbuf) == -1) {
1265 if (errno == ENOENT) {
1266 g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
1268 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1270 g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
1271 quoted, strerror(errno));
1274 amfree(conf_infofile);
1275 } else if (!S_ISDIR(statbuf.st_mode)) {
1276 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
1277 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1278 amfree(conf_infofile);
1280 } else if (access(conf_infofile, W_OK) == -1) {
1281 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1282 g_fprintf(outf, _("Check permissions\n"));
1283 amfree(conf_infofile);
1286 char *errmsg = NULL;
1287 if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1288 g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1292 strappend(conf_infofile, "/");
1296 while(!empty(origq)) {
1297 hostp = origq.head->host;
1298 host = sanitise_filename(hostp->hostname);
1300 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1301 quoted = quote_string(hostinfodir);
1302 if(stat(hostinfodir, &statbuf) == -1) {
1303 if (errno == ENOENT) {
1304 g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
1307 _("NOTE: it will be created on the next run.\n"));
1309 g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
1310 quoted, strerror(errno));
1313 amfree(hostinfodir);
1314 } else if (!S_ISDIR(statbuf.st_mode)) {
1315 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1317 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1318 amfree(hostinfodir);
1320 } else if (access(hostinfodir, W_OK) == -1) {
1321 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1322 g_fprintf(outf, _("Check permissions\n"));
1323 amfree(hostinfodir);
1326 strappend(hostinfodir, "/");
1330 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1331 disk = sanitise_filename(dp->name);
1335 diskdir = newstralloc2(diskdir, hostinfodir, disk);
1336 infofile = vstralloc(diskdir, "/", "info", NULL);
1337 quoted = quote_string(diskdir);
1338 quotedif = quote_string(infofile);
1339 if(stat(diskdir, &statbuf) == -1) {
1340 if (errno == ENOENT) {
1341 g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
1344 _("NOTE: it will be created on the next run.\n"));
1346 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1347 quoted, strerror(errno));
1350 } else if (!S_ISDIR(statbuf.st_mode)) {
1351 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1353 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1355 } else if (access(diskdir, W_OK) == -1) {
1356 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
1358 g_fprintf(outf,_("Check permissions\n"));
1360 } else if(stat(infofile, &statbuf) == -1) {
1361 if (errno == ENOENT) {
1362 g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
1364 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1366 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1367 quoted, strerror(errno));
1370 } else if (!S_ISREG(statbuf.st_mode)) {
1371 g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
1373 g_fprintf(outf, _("Remove the entry and create a new file\n"));
1375 } else if (access(infofile, R_OK) == -1) {
1376 g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
1385 if(! indexdir_checked) {
1386 quoted = quote_string(conf_indexdir);
1387 if(stat(conf_indexdir, &statbuf) == -1) {
1388 if (errno == ENOENT) {
1389 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1391 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1393 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1394 quoted, strerror(errno));
1397 amfree(conf_indexdir);
1398 } else if (!S_ISDIR(statbuf.st_mode)) {
1399 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1401 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1402 amfree(conf_indexdir);
1404 } else if (access(conf_indexdir, W_OK) == -1) {
1405 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1407 amfree(conf_indexdir);
1410 strappend(conf_indexdir, "/");
1412 indexdir_checked = 1;
1416 if(! hostindexdir_checked) {
1417 hostindexdir = stralloc2(conf_indexdir, host);
1418 quoted = quote_string(hostindexdir);
1419 if(stat(hostindexdir, &statbuf) == -1) {
1420 if (errno == ENOENT) {
1421 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1423 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1425 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1426 quoted, strerror(errno));
1429 amfree(hostindexdir);
1430 } else if (!S_ISDIR(statbuf.st_mode)) {
1431 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1433 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1434 amfree(hostindexdir);
1436 } else if (access(hostindexdir, W_OK) == -1) {
1437 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1439 amfree(hostindexdir);
1442 strappend(hostindexdir, "/");
1444 hostindexdir_checked = 1;
1448 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1449 quoted = quote_string(diskdir);
1450 if(stat(diskdir, &statbuf) == -1) {
1451 if (errno == ENOENT) {
1452 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1454 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1456 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1457 quoted, strerror(errno));
1460 } else if (!S_ISDIR(statbuf.st_mode)) {
1461 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1463 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1465 } else if (access(diskdir, W_OK) == -1) {
1466 g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
1475 if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1476 if ( dp->srv_encrypt[0] == '\0' ) {
1477 g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
1478 g_fprintf(outf, _("Specify \"server-custom-encrypt\" in the dumptype\n"));
1481 else if(access(dp->srv_encrypt, X_OK) == -1) {
1482 g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
1484 g_fprintf(outf, _("Check file type\n"));
1488 if ( dp->compress == COMP_SERVER_CUST ) {
1489 if ( dp->srvcompprog[0] == '\0' ) {
1490 g_fprintf(outf, _("ERROR: server custom compression program "
1491 "not specified\n"));
1492 g_fprintf(outf, _("Specify \"server-custom-compress\" in "
1496 else if(access(dp->srvcompprog, X_OK) == -1) {
1497 quoted = quote_string(dp->srvcompprog);
1499 g_fprintf(outf, _("ERROR: %s is not executable, server custom "
1500 "compression will not work\n"),
1503 g_fprintf(outf, _("Check file type\n"));
1508 /* check deprecated splitting parameters */
1509 if (dumptype_seen(dp->config, DUMPTYPE_TAPE_SPLITSIZE)
1510 || dumptype_seen(dp->config, DUMPTYPE_SPLIT_DISKBUFFER)
1511 || dumptype_seen(dp->config, DUMPTYPE_FALLBACK_SPLITSIZE)) {
1512 tape_size = tapetype_get_length(tp);
1513 if (dp->tape_splitsize > tape_size) {
1515 _("ERROR: %s %s: tape-splitsize > tape size\n"),
1516 hostp->hostname, dp->name);
1519 if (dp->tape_splitsize && dp->fallback_splitsize * 1024 > physmem_total()) {
1521 _("ERROR: %s %s: fallback-splitsize > total available memory\n"),
1522 hostp->hostname, dp->name);
1525 if (dp->tape_splitsize && dp->fallback_splitsize > tape_size) {
1527 _("ERROR: %s %s: fallback-splitsize > tape size\n"),
1528 hostp->hostname, dp->name);
1532 /* also check for part sizes that are too small */
1533 if (dp->tape_splitsize && dp->tape_splitsize * 1000 < tape_size) {
1535 _("WARNING: %s %s: tape-splitsize of %ju %sB < 0.1%% of tape length.\n"),
1536 hostp->hostname, dp->name,
1537 (uintmax_t)dp->tape_splitsize/(uintmax_t)unitdivisor,
1539 if (!printed_small_part_size_warning) {
1540 printed_small_part_size_warning = TRUE;
1541 g_fprintf(outf, "%s", small_part_size_warning);
1545 /* fallback splitsize will be used if split_diskbuffer is empty or NULL */
1546 if (dp->tape_splitsize != 0 && dp->fallback_splitsize != 0 &&
1547 (dp->split_diskbuffer == NULL ||
1548 dp->split_diskbuffer[0] == '\0') &&
1549 dp->fallback_splitsize * 1000 < tape_size) {
1551 _("WARNING: %s %s: fallback-splitsize of %ju %sB < 0.1%% of tape length.\n"),
1552 hostp->hostname, dp->name,
1553 (uintmax_t)dp->fallback_splitsize/(uintmax_t)unitdivisor,
1555 if (!printed_small_part_size_warning) {
1556 printed_small_part_size_warning = TRUE;
1557 g_fprintf(outf, "%s", small_part_size_warning);
1562 if (dp->data_path == DATA_PATH_DIRECTTCP) {
1563 if (dp->compress != COMP_NONE) {
1565 _("ERROR: %s %s: Can't compress directtcp data-path\n"),
1566 hostp->hostname, dp->name);
1569 if (dp->encrypt != ENCRYPT_NONE) {
1571 _("ERROR: %s %s: Can't encrypt directtcp data-path\n"),
1572 hostp->hostname, dp->name);
1575 if (dp->to_holdingdisk == HOLD_REQUIRED) {
1577 _("ERROR: %s %s: Holding disk can't be use for directtcp data-path\n"),
1578 hostp->hostname, dp->name);
1583 for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
1584 pp_scriptlist = pp_scriptlist->next) {
1585 pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
1586 g_assert(pp_script != NULL);
1587 if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1588 pp_script_get_execute_on(pp_script) & EXECUTE_ON_PRE_HOST_BACKUP) {
1590 _("ERROR: %s %s: Can't run pre-host-backup script on client\n"),
1591 hostp->hostname, dp->name);
1592 } else if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1593 pp_script_get_execute_on(pp_script) & EXECUTE_ON_POST_HOST_BACKUP) {
1595 _("ERROR: %s %s: Can't run post-host-backup script on client\n"),
1596 hostp->hostname, dp->name);
1601 remove_disk(&origq, dp);
1604 amfree(hostindexdir);
1605 hostindexdir_checked = 0;
1608 amfree(hostinfodir);
1609 amfree(conf_infofile);
1610 amfree(conf_indexdir);
1615 g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
1618 g_debug("userbad: %d", userbad);
1619 g_debug("confbad: %d", confbad);
1620 g_debug("tapebad: %d", tapebad);
1621 g_debug("disklow: %d", disklow);
1622 g_debug("logbad: %d", logbad);
1623 g_debug("infobad: %d", infobad);
1624 g_debug("indexbad: %d", indexbad);
1625 g_debug("pgmbad: %d", pgmbad);
1639 /* --------------------------------------------------- */
1644 static void handle_result(void *, pkt_t *, security_handle_t *);
1645 void start_host(am_host_t *hostp);
1647 #define HOST_READY ((void *)0) /* must be 0 */
1648 #define HOST_ACTIVE ((void *)1)
1649 #define HOST_DONE ((void *)2)
1651 #define DISK_READY ((void *)0) /* must be 0 */
1652 #define DISK_ACTIVE ((void *)1)
1653 #define DISK_DONE ((void *)2)
1663 const security_driver_t *secdrv;
1664 char number[NUM_STR_SIZE];
1665 estimate_t estimate;
1667 if(hostp->up != HOST_READY) {
1672 * The first time through here we send a "noop" request. This will
1673 * return the feature list from the client if it supports that.
1674 * If it does not, handle_result() will set the feature list to an
1675 * empty structure. In either case, we do the disks on the second
1676 * (and subsequent) pass(es).
1679 if(hostp->features != NULL) { /* selfcheck service */
1680 int has_features = am_has_feature(hostp->features,
1681 fe_req_options_features);
1682 int has_hostname = am_has_feature(hostp->features,
1683 fe_req_options_hostname);
1684 int has_maxdumps = am_has_feature(hostp->features,
1685 fe_req_options_maxdumps);
1686 int has_config = am_has_feature(hostp->features,
1687 fe_req_options_config);
1689 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1690 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1692 _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
1694 g_fprintf(outf, _("Client might be of a very old version\n"));
1696 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1698 _("ERROR: Client %s does not support selfcheck REP packet.\n"),
1700 g_fprintf(outf, _("Client might be of a very old version\n"));
1702 if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1703 !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1704 !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1706 _("ERROR: Client %s does not support sendsize REQ packet.\n"),
1708 g_fprintf(outf, _("Client might be of a very old version\n"));
1710 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1712 _("ERROR: Client %s does not support sendsize REP packet.\n"),
1714 g_fprintf(outf, _("Client might be of a very old version\n"));
1716 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1717 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1719 _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
1721 g_fprintf(outf, _("Client might be of a very old version\n"));
1723 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1725 _("ERROR: Client %s does not support sendbackup REP packet.\n"),
1727 g_fprintf(outf, _("Client might be of a very old version\n"));
1730 g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1731 req = vstralloc("SERVICE ", "selfcheck", "\n",
1733 has_features ? "features=" : "",
1734 has_features ? our_feature_string : "",
1735 has_features ? ";" : "",
1736 has_maxdumps ? "maxdumps=" : "",
1737 has_maxdumps ? number : "",
1738 has_maxdumps ? ";" : "",
1739 has_hostname ? "hostname=" : "",
1740 has_hostname ? hostp->hostname : "",
1741 has_hostname ? ";" : "",
1742 has_config ? "config=" : "",
1743 has_config ? get_config_name() : "",
1744 has_config ? ";" : "",
1748 req_len = strlen(req);
1749 req_len += 128; /* room for SECURITY ... */
1750 req_len += 256; /* room for non-disk answers */
1751 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1757 char *qname, *b64disk;
1758 char *qdevice, *b64device = NULL;
1759 GPtrArray *errarray;
1762 if(dp->up != DISK_READY || dp->todo != 1) {
1765 qname = quote_string(dp->name);
1767 errarray = validate_optionstr(dp);
1768 if (errarray->len > 0) {
1769 for (i=0; i < errarray->len; i++) {
1770 g_fprintf(outf, _("ERROR: %s:%s %s\n"),
1771 hostp->hostname, qname,
1772 (char *)g_ptr_array_index(errarray, i));
1774 g_ptr_array_free(errarray, TRUE);
1778 } else if (am_has_feature(hostp->features, fe_req_xml)) {
1779 o = xml_optionstr(dp, 0);
1784 b64disk = amxml_format_tag("disk", dp->name);
1785 qdevice = quote_string(dp->device);
1787 b64device = amxml_format_tag("diskdevice", dp->device);
1788 if ((dp->name && qname[0] == '"') ||
1789 (dp->device && qdevice[0] == '"')) {
1790 if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1792 _("WARNING: %s:%s:%s host does not support quoted text\n"),
1793 hostp->hostname, qname, qdevice);
1794 g_fprintf(outf, _("You must upgrade amanda on the client to "
1795 "specify a quoted text/device in the disklist, "
1796 "or don't use quoted text for the device.\n"));
1801 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1803 _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
1804 hostp->hostname, qname, dp->device);
1805 g_fprintf(outf, _("You must upgrade amanda on the client to "
1806 "specify a diskdevice in the disklist "
1807 "or don't specify a diskdevice in the disklist.\n"));
1809 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1811 _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
1812 hostp->hostname, qname, dp->device);
1813 g_fprintf(outf, _("You must upgrade amanda on the client to "
1814 "specify a diskdevice in the disklist"
1815 " or don't specify a diskdevice in the disklist.\n"));
1817 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1819 _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
1820 hostp->hostname, qname, dp->device);
1821 g_fprintf(outf, _("You must upgrade amanda on the client to "
1822 "specify a diskdevice in the disklist"
1823 " or don't specify a diskdevice in the disklist.\n"));
1826 if (dp->data_path != DATA_PATH_AMANDA &&
1827 !am_has_feature(hostp->features, fe_xml_data_path)) {
1829 _("ERROR: Client %s does not support %s data-path\n"),
1830 hostp->hostname, data_path_to_string(dp->data_path));
1831 } else if (dp->data_path == DATA_PATH_DIRECTTCP &&
1832 !am_has_feature(hostp->features, fe_xml_directtcp_list)) {
1834 _("ERROR: Client %s does not support directtcp data-path\n"),
1839 (strcmp(dp->program,"DUMP") == 0 ||
1840 strcmp(dp->program,"GNUTAR") == 0)) {
1841 if(strcmp(dp->program, "DUMP") == 0 &&
1842 !am_has_feature(hostp->features, fe_program_dump)) {
1843 g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
1844 hostp->hostname, qname);
1845 g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
1846 "or you can use another program.\n"));
1848 if(strcmp(dp->program, "GNUTAR") == 0 &&
1849 !am_has_feature(hostp->features, fe_program_gnutar)) {
1850 g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
1851 hostp->hostname, qname);
1852 g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
1853 "or you can use another program.\n"));
1855 estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
1856 if(estimate == ES_CALCSIZE &&
1857 !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1858 g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
1859 "estimate, using CLIENT.\n"),
1860 hostp->hostname, qname);
1861 g_fprintf(outf, _("You must upgrade amanda on the client to use "
1862 "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
1863 estimate = ES_CLIENT;
1865 if(estimate == ES_CALCSIZE &&
1866 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1867 calcsize = "CALCSIZE ";
1871 if(dp->compress == COMP_CUST &&
1872 !am_has_feature(hostp->features, fe_options_compress_cust)) {
1874 _("ERROR: Client %s does not support custom compression.\n"),
1876 g_fprintf(outf, _("You must upgrade amanda on the client to "
1877 "use custom compression\n"));
1878 g_fprintf(outf, _("Otherwise you can use the default client "
1879 "compression program.\n"));
1881 if(dp->encrypt == ENCRYPT_CUST ) {
1882 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1884 _("ERROR: Client %s does not support data encryption.\n"),
1886 g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
1888 } else if ( dp->compress == COMP_SERVER_FAST ||
1889 dp->compress == COMP_SERVER_BEST ||
1890 dp->compress == COMP_SERVER_CUST ) {
1892 _("ERROR: %s: Client encryption with server compression "
1893 "is not supported. See amanda.conf(5) for detail.\n"),
1898 if (am_has_feature(hostp->features, fe_req_xml)) {
1899 l = vstralloc("<dle>\n"
1902 "</program>\n", NULL);
1903 es = xml_estimate(dp->estimatelist, hostp->features);
1904 vstrextend(&l, es, "\n", NULL);
1906 vstrextend(&l, " ", b64disk, "\n", NULL);
1908 vstrextend(&l, " ", b64device, "\n", NULL);
1909 vstrextend(&l, o, "</dle>\n", NULL);
1912 l = vstralloc(calcsize,
1921 l = vstralloc(calcsize,
1931 if (!am_has_feature(hostp->features, fe_program_application_api) ||
1932 !am_has_feature(hostp->features, fe_req_xml)) {
1933 g_fprintf(outf, _("ERROR: %s:%s does not support APPLICATION-API.\n"),
1934 hostp->hostname, qname);
1935 g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
1936 " It is case sensitive\n"));
1940 l = vstralloc("<dle>\n"
1941 " <program>APPLICATION</program>\n", NULL);
1942 if (dp->application) {
1943 application_t *application;
1946 application = lookup_application(dp->application);
1949 _("ERROR: application '%s' not found.\n"), dp->application);
1951 char *client_name = application_get_client_name(application);
1952 if (client_name && strlen(client_name) > 0 &&
1953 !am_has_feature(hostp->features, fe_application_client_name)) {
1955 _("WARNING: %s:%s does not support client-name in application.\n"),
1956 hostp->hostname, qname);
1958 xml_app = xml_application(dp, application, hostp->features);
1959 vstrextend(&l, xml_app, NULL);
1963 if (dp->pp_scriptlist) {
1964 if (!am_has_feature(hostp->features, fe_pp_script)) {
1966 _("ERROR: %s:%s does not support SCRIPT-API.\n"),
1967 hostp->hostname, qname);
1969 identlist_t pp_scriptlist;
1970 for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
1971 pp_scriptlist = pp_scriptlist->next) {
1972 pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
1973 char *client_name = pp_script_get_client_name(pp_script);;
1974 if (client_name && strlen(client_name) > 0 &&
1975 !am_has_feature(hostp->features, fe_script_client_name)) {
1977 _("WARNING: %s:%s does not support client-name in script.\n"),
1978 hostp->hostname, dp->name);
1983 es = xml_estimate(dp->estimatelist, hostp->features);
1984 vstrextend(&l, es, "\n", NULL);
1986 vstrextend(&l, " ", b64disk, "\n", NULL);
1988 vstrextend(&l, " ", b64device, "\n", NULL);
1989 vstrextend(&l, o, "</dle>\n", NULL);
2002 dp->up = DISK_ACTIVE;
2006 else { /* noop service */
2007 req = vstralloc("SERVICE ", "noop", "\n",
2009 "features=", our_feature_string, ";",
2012 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2013 if(dp->up != DISK_READY || dp->todo != 1) {
2020 if(disk_count == 0) {
2022 hostp->up = HOST_DONE;
2026 secdrv = security_getdriver(hostp->disks->auth);
2027 if (secdrv == NULL) {
2028 fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
2029 hostp->disks->auth, hostp->hostname);
2031 protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf,
2032 req, conf_ctimeout, handle_result, hostp);
2037 hostp->up = HOST_ACTIVE;
2041 start_client_checks(
2050 switch(pid = fork()) {
2052 error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
2065 set_pname("amcheck-clients");
2069 if((outf = fdopen(fd, "w")) == NULL) {
2070 error(_("fdopen %d: %s"), fd, strerror(errno));
2075 g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
2076 g_fprintf(outf, "--------------------------------\n");
2078 run_server_global_scripts(EXECUTE_ON_PRE_AMCHECK, get_config_name());
2081 hostcount = remote_errors = 0;
2083 for(dp = origq.head; dp != NULL; dp = dp->next) {
2085 if(hostp->up == HOST_READY && dp->todo == 1) {
2086 run_server_host_scripts(EXECUTE_ON_PRE_HOST_AMCHECK,
2087 get_config_name(), hostp);
2088 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
2089 run_server_dle_scripts(EXECUTE_ON_PRE_DLE_AMCHECK,
2090 get_config_name(), dp1, -1);
2099 run_server_global_scripts(EXECUTE_ON_POST_AMCHECK, get_config_name());
2101 g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."),
2102 _("Client check: %d hosts checked in %s seconds."),
2104 hostcount, walltime_str(curclock()));
2105 g_fprintf(outf, plural(_(" %d problem found.\n"),
2106 _(" %d problems found.\n"), remote_errors),
2110 exit(userbad || remote_errors > 0);
2119 security_handle_t * sech)
2128 gboolean printed_hostname = FALSE;
2130 hostp = (am_host_t *)datap;
2131 hostp->up = HOST_READY;
2135 _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
2136 security_geterror(sech));
2138 hostp->up = HOST_DONE;
2143 g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
2144 hostp->hostname, pkt->body);
2151 skip_quoted_line(s, ch);
2152 if (s[-2] == '\n') {
2156 if(strncmp_const(line, "OPTIONS ") == 0) {
2158 t = strstr(line, "features=");
2159 if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
2160 char *u = strchr(t, ';');
2163 t += SIZEOF("features=")-1;
2164 am_release_feature_set(hostp->features);
2165 if((hostp->features = am_string_to_feature(t)) == NULL) {
2166 g_fprintf(outf, _("ERROR: %s: bad features value: '%s'\n"),
2167 hostp->hostname, t);
2168 g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
2170 hostp->up = HOST_DONE;
2179 if (client_verbose && !printed_hostname) {
2180 g_fprintf(outf, "HOST %s\n", hostp->hostname);
2181 printed_hostname = TRUE;
2184 if(strncmp_const(line, "OK ") == 0) {
2185 if (client_verbose) {
2186 g_fprintf(outf, "%s\n", line);
2192 if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
2193 skip_whitespace(t, tch);
2195 * If the "error" is that the "noop" service is unknown, it
2196 * just means the client is "old" (does not support the service).
2197 * We can ignore this.
2199 if(!((hostp->features == NULL) && (pkt->type == P_NAK)
2200 && ((strcmp(t - 1, "unknown service: noop") == 0)
2201 || (strcmp(t - 1, "noop: invalid service") == 0)))) {
2202 g_fprintf(outf, _("ERROR: %s%s: %s\n"),
2203 (pkt->type == P_NAK) ? "NAK " : "",
2207 hostp->up = HOST_DONE;
2212 g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
2213 hostp->hostname, line);
2215 hostp->up = HOST_DONE;
2217 if(hostp->up == HOST_READY && hostp->features == NULL) {
2219 * The client does not support the features list, so give it an
2222 dbprintf(_("no feature set from host %s\n"), hostp->hostname);
2223 hostp->features = am_set_default_feature_set();
2225 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2226 if(dp->up == DISK_ACTIVE) {
2231 if(hostp->up == HOST_DONE) {
2232 security_close_connection(sech, hostp->hostname);
2233 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2234 run_server_dle_scripts(EXECUTE_ON_POST_DLE_AMCHECK,
2235 get_config_name(), dp, -1);
2237 run_server_host_scripts(EXECUTE_ON_POST_HOST_AMCHECK,
2238 get_config_name(), hostp);
2240 /* try to clean up any defunct processes, since Amanda doesn't wait() for
2242 while(waitpid(-1, NULL, WNOHANG)> 0);