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"
52 #define BUFFER_SIZE 32768
54 static time_t conf_ctimeout;
57 static disklist_t origq;
59 static uid_t uid_dumpuser;
64 pid_t start_client_checks(int fd);
65 pid_t start_server_check(int fd, int do_localchk, int do_tapechk);
66 int main(int argc, char **argv);
67 int check_tapefile(FILE *outf, char *tapefile);
68 int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
73 g_printf(_("Usage: amcheck [-am] [-w] [-sclt] [-M <address>] [-o configoption]* <conf> [host [disk]* ]*\n"));
78 static am_feature_t *our_features = NULL;
79 static char *our_feature_string = NULL;
80 static char *displayunit;
81 static long int unitdivisor;
88 char buffer[BUFFER_SIZE];
90 char *mainfname = NULL;
91 char pid_str[NUM_STR_SIZE];
92 int do_clientchk, client_probs;
93 int do_localchk, do_tapechk, server_probs;
94 pid_t clientchk_pid, serverchk_pid;
95 int opt, tempfd, mainfd;
104 char *tempfname = NULL;
110 config_overrides_t *cfg_ovr;
114 * Configure program for internationalization:
115 * 1) Only set the message locale for now.
116 * 2) Set textdomain for all amanda related programs to "amanda"
117 * We don't want to be forced to support dozens of message catalogs.
119 setlocale(LC_MESSAGES, "C");
120 textdomain("amanda");
125 set_pname("amcheck");
126 /* drop root privileges */
127 if (!set_root_privs(0)) {
128 error(_("amcheck must be run setuid root"));
131 /* Don't die when child closes pipe */
132 signal(SIGPIPE, SIG_IGN);
134 dbopen(DBG_SUBDIR_SERVER);
136 memset(buffer, 0, sizeof(buffer));
138 g_snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid());
140 add_amanda_log_handler(amanda_log_stderr);
142 our_features = am_init_feature_set();
143 our_feature_string = am_feature_to_string(our_features);
147 alwaysmail = mailout = overwrite = 0;
148 do_localchk = do_tapechk = do_clientchk = 0;
149 server_probs = client_probs = 0;
150 tempfd = mainfd = -1;
152 /* process arguments */
154 cfg_ovr = new_config_overrides(argc/2);
155 while((opt = getopt(argc, argv, "M:mawsclto:")) != EOF) {
157 case 'M': if (mailto) {
158 g_printf(_("Multiple -M options\n"));
161 mailto=stralloc(optarg);
162 if(!validate_mailto(mailto)){
163 g_printf(_("Invalid characters in mail address\n"));
174 case 's': do_localchk = do_tapechk = 1;
176 case 'c': do_clientchk = 1;
178 case 'l': do_localchk = 1;
180 case 'w': overwrite = 1;
182 case 'o': add_config_override_opt(cfg_ovr, optarg);
184 case 't': do_tapechk = 1;
191 argc -= optind, argv += optind;
192 if(argc < 1) usage();
195 if ((do_localchk | do_clientchk | do_tapechk) == 0) {
196 /* Check everything if individual checks were not asked for */
197 do_localchk = do_clientchk = do_tapechk = 1;
203 set_config_overrides(cfg_ovr);
204 config_init(CONFIG_INIT_EXPLICIT_NAME, argv[0]);
205 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
207 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
208 read_diskfile(conf_diskfile, &origq);
209 disable_skip_disk(&origq);
210 amfree(conf_diskfile);
212 if (config_errors(NULL) >= CFGERR_WARNINGS) {
213 config_print_errors();
214 if (config_errors(NULL) >= CFGERR_ERRORS) {
215 g_critical(_("errors processing config file"));
219 mailer = getconf_str(CNF_MAILER);
220 if ((!mailer || *mailer == '\0') && mailout == 1) {
221 if (alwaysmail == 1) {
222 g_printf(_("You can't use -a because a mailer is not defined\n"));
224 g_printf(_("You can't use -m because a mailer is not defined\n"));
228 if(mailout && !mailto &&
229 (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
230 g_printf(_("\nWARNING:No mail address configured in amanda.conf.\n"));
231 g_printf(_("To receive dump results by email configure the "
232 "\"mailto\" parameter in amanda.conf\n"));
234 g_printf(_("When using -a option please specify -Maddress also\n\n"));
236 g_printf(_("Use -Maddress instead of -m\n\n"));
239 if(mailout && !mailto)
241 if(getconf_seen(CNF_MAILTO) &&
242 strlen(getconf_str(CNF_MAILTO)) > 0) {
243 if(!validate_mailto(getconf_str(CNF_MAILTO))){
244 g_printf(_("\nMail address in amanda.conf has invalid characters"));
245 g_printf(_("\nNo email will be sent\n"));
250 g_printf(_("\nNo mail address configured in amanda.conf\n"));
252 g_printf(_("When using -a option please specify -Maddress also\n\n"));
254 g_printf(_("Use -Maddress instead of -m\n\n"));
259 conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
261 errstr = match_disklist(&origq, argc-1, argv+1);
263 g_printf(_("%s"),errstr);
268 * Make sure we are running as the dump user. Don't use
269 * check_running_as(..) here, because we want to produce more
270 * verbose error messages.
272 dumpuser = getconf_str(CNF_DUMPUSER);
273 if ((pw = getpwnam(dumpuser)) == NULL) {
274 error(_("amanda.conf has dump user configured to \"%s\", but that user does not exist."), dumpuser);
277 uid_dumpuser = pw->pw_uid;
278 if ((pw = getpwuid(uid_me)) == NULL) {
279 error(_("cannot get username for running user, uid %ld is not in your user database."),
284 if (uid_me != uid_dumpuser) {
285 error(_("running as user \"%s\" instead of \"%s\".\n"
286 "Change user to \"%s\" or change dump user to \"%s\" in amanda.conf"),
287 pw->pw_name, dumpuser, dumpuser, pw->pw_name);
292 displayunit = getconf_str(CNF_DISPLAYUNIT);
293 unitdivisor = getconf_unit_divisor();
296 * If both server and client side checks are being done, the server
297 * check output goes to the main output, while the client check output
298 * goes to a temporary file and is copied to the main output when done.
300 * If the output is to be mailed, the main output is also a disk file,
301 * otherwise it is stdout.
303 if(do_clientchk && (do_localchk || do_tapechk)) {
304 /* we need the temp file */
305 tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
306 if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
307 error(_("could not open temporary amcheck output file %s: %s. Check permissions"), tempfname, strerror(errno));
310 unlink(tempfname); /* so it goes away on close */
315 /* the main fd is a file too */
316 mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
317 if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
318 error(_("could not open amcheck server output file %s: %s. Check permissions"), mainfname, strerror(errno));
321 unlink(mainfname); /* so it goes away on close */
325 /* just use stdout */
328 /* start server side checks */
330 if(do_localchk || do_tapechk)
331 serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
335 /* start client side checks */
338 clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
343 /* wait for child processes and note any problems */
346 if((pid = wait(&retstat)) == -1) {
347 if(errno == EINTR) continue;
349 } else if(pid == clientchk_pid) {
350 client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
352 } else if(pid == serverchk_pid) {
353 server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
356 char *wait_msg = NULL;
358 wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid);
359 if (full_write(mainfd, wait_msg, strlen(wait_msg)) < strlen(wait_msg)) {
360 error(_("write main file: %s"), strerror(errno));
367 /* copy temp output to main output and write tagline */
369 if(do_clientchk && (do_localchk || do_tapechk)) {
370 if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) {
371 error(_("seek temp file: %s"), strerror(errno));
375 while((size = full_read(tempfd, buffer, SIZEOF(buffer))) > 0) {
376 if (full_write(mainfd, buffer, size) < size) {
377 error(_("write main file: %s"), strerror(errno));
382 error(_("read temp file: %s"), strerror(errno));
388 version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), VERSION);
389 if (full_write(mainfd, version_string, strlen(version_string)) < strlen(version_string)) {
390 error(_("write main file: %s"), strerror(errno));
393 amfree(version_string);
394 amfree(our_feature_string);
395 am_release_feature_set(our_features);
398 /* send mail if requested, but only if there were problems */
400 if((server_probs || client_probs || alwaysmail) && mailout) {
412 char *extra_info = NULL;
417 if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
418 error(_("lseek main file: %s"), strerror(errno));
421 if(alwaysmail && !(server_probs || client_probs)) {
422 subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"),
423 getconf_str(CNF_ORG));
425 subject = vstrallocf(
426 _("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"),
427 getconf_str(CNF_ORG));
430 a = (char **) g_new0(char *, 2);
431 a[1] = stralloc(mailto);
434 /* (note that validate_mailto doesn't allow any quotes, so this
435 * is really just splitting regular old strings) */
436 a = split_quoted_strings(getconf_str(CNF_MAILTO));
438 if((nullfd = open("/dev/null", O_RDWR)) < 0) {
439 error("nullfd: /dev/null: %s", strerror(errno));
443 /* assemble the command line for the mailer */
444 pipeargs = g_ptr_array_sized_new(4);
445 g_ptr_array_add(pipeargs, mailer);
446 g_ptr_array_add(pipeargs, "-s");
447 g_ptr_array_add(pipeargs, subject);
449 g_ptr_array_add(pipeargs, *b);
450 g_ptr_array_add(pipeargs, NULL);
452 pipespawnv(mailer, STDIN_PIPE | STDERR_PIPE, 0,
453 &mailfd, &nullfd, &errfd,
454 (char **)pipeargs->pdata);
456 g_ptr_array_free(pipeargs, FALSE);
462 * There is the potential for a deadlock here since we are writing
463 * to the process and then reading stderr, but in the normal case,
464 * nothing should be coming back to us, and hopefully in error
465 * cases, the pipe will break and we will exit out of the loop.
467 signal(SIGPIPE, SIG_IGN);
468 while((r = full_read(mainfd, buffer, SIZEOF(buffer))) > 0) {
469 if((w = full_write(mailfd, buffer, r)) != r) {
471 strappend(extra_info, _("EPIPE writing to mail process\n"));
473 } else if(errno != 0) {
474 error(_("mailfd write: %s"), strerror(errno));
477 error(_("mailfd write: wrote %zd instead of %zd"), w, r);
483 ferr = fdopen(errfd, "r");
485 error(_("Can't fdopen: %s"), strerror(errno));
488 for(; (line = agets(ferr)) != NULL; free(line)) {
491 strappend(extra_info, line);
492 strappend(extra_info, "\n");
497 while (wait(&retstat) != -1) {
498 if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) {
499 char *mailer_error = str_exit_status("mailer", retstat);
500 strappend(err, mailer_error);
501 amfree(mailer_error);
508 fputs(extra_info, stderr);
511 error(_("error running mailer %s: %s"), mailer, err?err:"(unknown)");
517 return (server_probs || client_probs);
520 /* --------------------------------------------------- */
522 static char *datestamp;
523 static FILE *errf = NULL;
533 if (stat(tapefile, &statbuf) == 0) {
534 if (!S_ISREG(statbuf.st_mode)) {
535 quoted = quote_string(tapefile);
536 g_fprintf(outf, _("ERROR: tapelist %s: should be a regular file.\n"),
540 } else if (access(tapefile, F_OK) != 0) {
541 quoted = quote_string(tapefile);
542 g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted);
545 } else if (access(tapefile, W_OK) != 0) {
546 quoted = quote_string(tapefile);
547 g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted);
567 pgm = vstralloc(dir, "/", pgm, NULL);
568 quoted = quote_string(pgm);
569 if(stat(pgm, &statbuf) == -1) {
570 g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
573 } else if (!S_ISREG(statbuf.st_mode)) {
574 g_fprintf(outf, _("ERROR: program %s: not a file\n"),
577 } else if (access(pgm, X_OK) == -1) {
578 g_fprintf(outf, _("ERROR: program %s: not executable\n"),
581 #ifndef SINGLE_USERID
584 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
585 g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"),
589 /* Quiet unused parameter warnings */
592 #endif /* SINGLE_USERID */
599 /* check that the tape is a valid amanda tape
600 Returns TRUE if all tests passed; FALSE otherwise. */
601 static gboolean test_tape_status(FILE * outf) {
605 char *amcheck_device = NULL;
607 amwait_t wait_status;
610 if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
615 outfd = fileno(outf);
617 amcheck_device = vstralloc(amlibexecdir, "/", "amcheck-device", NULL);
618 args = get_config_options(overwrite? 3 : 2);
619 args[0] = amcheck_device; /* steal the reference */
620 args[1] = g_strdup(get_config_name());
622 args[2] = g_strdup("-w");
624 /* run libexecdir/amcheck-device.pl, capturing STDERR and STDOUT to outf */
625 devpid = pipespawnv(amcheck_device, 0, 0,
626 &nullfd, &outfd, &outfd,
629 /* and immediately wait for it to die */
630 waitpid(devpid, &wait_status, 0);
632 if (WIFSIGNALED(wait_status)) {
633 g_fprintf(outf, _("amcheck-device terminated with signal %d"),
634 WTERMSIG(wait_status));
636 } else if (WIFEXITED(wait_status)) {
637 success = (WEXITSTATUS(wait_status) == 0);
654 struct fs_usage fsusage;
656 pid_t pid G_GNUC_UNUSED;
657 int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
658 int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
659 int testtape = do_tapechk;
660 tapetype_t *tp = NULL;
663 intmax_t kb_avail, kb_needed;
665 gboolean printed_small_part_size_warning = FALSE;
666 char *small_part_size_warning =
667 _(" This may create > 1000 parts, severely degrading backup/restore performance.\n"
668 " See http://wiki.zmanda.com/index.php/Splitsize_too_small for more information.\n");
670 switch(pid = fork()) {
672 error(_("could not spawn a process for checking the server: %s"), strerror(errno));
673 g_assert_not_reached();
685 set_pname("amcheck-server");
689 /* server does not need root privileges, and the access() calls below use the real userid,
690 * so totally drop privileges at this point (making the userid equal to the dumpuser) */
693 if((outf = fdopen(fd, "w")) == NULL) {
694 error(_("fdopen %d: %s"), fd, strerror(errno));
699 g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
700 g_fprintf(outf, "-----------------------------\n");
702 if (do_localchk || testtape) {
703 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
707 * Check various server side config file settings.
714 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
715 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
716 g_fprintf(outf, _("ERROR: %s\n"), errstr);
720 lbl_templ = tapetype_get_lbl_templ(tp);
721 if(strcmp(lbl_templ, "") != 0) {
722 lbl_templ = config_dir_relative(lbl_templ);
723 if(access(lbl_templ, R_OK) == -1) {
725 _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
730 #if !defined(HAVE_LPR_CMD)
731 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"));
736 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
737 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) {
738 g_fprintf(outf, _("WARNING: flush-threshold-dumped (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"),
739 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED),
740 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
743 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
744 getconf_int(CNF_TAPERFLUSH)) {
745 g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"),
746 getconf_int(CNF_TAPERFLUSH),
747 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
750 if (getconf_int(CNF_TAPERFLUSH) > 0 &&
751 !getconf_boolean(CNF_AUTOFLUSH)) {
752 g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' if taperflush (%d) is greater that 0.\n"),
753 getconf_int(CNF_TAPERFLUSH));
756 /* Double-check that 'localhost' resolves properly */
757 if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) {
758 g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
762 if (!getconf_seen(CNF_TAPETYPE)) {
764 _("ERROR: no tapetype specified; you must give a value for "
765 "the 'tapetype' parameter\n"));
771 * Look up the programs used on the server side.
775 * entreprise version will do planner/dumper suid check
777 if(access(amlibexecdir, X_OK) == -1) {
778 quoted = quote_string(amlibexecdir);
779 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
781 g_fprintf(outf, _("Check permissions\n"));
785 if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
787 if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
789 if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
791 if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
793 if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
795 if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
798 if(access(sbindir, X_OK) == -1) {
799 quoted = quote_string(sbindir);
800 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
802 g_fprintf(outf, _("Check permissions\n"));
806 if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
808 if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
810 if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
812 if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
815 if(access(COMPRESS_PATH, X_OK) == -1) {
816 quoted = quote_string(COMPRESS_PATH);
817 g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
818 "and indexing will not work. \n"),quoted);
819 g_fprintf(outf, _("Check permissions\n"));
825 * Check that the directory for the tapelist file is writable, as well
826 * as the tapelist file itself (if it already exists). Also, check for
827 * a "hold" file (just because it is convenient to do it here) and warn
828 * if tapedev is set to the null device.
831 if(do_localchk || do_tapechk) {
839 guint64 part_size, part_cache_max_size, tape_size;
840 part_cache_type_t part_cache_type;
841 char *part_cache_dir;
843 tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
845 * XXX There Really Ought to be some error-checking here... dhw
847 tape_dir = stralloc(tapefile);
848 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
851 * else whine Really Loudly about a path with no slashes??!?
854 if(access(tape_dir, W_OK) == -1) {
855 quoted = quote_string(tape_dir);
856 g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"),
861 else if(stat(tapefile, &statbuf) == -1) {
862 if (errno != ENOENT) {
863 quoted = quote_string(tape_dir);
864 g_fprintf(outf, _("ERROR: tapelist %s (%s), "
865 "you must create an empty file.\n"),
866 quoted, strerror(errno));
870 g_fprintf(outf, _("NOTE: tapelist will be created on the next run.\n"));
873 tapebad |= check_tapefile(outf, tapefile);
874 if (tapebad == 0 && read_tapelist(tapefile)) {
875 quoted = quote_string(tapefile);
876 g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
880 newtapefile = stralloc2(tapefile, ".new");
881 tapebad |= check_tapefile(outf, newtapefile);
883 newtapefile = stralloc2(tapefile, ".amlabel");
884 tapebad |= check_tapefile(outf, newtapefile);
886 newtapefile = stralloc2(tapefile, ".amlabel.new");
887 tapebad |= check_tapefile(outf, newtapefile);
889 newtapefile = stralloc2(tapefile, ".yesterday");
890 tapebad |= check_tapefile(outf, newtapefile);
892 newtapefile = stralloc2(tapefile, ".yesterday.new");
893 tapebad |= check_tapefile(outf, newtapefile);
896 holdfile = config_dir_relative("hold");
897 if(access(holdfile, F_OK) != -1) {
898 quoted = quote_string(holdfile);
899 g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
900 g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
901 g_fprintf(outf, _("You might want to delete the existing hold file\n"));
907 tapename = getconf_str(CNF_TAPEDEV);
908 if (tapename == NULL) {
909 if (getconf_str(CNF_TPCHANGER) == NULL) {
910 g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
916 /* check tapetype-based splitting parameters */
917 part_size = tapetype_get_part_size(tp);
918 part_cache_type = tapetype_get_part_cache_type(tp);
919 part_cache_dir = tapetype_get_part_cache_dir(tp);
920 part_cache_max_size = tapetype_get_part_cache_max_size(tp);
922 if (!tapetype_seen(tp, TAPETYPE_PART_SIZE)) {
923 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_TYPE)) {
924 g_fprintf(outf, "ERROR: part-cache-type specified, but no part-size\n");
927 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
928 g_fprintf(outf, "ERROR: part-cache-dir specified, but no part-size\n");
931 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
932 g_fprintf(outf, "ERROR: part-cache-max-size specified, but no part-size\n");
936 switch (part_cache_type) {
937 case PART_CACHE_TYPE_DISK:
938 if (!tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)
939 || !part_cache_dir || !*part_cache_dir) {
941 "ERROR: part-cache-type is DISK, but no part-cache-dir specified\n");
944 if(get_fs_usage(part_cache_dir, NULL, &fsusage) == -1) {
945 g_fprintf(outf, "ERROR: part-cache-dir '%s': %s\n",
946 part_cache_dir, strerror(errno));
949 kb_avail = fsusage.fsu_bavail_top_bit_set?
950 0 : fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
951 kb_needed = part_size;
952 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
953 kb_needed = part_cache_max_size;
955 if (kb_avail < kb_needed) {
957 "ERROR: part-cache-dir has %ju %sB available, but needs %ju %sB\n",
958 kb_avail/(uintmax_t)unitdivisor, displayunit,
959 kb_needed/(uintmax_t)unitdivisor, displayunit);
966 case PART_CACHE_TYPE_MEMORY:
967 kb_avail = physmem_total() / 1024;
968 kb_needed = part_size;
969 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
970 kb_needed = part_cache_max_size;
972 if (kb_avail < kb_needed) {
974 "ERROR: system has %ju %sB memory, but part cache needs %ju %sB\n",
975 kb_avail/(uintmax_t)unitdivisor, displayunit,
976 kb_needed/(uintmax_t)unitdivisor, displayunit);
982 case PART_CACHE_TYPE_NONE:
983 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
985 "ERROR: part-cache-dir specified, but part-cache-type is not DISK\n");
992 if (tapetype_seen(tp, TAPETYPE_PART_SIZE) && part_size == 0
993 && part_cache_type != PART_CACHE_TYPE_NONE) {
995 "ERROR: part_size is zero, but part-cache-type is not 'none'\n");
999 if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
1000 if (part_cache_type == PART_CACHE_TYPE_NONE) {
1002 "ERROR: part-cache-max-size is specified but no part cache is in use\n");
1006 if (part_cache_max_size > part_size) {
1008 "WARNING: part-cache-max-size is greater than part-size\n");
1012 tape_size = tapetype_get_length(tp);
1013 if (part_size && part_size * 1000 < tape_size) {
1015 _("WARNING: part-size of %ju %sB < 0.1%% of tape length.\n"),
1016 (uintmax_t)part_size/(uintmax_t)unitdivisor, displayunit);
1017 if (!printed_small_part_size_warning) {
1018 printed_small_part_size_warning = TRUE;
1019 g_fprintf(outf, "%s", small_part_size_warning);
1021 } else if (part_cache_max_size && part_cache_max_size * 1000 < tape_size) {
1023 _("WARNING: part-cache-max-size of %ju %sB < 0.1%% of tape length.\n"),
1024 (uintmax_t)part_cache_max_size/(uintmax_t)unitdivisor, displayunit);
1025 if (!printed_small_part_size_warning) {
1026 printed_small_part_size_warning = TRUE;
1027 g_fprintf(outf, "%s", small_part_size_warning);
1032 /* check available disk space */
1038 for (il = getconf_identlist(CNF_HOLDINGDISK);
1041 hdp = lookup_holdingdisk(il->data);
1042 quoted = quote_string(holdingdisk_get_diskdir(hdp));
1043 if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
1044 g_fprintf(outf, _("ERROR: holding dir %s (%s), "
1045 "you must create a directory.\n"),
1046 quoted, strerror(errno));
1052 /* do the division first to avoid potential integer overflow */
1053 if (fsusage.fsu_bavail_top_bit_set)
1056 kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
1058 if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
1059 g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
1060 quoted, strerror(errno));
1061 g_fprintf(outf, _("Check permissions\n"));
1064 else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
1065 g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
1066 quoted, strerror(errno));
1067 g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
1070 else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
1073 _("WARNING: holding disk %s: "
1074 "no space available (%lld %sB requested)\n"), quoted,
1075 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1079 else if(kb_avail < holdingdisk_get_disksize(hdp)) {
1081 _("WARNING: holding disk %s: "
1082 "only %lld %sB available (%lld %sB requested)\n"), quoted,
1083 (long long)(kb_avail / (off_t)unitdivisor),
1085 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1091 _("Holding disk %s: %lld %sB disk space available,"
1092 " using %lld %sB as requested\n"),
1094 (long long)(kb_avail / (off_t)unitdivisor),
1096 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1101 if(kb_avail < -holdingdisk_get_disksize(hdp)) {
1103 _("WARNING: holding disk %s: "
1104 "only %lld %sB free, using nothing\n"),
1105 quoted, (long long)(kb_avail / (off_t)unitdivisor),
1107 g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
1112 _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
1114 (long long)(kb_avail/(off_t)unitdivisor),
1116 (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
1124 /* check that the log file is writable if it already exists */
1130 struct stat stat_old;
1131 struct stat statbuf;
1133 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
1134 logfile = vstralloc(conf_logdir, "/log", NULL);
1136 quoted = quote_string(conf_logdir);
1137 if(stat(conf_logdir, &statbuf) == -1) {
1138 g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
1139 quoted, strerror(errno));
1142 else if(access(conf_logdir, W_OK) == -1) {
1143 g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
1148 if(logbad == 0 && access(logfile, F_OK) == 0) {
1151 if(access(logfile, W_OK) != 0) {
1152 quoted = quote_string(logfile);
1153 g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
1158 olddir = vstralloc(conf_logdir, "/oldlog", NULL);
1159 quoted = quote_string(olddir);
1160 if (logbad == 0 && stat(olddir,&stat_old) == 0) { /* oldlog exist */
1161 if(!(S_ISDIR(stat_old.st_mode))) {
1162 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1164 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1167 if(logbad == 0 && access(olddir, W_OK) == -1) {
1168 g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
1169 g_fprintf(outf, _("Check permissions\n"));
1173 else if(logbad == 0 && lstat(olddir,&stat_old) == 0) {
1174 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1176 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1181 if (logbad == 0 && testtape) {
1182 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
1183 if (access(logfile, F_OK) == 0) {
1191 amfree(conf_logdir);
1195 tapebad = !test_tape_status(outf);
1196 } else if (do_tapechk) {
1197 g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
1198 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1199 } else if (logbad == 2) {
1200 g_fprintf(outf, _("NOTE: amdump or amflush seem to be running\n"));
1201 g_fprintf(outf, _("NOTE: if they are not, you must run amcleanup\n"));
1203 /* we skipped the tape checks, but this is just a NOTE and
1204 * should not result in a nonzero exit status, so reset logbad to 0 */
1207 g_fprintf(outf, _("NOTE: skipping tape checks\n"));
1211 * See if the information file and index directory for each client
1212 * and disk is OK. Since we may be seeing clients and/or disks for
1213 * the first time, these are just warnings, not errors.
1216 char *conf_infofile;
1217 char *conf_indexdir;
1218 char *hostinfodir = NULL;
1219 char *hostindexdir = NULL;
1220 char *diskdir = NULL;
1221 char *infofile = NULL;
1222 struct stat statbuf;
1225 int indexdir_checked = 0;
1226 int hostindexdir_checked = 0;
1229 int conf_tapecycle, conf_runspercycle;
1230 identlist_t pp_scriptlist;
1232 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1233 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1235 if(conf_tapecycle <= conf_runspercycle) {
1236 g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
1237 conf_tapecycle, conf_runspercycle);
1240 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
1241 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
1243 quoted = quote_string(conf_infofile);
1244 if(stat(conf_infofile, &statbuf) == -1) {
1245 if (errno == ENOENT) {
1246 g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
1248 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1250 g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
1251 quoted, strerror(errno));
1254 amfree(conf_infofile);
1255 } else if (!S_ISDIR(statbuf.st_mode)) {
1256 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
1257 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1258 amfree(conf_infofile);
1260 } else if (access(conf_infofile, W_OK) == -1) {
1261 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1262 g_fprintf(outf, _("Check permissions\n"));
1263 amfree(conf_infofile);
1266 char *errmsg = NULL;
1267 if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1268 g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1272 strappend(conf_infofile, "/");
1276 while(!empty(origq)) {
1277 hostp = origq.head->host;
1278 host = sanitise_filename(hostp->hostname);
1280 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1281 quoted = quote_string(hostinfodir);
1282 if(stat(hostinfodir, &statbuf) == -1) {
1283 if (errno == ENOENT) {
1284 g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
1287 _("NOTE: it will be created on the next run.\n"));
1289 g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
1290 quoted, strerror(errno));
1293 amfree(hostinfodir);
1294 } else if (!S_ISDIR(statbuf.st_mode)) {
1295 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1297 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1298 amfree(hostinfodir);
1300 } else if (access(hostinfodir, W_OK) == -1) {
1301 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1302 g_fprintf(outf, _("Check permissions\n"));
1303 amfree(hostinfodir);
1306 strappend(hostinfodir, "/");
1310 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1311 disk = sanitise_filename(dp->name);
1315 diskdir = newstralloc2(diskdir, hostinfodir, disk);
1316 infofile = vstralloc(diskdir, "/", "info", NULL);
1317 quoted = quote_string(diskdir);
1318 quotedif = quote_string(infofile);
1319 if(stat(diskdir, &statbuf) == -1) {
1320 if (errno == ENOENT) {
1321 g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
1324 _("NOTE: it will be created on the next run.\n"));
1326 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1327 quoted, strerror(errno));
1330 } else if (!S_ISDIR(statbuf.st_mode)) {
1331 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1333 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1335 } else if (access(diskdir, W_OK) == -1) {
1336 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
1338 g_fprintf(outf,_("Check permissions\n"));
1340 } else if(stat(infofile, &statbuf) == -1) {
1341 if (errno == ENOENT) {
1342 g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
1344 g_fprintf(outf, _("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_ISREG(statbuf.st_mode)) {
1351 g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
1353 g_fprintf(outf, _("Remove the entry and create a new file\n"));
1355 } else if (access(infofile, R_OK) == -1) {
1356 g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
1365 if(! indexdir_checked) {
1366 quoted = quote_string(conf_indexdir);
1367 if(stat(conf_indexdir, &statbuf) == -1) {
1368 if (errno == ENOENT) {
1369 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1371 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1373 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1374 quoted, strerror(errno));
1377 amfree(conf_indexdir);
1378 } else if (!S_ISDIR(statbuf.st_mode)) {
1379 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1381 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1382 amfree(conf_indexdir);
1384 } else if (access(conf_indexdir, W_OK) == -1) {
1385 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1387 amfree(conf_indexdir);
1390 strappend(conf_indexdir, "/");
1392 indexdir_checked = 1;
1396 if(! hostindexdir_checked) {
1397 hostindexdir = stralloc2(conf_indexdir, host);
1398 quoted = quote_string(hostindexdir);
1399 if(stat(hostindexdir, &statbuf) == -1) {
1400 if (errno == ENOENT) {
1401 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1403 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1405 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1406 quoted, strerror(errno));
1409 amfree(hostindexdir);
1410 } else if (!S_ISDIR(statbuf.st_mode)) {
1411 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1413 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1414 amfree(hostindexdir);
1416 } else if (access(hostindexdir, W_OK) == -1) {
1417 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1419 amfree(hostindexdir);
1422 strappend(hostindexdir, "/");
1424 hostindexdir_checked = 1;
1428 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1429 quoted = quote_string(diskdir);
1430 if(stat(diskdir, &statbuf) == -1) {
1431 if (errno == ENOENT) {
1432 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1434 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1436 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1437 quoted, strerror(errno));
1440 } else if (!S_ISDIR(statbuf.st_mode)) {
1441 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1443 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1445 } else if (access(diskdir, W_OK) == -1) {
1446 g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
1455 if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1456 if ( dp->srv_encrypt[0] == '\0' ) {
1457 g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
1458 g_fprintf(outf, _("Specify \"server-custom-encrypt\" in the dumptype\n"));
1461 else if(access(dp->srv_encrypt, X_OK) == -1) {
1462 g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
1464 g_fprintf(outf, _("Check file type\n"));
1468 if ( dp->compress == COMP_SERVER_CUST ) {
1469 if ( dp->srvcompprog[0] == '\0' ) {
1470 g_fprintf(outf, _("ERROR: server custom compression program "
1471 "not specified\n"));
1472 g_fprintf(outf, _("Specify \"server-custom-compress\" in "
1476 else if(access(dp->srvcompprog, X_OK) == -1) {
1477 quoted = quote_string(dp->srvcompprog);
1479 g_fprintf(outf, _("ERROR: %s is not executable, server custom "
1480 "compression will not work\n"),
1483 g_fprintf(outf, _("Check file type\n"));
1488 /* check deprecated splitting parameters */
1489 if (dumptype_seen(dp->config, DUMPTYPE_TAPE_SPLITSIZE)
1490 || dumptype_seen(dp->config, DUMPTYPE_SPLIT_DISKBUFFER)
1491 || dumptype_seen(dp->config, DUMPTYPE_FALLBACK_SPLITSIZE)) {
1492 tape_size = tapetype_get_length(tp);
1493 if (dp->tape_splitsize > tape_size) {
1495 _("ERROR: %s %s: tape-splitsize > tape size\n"),
1496 hostp->hostname, dp->name);
1499 if (dp->tape_splitsize && dp->fallback_splitsize * 1024 > physmem_total()) {
1501 _("ERROR: %s %s: fallback-splitsize > total available memory\n"),
1502 hostp->hostname, dp->name);
1505 if (dp->tape_splitsize && dp->fallback_splitsize > tape_size) {
1507 _("ERROR: %s %s: fallback-splitsize > tape size\n"),
1508 hostp->hostname, dp->name);
1512 /* also check for part sizes that are too small */
1513 if (dp->tape_splitsize && dp->tape_splitsize * 1000 < tape_size) {
1515 _("WARNING: %s %s: tape-splitsize of %ju %sB < 0.1%% of tape length.\n"),
1516 hostp->hostname, dp->name,
1517 (uintmax_t)dp->tape_splitsize/(uintmax_t)unitdivisor,
1519 if (!printed_small_part_size_warning) {
1520 printed_small_part_size_warning = TRUE;
1521 g_fprintf(outf, "%s", small_part_size_warning);
1525 /* fallback splitsize will be used if split_diskbuffer is empty or NULL */
1526 if (dp->tape_splitsize != 0 && dp->fallback_splitsize != 0 &&
1527 (dp->split_diskbuffer == NULL ||
1528 dp->split_diskbuffer[0] == '\0') &&
1529 dp->fallback_splitsize * 1000 < tape_size) {
1531 _("WARNING: %s %s: fallback-splitsize of %ju %sB < 0.1%% of tape length.\n"),
1532 hostp->hostname, dp->name,
1533 (uintmax_t)dp->fallback_splitsize/(uintmax_t)unitdivisor,
1535 if (!printed_small_part_size_warning) {
1536 printed_small_part_size_warning = TRUE;
1537 g_fprintf(outf, "%s", small_part_size_warning);
1542 if (dp->data_path == DATA_PATH_DIRECTTCP) {
1543 if (dp->compress != COMP_NONE) {
1545 _("ERROR: %s %s: Can't compress directtcp data-path\n"),
1546 hostp->hostname, dp->name);
1549 if (dp->encrypt != ENCRYPT_NONE) {
1551 _("ERROR: %s %s: Can't encrypt directtcp data-path\n"),
1552 hostp->hostname, dp->name);
1555 if (dp->to_holdingdisk == HOLD_REQUIRED) {
1557 _("ERROR: %s %s: Holding disk can't be use for directtcp data-path\n"),
1558 hostp->hostname, dp->name);
1563 for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
1564 pp_scriptlist = pp_scriptlist->next) {
1565 pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
1566 g_assert(pp_script != NULL);
1567 if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1568 pp_script_get_execute_on(pp_script) & EXECUTE_ON_PRE_HOST_BACKUP) {
1570 _("ERROR: %s %s: Can't run pre-host-backup script on client\n"),
1571 hostp->hostname, dp->name);
1572 } else if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1573 pp_script_get_execute_on(pp_script) & EXECUTE_ON_POST_HOST_BACKUP) {
1575 _("ERROR: %s %s: Can't run post-host-backup script on client\n"),
1576 hostp->hostname, dp->name);
1581 remove_disk(&origq, dp);
1584 amfree(hostindexdir);
1585 hostindexdir_checked = 0;
1588 amfree(hostinfodir);
1589 amfree(conf_infofile);
1590 amfree(conf_indexdir);
1595 g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
1598 g_debug("userbad: %d", userbad);
1599 g_debug("confbad: %d", confbad);
1600 g_debug("tapebad: %d", tapebad);
1601 g_debug("disklow: %d", disklow);
1602 g_debug("logbad: %d", logbad);
1603 g_debug("infobad: %d", infobad);
1604 g_debug("indexbad: %d", indexbad);
1605 g_debug("pgmbad: %d", pgmbad);
1619 /* --------------------------------------------------- */
1624 static void handle_result(void *, pkt_t *, security_handle_t *);
1625 void start_host(am_host_t *hostp);
1627 #define HOST_READY ((void *)0) /* must be 0 */
1628 #define HOST_ACTIVE ((void *)1)
1629 #define HOST_DONE ((void *)2)
1631 #define DISK_READY ((void *)0) /* must be 0 */
1632 #define DISK_ACTIVE ((void *)1)
1633 #define DISK_DONE ((void *)2)
1643 const security_driver_t *secdrv;
1644 char number[NUM_STR_SIZE];
1645 estimate_t estimate;
1647 if(hostp->up != HOST_READY) {
1652 * The first time through here we send a "noop" request. This will
1653 * return the feature list from the client if it supports that.
1654 * If it does not, handle_result() will set the feature list to an
1655 * empty structure. In either case, we do the disks on the second
1656 * (and subsequent) pass(es).
1659 if(hostp->features != NULL) { /* selfcheck service */
1660 int has_features = am_has_feature(hostp->features,
1661 fe_req_options_features);
1662 int has_hostname = am_has_feature(hostp->features,
1663 fe_req_options_hostname);
1664 int has_maxdumps = am_has_feature(hostp->features,
1665 fe_req_options_maxdumps);
1666 int has_config = am_has_feature(hostp->features,
1667 fe_req_options_config);
1669 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1670 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1672 _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
1674 g_fprintf(outf, _("Client might be of a very old version\n"));
1676 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1678 _("ERROR: Client %s does not support selfcheck REP packet.\n"),
1680 g_fprintf(outf, _("Client might be of a very old version\n"));
1682 if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1683 !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1684 !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1686 _("ERROR: Client %s does not support sendsize REQ packet.\n"),
1688 g_fprintf(outf, _("Client might be of a very old version\n"));
1690 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1692 _("ERROR: Client %s does not support sendsize REP packet.\n"),
1694 g_fprintf(outf, _("Client might be of a very old version\n"));
1696 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1697 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1699 _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
1701 g_fprintf(outf, _("Client might be of a very old version\n"));
1703 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1705 _("ERROR: Client %s does not support sendbackup REP packet.\n"),
1707 g_fprintf(outf, _("Client might be of a very old version\n"));
1710 g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1711 req = vstralloc("SERVICE ", "selfcheck", "\n",
1713 has_features ? "features=" : "",
1714 has_features ? our_feature_string : "",
1715 has_features ? ";" : "",
1716 has_maxdumps ? "maxdumps=" : "",
1717 has_maxdumps ? number : "",
1718 has_maxdumps ? ";" : "",
1719 has_hostname ? "hostname=" : "",
1720 has_hostname ? hostp->hostname : "",
1721 has_hostname ? ";" : "",
1722 has_config ? "config=" : "",
1723 has_config ? get_config_name() : "",
1724 has_config ? ";" : "",
1728 req_len = strlen(req);
1729 req_len += 128; /* room for SECURITY ... */
1730 req_len += 256; /* room for non-disk answers */
1731 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1737 char *qname, *b64disk;
1738 char *qdevice, *b64device = NULL;
1739 GPtrArray *errarray;
1742 if(dp->up != DISK_READY || dp->todo != 1) {
1745 qname = quote_string(dp->name);
1747 errarray = validate_optionstr(dp);
1748 if (errarray->len > 0) {
1749 for (i=0; i < errarray->len; i++) {
1750 g_fprintf(outf, _("ERROR: %s:%s %s\n"),
1751 hostp->hostname, qname,
1752 (char *)g_ptr_array_index(errarray, i));
1754 g_ptr_array_free(errarray, TRUE);
1758 } else if (am_has_feature(hostp->features, fe_req_xml)) {
1759 o = xml_optionstr(dp, 0);
1764 b64disk = amxml_format_tag("disk", dp->name);
1765 qdevice = quote_string(dp->device);
1767 b64device = amxml_format_tag("diskdevice", dp->device);
1768 if ((dp->name && qname[0] == '"') ||
1769 (dp->device && qdevice[0] == '"')) {
1770 if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1772 _("WARNING: %s:%s:%s host does not support quoted text\n"),
1773 hostp->hostname, qname, qdevice);
1774 g_fprintf(outf, _("You must upgrade amanda on the client to "
1775 "specify a quoted text/device in the disklist, "
1776 "or don't use quoted text for the device.\n"));
1781 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1783 _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
1784 hostp->hostname, qname, dp->device);
1785 g_fprintf(outf, _("You must upgrade amanda on the client to "
1786 "specify a diskdevice in the disklist "
1787 "or don't specify a diskdevice in the disklist.\n"));
1789 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1791 _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
1792 hostp->hostname, qname, dp->device);
1793 g_fprintf(outf, _("You must upgrade amanda on the client to "
1794 "specify a diskdevice in the disklist"
1795 " or don't specify a diskdevice in the disklist.\n"));
1797 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1799 _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
1800 hostp->hostname, qname, dp->device);
1801 g_fprintf(outf, _("You must upgrade amanda on the client to "
1802 "specify a diskdevice in the disklist"
1803 " or don't specify a diskdevice in the disklist.\n"));
1806 if (dp->data_path != DATA_PATH_AMANDA &&
1807 !am_has_feature(hostp->features, fe_xml_data_path)) {
1809 _("ERROR: Client %s does not support %s data-path\n"),
1810 hostp->hostname, data_path_to_string(dp->data_path));
1811 } else if (dp->data_path == DATA_PATH_DIRECTTCP &&
1812 !am_has_feature(hostp->features, fe_xml_directtcp_list)) {
1814 _("ERROR: Client %s does not support directtcp data-path\n"),
1819 (strcmp(dp->program,"DUMP") == 0 ||
1820 strcmp(dp->program,"GNUTAR") == 0)) {
1821 if(strcmp(dp->program, "DUMP") == 0 &&
1822 !am_has_feature(hostp->features, fe_program_dump)) {
1823 g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
1824 hostp->hostname, qname);
1825 g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
1826 "or you can use another program.\n"));
1828 if(strcmp(dp->program, "GNUTAR") == 0 &&
1829 !am_has_feature(hostp->features, fe_program_gnutar)) {
1830 g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
1831 hostp->hostname, qname);
1832 g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
1833 "or you can use another program.\n"));
1835 estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
1836 if(estimate == ES_CALCSIZE &&
1837 !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1838 g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
1839 "estimate, using CLIENT.\n"),
1840 hostp->hostname, qname);
1841 g_fprintf(outf, _("You must upgrade amanda on the client to use "
1842 "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
1843 estimate = ES_CLIENT;
1845 if(estimate == ES_CALCSIZE &&
1846 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1847 calcsize = "CALCSIZE ";
1851 if(dp->compress == COMP_CUST &&
1852 !am_has_feature(hostp->features, fe_options_compress_cust)) {
1854 _("ERROR: Client %s does not support custom compression.\n"),
1856 g_fprintf(outf, _("You must upgrade amanda on the client to "
1857 "use custom compression\n"));
1858 g_fprintf(outf, _("Otherwise you can use the default client "
1859 "compression program.\n"));
1861 if(dp->encrypt == ENCRYPT_CUST ) {
1862 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1864 _("ERROR: Client %s does not support data encryption.\n"),
1866 g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
1868 } else if ( dp->compress == COMP_SERVER_FAST ||
1869 dp->compress == COMP_SERVER_BEST ||
1870 dp->compress == COMP_SERVER_CUST ) {
1872 _("ERROR: %s: Client encryption with server compression "
1873 "is not supported. See amanda.conf(5) for detail.\n"),
1878 if (am_has_feature(hostp->features, fe_req_xml)) {
1879 l = vstralloc("<dle>\n"
1882 "</program>\n", NULL);
1883 es = xml_estimate(dp->estimatelist, hostp->features);
1884 vstrextend(&l, es, "\n", NULL);
1886 vstrextend(&l, " ", b64disk, "\n", NULL);
1888 vstrextend(&l, " ", b64device, "\n", NULL);
1889 vstrextend(&l, o, "</dle>\n", NULL);
1892 l = vstralloc(calcsize,
1901 l = vstralloc(calcsize,
1911 if (!am_has_feature(hostp->features, fe_program_application_api) ||
1912 !am_has_feature(hostp->features, fe_req_xml)) {
1913 g_fprintf(outf, _("ERROR: %s:%s does not support APPLICATION-API.\n"),
1914 hostp->hostname, qname);
1915 g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
1916 " It is case sensitive\n"));
1920 l = vstralloc("<dle>\n"
1921 " <program>APPLICATION</program>\n", NULL);
1922 if (dp->application) {
1923 application_t *application;
1926 application = lookup_application(dp->application);
1929 _("ERROR: application '%s' not found.\n"), dp->application);
1931 xml_app = xml_application(dp, application, hostp->features);
1932 vstrextend(&l, xml_app, NULL);
1936 if (dp->pp_scriptlist) {
1937 if (!am_has_feature(hostp->features, fe_pp_script)) {
1939 _("ERROR: %s:%s does not support SCRIPT-API.\n"),
1940 hostp->hostname, qname);
1943 es = xml_estimate(dp->estimatelist, hostp->features);
1944 vstrextend(&l, es, "\n", NULL);
1946 vstrextend(&l, " ", b64disk, "\n", NULL);
1948 vstrextend(&l, " ", b64device, "\n", NULL);
1949 vstrextend(&l, o, "</dle>\n", NULL);
1962 dp->up = DISK_ACTIVE;
1966 else { /* noop service */
1967 req = vstralloc("SERVICE ", "noop", "\n",
1969 "features=", our_feature_string, ";",
1972 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1973 if(dp->up != DISK_READY || dp->todo != 1) {
1980 if(disk_count == 0) {
1982 hostp->up = HOST_DONE;
1986 secdrv = security_getdriver(hostp->disks->auth);
1987 if (secdrv == NULL) {
1988 fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
1989 hostp->disks->auth, hostp->hostname);
1991 protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf,
1992 req, conf_ctimeout, handle_result, hostp);
1997 hostp->up = HOST_ACTIVE;
2001 start_client_checks(
2010 switch(pid = fork()) {
2012 error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
2025 set_pname("amcheck-clients");
2029 if((outf = fdopen(fd, "w")) == NULL) {
2030 error(_("fdopen %d: %s"), fd, strerror(errno));
2035 g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
2036 g_fprintf(outf, "--------------------------------\n");
2040 hostcount = remote_errors = 0;
2042 for(dp = origq.head; dp != NULL; dp = dp->next) {
2044 if(hostp->up == HOST_READY && dp->todo == 1) {
2045 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
2046 run_server_scripts(EXECUTE_ON_PRE_HOST_AMCHECK,
2047 get_config_name(), dp1, -1);
2049 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
2050 run_server_scripts(EXECUTE_ON_PRE_DLE_AMCHECK,
2051 get_config_name(), dp1, -1);
2061 g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."),
2062 _("Client check: %d hosts checked in %s seconds."),
2064 hostcount, walltime_str(curclock()));
2065 g_fprintf(outf, plural(_(" %d problem found.\n"),
2066 _(" %d problems found.\n"), remote_errors),
2070 exit(userbad || remote_errors > 0);
2079 security_handle_t * sech)
2089 hostp = (am_host_t *)datap;
2090 hostp->up = HOST_READY;
2094 _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
2095 security_geterror(sech));
2097 hostp->up = HOST_DONE;
2102 g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
2103 hostp->hostname, pkt->body);
2110 skip_quoted_line(s, ch);
2111 if (s[-2] == '\n') {
2115 if(strncmp_const(line, "OPTIONS ") == 0) {
2117 t = strstr(line, "features=");
2118 if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
2119 char *u = strchr(t, ';');
2122 t += SIZEOF("features=")-1;
2123 am_release_feature_set(hostp->features);
2124 if((hostp->features = am_string_to_feature(t)) == NULL) {
2125 g_fprintf(outf, _("ERROR: %s: bad features value: '%s'\n"),
2126 hostp->hostname, t);
2127 g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
2129 hostp->up = HOST_DONE;
2138 if(strncmp_const(line, "OK ") == 0) {
2143 if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
2144 skip_whitespace(t, tch);
2146 * If the "error" is that the "noop" service is unknown, it
2147 * just means the client is "old" (does not support the service).
2148 * We can ignore this.
2150 if(!((hostp->features == NULL) && (pkt->type == P_NAK)
2151 && ((strcmp(t - 1, "unknown service: noop") == 0)
2152 || (strcmp(t - 1, "noop: invalid service") == 0)))) {
2153 g_fprintf(outf, _("ERROR: %s%s: %s\n"),
2154 (pkt->type == P_NAK) ? "NAK " : "",
2158 hostp->up = HOST_DONE;
2163 g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
2164 hostp->hostname, line);
2166 hostp->up = HOST_DONE;
2168 if(hostp->up == HOST_READY && hostp->features == NULL) {
2170 * The client does not support the features list, so give it an
2173 dbprintf(_("no feature set from host %s\n"), hostp->hostname);
2174 hostp->features = am_set_default_feature_set();
2176 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2177 if(dp->up == DISK_ACTIVE) {
2182 if(hostp->up == HOST_DONE) {
2183 security_close_connection(sech, hostp->hostname);
2184 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2185 run_server_scripts(EXECUTE_ON_POST_DLE_AMCHECK,
2186 get_config_name(), dp, -1);
2188 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2189 run_server_scripts(EXECUTE_ON_POST_HOST_AMCHECK,
2190 get_config_name(), dp, -1);
2193 /* try to clean up any defunct processes, since Amanda doesn't wait() for
2195 while(waitpid(-1, NULL, WNOHANG)> 0);