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;
666 switch(pid = fork()) {
668 error(_("could not spawn a process for checking the server: %s"), strerror(errno));
669 g_assert_not_reached();
681 set_pname("amcheck-server");
685 if((outf = fdopen(fd, "w")) == NULL) {
686 error(_("fdopen %d: %s"), fd, strerror(errno));
691 g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
692 g_fprintf(outf, "-----------------------------\n");
694 if (do_localchk || testtape) {
695 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
699 * Check various server side config file settings.
706 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
707 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
708 g_fprintf(outf, _("ERROR: %s\n"), errstr);
712 lbl_templ = tapetype_get_lbl_templ(tp);
713 if(strcmp(lbl_templ, "") != 0) {
714 lbl_templ = config_dir_relative(lbl_templ);
715 if(access(lbl_templ, R_OK) == -1) {
717 _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
722 #if !defined(HAVE_LPR_CMD)
723 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"));
728 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
729 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) {
730 g_fprintf(outf, _("WARNING: flush_threshold_dumped (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
731 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED),
732 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
735 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
736 getconf_int(CNF_TAPERFLUSH)) {
737 g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
738 getconf_int(CNF_TAPERFLUSH),
739 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
742 if (getconf_int(CNF_TAPERFLUSH) > 0 &&
743 !getconf_boolean(CNF_AUTOFLUSH)) {
744 g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' if taperflush (%d) is greater that 0.\n"),
745 getconf_int(CNF_TAPERFLUSH));
748 /* Double-check that 'localhost' resolves properly */
749 if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) {
750 g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
754 if (!getconf_seen(CNF_TAPETYPE)) {
756 _("ERROR: no tapetype specified; you must give a value for "
757 "the 'tapetype' parameter\n"));
764 * Look up the programs used on the server side.
768 * entreprise version will do planner/dumper suid check
770 if(access(amlibexecdir, X_OK) == -1) {
771 quoted = quote_string(amlibexecdir);
772 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
774 g_fprintf(outf, _("Check permissions\n"));
778 if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
780 if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
782 if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
784 if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
786 if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
788 if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
791 if(access(sbindir, X_OK) == -1) {
792 quoted = quote_string(sbindir);
793 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
795 g_fprintf(outf, _("Check permissions\n"));
799 if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
801 if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
803 if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
805 if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
808 if(access(COMPRESS_PATH, X_OK) == -1) {
809 quoted = quote_string(COMPRESS_PATH);
810 g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
811 "and indexing will not work. \n"),quoted);
812 g_fprintf(outf, _("Check permissions\n"));
818 * Check that the directory for the tapelist file is writable, as well
819 * as the tapelist file itself (if it already exists). Also, check for
820 * a "hold" file (just because it is convenient to do it here) and warn
821 * if tapedev is set to the null device.
824 if(do_localchk || do_tapechk) {
833 tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
835 * XXX There Really Ought to be some error-checking here... dhw
837 tape_dir = stralloc(tapefile);
838 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
841 * else whine Really Loudly about a path with no slashes??!?
844 if(access(tape_dir, W_OK) == -1) {
845 quoted = quote_string(tape_dir);
846 g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"),
851 else if(stat(tapefile, &statbuf) == -1) {
852 if (errno != ENOENT) {
853 quoted = quote_string(tape_dir);
854 g_fprintf(outf, _("ERROR: tapelist %s (%s), "
855 "you must create an empty file.\n"),
856 quoted, strerror(errno));
860 g_fprintf(outf, _("NOTE: tapelist will be created on the next run.\n"));
863 tapebad |= check_tapefile(outf, tapefile);
864 if (tapebad == 0 && read_tapelist(tapefile)) {
865 quoted = quote_string(tapefile);
866 g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
870 newtapefile = stralloc2(tapefile, ".new");
871 tapebad |= check_tapefile(outf, newtapefile);
873 newtapefile = stralloc2(tapefile, ".amlabel");
874 tapebad |= check_tapefile(outf, newtapefile);
876 newtapefile = stralloc2(tapefile, ".amlabel.new");
877 tapebad |= check_tapefile(outf, newtapefile);
879 newtapefile = stralloc2(tapefile, ".yesterday");
880 tapebad |= check_tapefile(outf, newtapefile);
882 newtapefile = stralloc2(tapefile, ".yesterday.new");
883 tapebad |= check_tapefile(outf, newtapefile);
886 holdfile = config_dir_relative("hold");
887 if(access(holdfile, F_OK) != -1) {
888 quoted = quote_string(holdfile);
889 g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
890 g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
891 g_fprintf(outf, _("You might want to delete the existing hold file\n"));
897 tapename = getconf_str(CNF_TAPEDEV);
898 if (tapename == NULL) {
899 if (getconf_str(CNF_TPCHANGER) == NULL) {
900 g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
907 /* check available disk space */
913 for (il = getconf_identlist(CNF_HOLDINGDISK);
916 hdp = lookup_holdingdisk(il->data);
917 quoted = quote_string(holdingdisk_get_diskdir(hdp));
918 if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
919 g_fprintf(outf, _("ERROR: holding dir %s (%s), "
920 "you must create a directory.\n"),
921 quoted, strerror(errno));
927 /* do the division first to avoid potential integer overflow */
928 if (fsusage.fsu_bavail_top_bit_set)
931 kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
933 if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
934 g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
935 quoted, strerror(errno));
936 g_fprintf(outf, _("Check permissions\n"));
939 else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
940 g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
941 quoted, strerror(errno));
942 g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
945 else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
948 _("WARNING: holding disk %s: "
949 "no space available (%lld %sB requested)\n"), quoted,
950 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
954 else if(kb_avail < holdingdisk_get_disksize(hdp)) {
956 _("WARNING: holding disk %s: "
957 "only %lld %sB available (%lld %sB requested)\n"), quoted,
958 (long long)(kb_avail / (off_t)unitdivisor),
960 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
966 _("Holding disk %s: %lld %sB disk space available,"
967 " using %lld %sB as requested\n"),
969 (long long)(kb_avail / (off_t)unitdivisor),
971 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
976 if(kb_avail < -holdingdisk_get_disksize(hdp)) {
978 _("WARNING: holding disk %s: "
979 "only %lld %sB free, using nothing\n"),
980 quoted, (long long)(kb_avail / (off_t)unitdivisor),
982 g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
987 _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
989 (long long)(kb_avail/(off_t)unitdivisor),
991 (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
999 /* check that the log file is writable if it already exists */
1005 struct stat stat_old;
1006 struct stat statbuf;
1008 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
1009 logfile = vstralloc(conf_logdir, "/log", NULL);
1011 quoted = quote_string(conf_logdir);
1012 if(stat(conf_logdir, &statbuf) == -1) {
1013 g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
1014 quoted, strerror(errno));
1017 else if(access(conf_logdir, W_OK) == -1) {
1018 g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
1023 if(logbad == 0 && access(logfile, F_OK) == 0) {
1026 if(access(logfile, W_OK) != 0) {
1027 quoted = quote_string(logfile);
1028 g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
1033 olddir = vstralloc(conf_logdir, "/oldlog", NULL);
1034 quoted = quote_string(olddir);
1035 if (logbad == 0 && stat(olddir,&stat_old) == 0) { /* oldlog exist */
1036 if(!(S_ISDIR(stat_old.st_mode))) {
1037 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1039 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1042 if(logbad == 0 && access(olddir, W_OK) == -1) {
1043 g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
1044 g_fprintf(outf, _("Check permissions\n"));
1048 else if(logbad == 0 && lstat(olddir,&stat_old) == 0) {
1049 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1051 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1056 if (logbad == 0 && testtape) {
1057 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
1058 if (access(logfile, F_OK) == 0) {
1066 amfree(conf_logdir);
1070 tapebad = !test_tape_status(outf);
1071 } else if (do_tapechk) {
1072 g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
1073 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1074 } else if (logbad == 2) {
1075 g_fprintf(outf, _("NOTE: amdump or amflush seem to be running\n"));
1076 g_fprintf(outf, _("NOTE: if they are not, you must run amcleanup\n"));
1078 /* we skipped the tape checks, but this is just a NOTE and
1079 * should not result in a nonzero exit status, so reset logbad to 0 */
1082 g_fprintf(outf, _("NOTE: skipping tape checks\n"));
1086 * See if the information file and index directory for each client
1087 * and disk is OK. Since we may be seeing clients and/or disks for
1088 * the first time, these are just warnings, not errors.
1091 char *conf_infofile;
1092 char *conf_indexdir;
1093 char *hostinfodir = NULL;
1094 char *hostindexdir = NULL;
1095 char *diskdir = NULL;
1096 char *infofile = NULL;
1097 struct stat statbuf;
1100 int indexdir_checked = 0;
1101 int hostindexdir_checked = 0;
1104 int conf_tapecycle, conf_runspercycle;
1105 identlist_t pp_scriptlist;
1106 gboolean printed_small_tape_splitsize_warning = FALSE;
1107 char *small_tape_splitsize_warning =
1108 _(" This may create > 1000 parts, severely degrading backup/restore performance.\n"
1109 " To remedy, increase tape_splitsize or disable splitting by setting it to 0\n"
1110 " See http://wiki.zmanda.com/index.php/Splitsize_too_small for more information.\n");
1112 gboolean printed_small_fallback_splitsize_warning = FALSE;
1113 char *small_fallback_splitsize_warning =
1114 _(" This may create > 1000 parts, severely degrading backup/restore performance.\n"
1115 " To remedy, create/set a split_diskbuffer or increase fallback_splitsize\n"
1116 " See http://wiki.zmanda.com/index.php/Splitsize_too_small for more information.\n");
1119 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1120 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1122 if(conf_tapecycle <= conf_runspercycle) {
1123 g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
1124 conf_tapecycle, conf_runspercycle);
1127 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
1128 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
1130 quoted = quote_string(conf_infofile);
1131 if(stat(conf_infofile, &statbuf) == -1) {
1132 if (errno == ENOENT) {
1133 g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
1135 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1137 g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
1138 quoted, strerror(errno));
1141 amfree(conf_infofile);
1142 } else if (!S_ISDIR(statbuf.st_mode)) {
1143 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
1144 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1145 amfree(conf_infofile);
1147 } else if (access(conf_infofile, W_OK) == -1) {
1148 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1149 g_fprintf(outf, _("Check permissions\n"));
1150 amfree(conf_infofile);
1153 char *errmsg = NULL;
1154 if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1155 g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1159 strappend(conf_infofile, "/");
1163 while(!empty(origq)) {
1164 hostp = origq.head->host;
1165 host = sanitise_filename(hostp->hostname);
1167 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1168 quoted = quote_string(hostinfodir);
1169 if(stat(hostinfodir, &statbuf) == -1) {
1170 if (errno == ENOENT) {
1171 g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
1174 _("NOTE: it will be created on the next run.\n"));
1176 g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
1177 quoted, strerror(errno));
1180 amfree(hostinfodir);
1181 } else if (!S_ISDIR(statbuf.st_mode)) {
1182 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1184 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1185 amfree(hostinfodir);
1187 } else if (access(hostinfodir, W_OK) == -1) {
1188 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1189 g_fprintf(outf, _("Check permissions\n"));
1190 amfree(hostinfodir);
1193 strappend(hostinfodir, "/");
1197 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1198 disk = sanitise_filename(dp->name);
1202 diskdir = newstralloc2(diskdir, hostinfodir, disk);
1203 infofile = vstralloc(diskdir, "/", "info", NULL);
1204 quoted = quote_string(diskdir);
1205 quotedif = quote_string(infofile);
1206 if(stat(diskdir, &statbuf) == -1) {
1207 if (errno == ENOENT) {
1208 g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
1211 _("NOTE: it will be created on the next run.\n"));
1213 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1214 quoted, strerror(errno));
1217 } else if (!S_ISDIR(statbuf.st_mode)) {
1218 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1220 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1222 } else if (access(diskdir, W_OK) == -1) {
1223 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
1225 g_fprintf(outf,_("Check permissions\n"));
1227 } else if(stat(infofile, &statbuf) == -1) {
1228 if (errno == ENOENT) {
1229 g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
1231 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1233 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1234 quoted, strerror(errno));
1237 } else if (!S_ISREG(statbuf.st_mode)) {
1238 g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
1240 g_fprintf(outf, _("Remove the entry and create a new file\n"));
1242 } else if (access(infofile, R_OK) == -1) {
1243 g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
1252 if(! indexdir_checked) {
1253 quoted = quote_string(conf_indexdir);
1254 if(stat(conf_indexdir, &statbuf) == -1) {
1255 if (errno == ENOENT) {
1256 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1258 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1260 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1261 quoted, strerror(errno));
1264 amfree(conf_indexdir);
1265 } else if (!S_ISDIR(statbuf.st_mode)) {
1266 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1268 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1269 amfree(conf_indexdir);
1271 } else if (access(conf_indexdir, W_OK) == -1) {
1272 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1274 amfree(conf_indexdir);
1277 strappend(conf_indexdir, "/");
1279 indexdir_checked = 1;
1283 if(! hostindexdir_checked) {
1284 hostindexdir = stralloc2(conf_indexdir, host);
1285 quoted = quote_string(hostindexdir);
1286 if(stat(hostindexdir, &statbuf) == -1) {
1287 if (errno == ENOENT) {
1288 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1290 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1292 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1293 quoted, strerror(errno));
1296 amfree(hostindexdir);
1297 } else if (!S_ISDIR(statbuf.st_mode)) {
1298 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1300 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1301 amfree(hostindexdir);
1303 } else if (access(hostindexdir, W_OK) == -1) {
1304 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1306 amfree(hostindexdir);
1309 strappend(hostindexdir, "/");
1311 hostindexdir_checked = 1;
1315 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1316 quoted = quote_string(diskdir);
1317 if(stat(diskdir, &statbuf) == -1) {
1318 if (errno == ENOENT) {
1319 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1321 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1323 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1324 quoted, strerror(errno));
1327 } else if (!S_ISDIR(statbuf.st_mode)) {
1328 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1330 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1332 } else if (access(diskdir, W_OK) == -1) {
1333 g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
1342 if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1343 if ( dp->srv_encrypt[0] == '\0' ) {
1344 g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
1345 g_fprintf(outf, _("Specify \"server_custom_encrypt\" in the dumptype\n"));
1348 else if(access(dp->srv_encrypt, X_OK) == -1) {
1349 g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
1351 g_fprintf(outf, _("Check file type\n"));
1355 if ( dp->compress == COMP_SERVER_CUST ) {
1356 if ( dp->srvcompprog[0] == '\0' ) {
1357 g_fprintf(outf, _("ERROR: server custom compression program "
1358 "not specified\n"));
1359 g_fprintf(outf, _("Specify \"server_custom_compress\" in "
1363 else if(access(dp->srvcompprog, X_OK) == -1) {
1364 quoted = quote_string(dp->srvcompprog);
1366 g_fprintf(outf, _("ERROR: %s is not executable, server custom "
1367 "compression will not work\n"),
1370 g_fprintf(outf, _("Check file type\n"));
1375 /* check tape_splitsize */
1376 tape_size = tapetype_get_length(tp);
1377 if (dp->tape_splitsize > tape_size) {
1379 _("ERROR: %s %s: tape_splitsize > tape size\n"),
1380 hostp->hostname, dp->name);
1383 if (dp->tape_splitsize && dp->fallback_splitsize * 1024 > physmem_total()) {
1385 _("ERROR: %s %s: fallback_splitsize > total available memory\n"),
1386 hostp->hostname, dp->name);
1389 if (dp->tape_splitsize && dp->fallback_splitsize > tape_size) {
1391 _("ERROR: %s %s: fallback_splitsize > tape size\n"),
1392 hostp->hostname, dp->name);
1396 /* also check for part sizes that are too small */
1397 if (dp->tape_splitsize && dp->tape_splitsize * 1000 < tape_size) {
1399 _("WARNING: %s %s: tape_splitsize of %ju %sB < 0.1%% of tape length.\n"),
1400 hostp->hostname, dp->name,
1401 (uintmax_t)dp->tape_splitsize/(uintmax_t)unitdivisor,
1403 if (!printed_small_tape_splitsize_warning) {
1404 printed_small_tape_splitsize_warning = TRUE;
1405 g_fprintf(outf, "%s", small_tape_splitsize_warning);
1409 /* fallback splitsize will be used if split_diskbuffer is empty or NULL */
1410 if (dp->tape_splitsize != 0 && dp->fallback_splitsize != 0 &&
1411 (dp->split_diskbuffer == NULL ||
1412 dp->split_diskbuffer[0] == '\0') &&
1413 dp->fallback_splitsize * 1000 < tape_size) {
1415 _("WARNING: %s %s: fallback_splitsize of %ju %sB < 0.1%% of tape length.\n"),
1416 hostp->hostname, dp->name,
1417 (uintmax_t)dp->fallback_splitsize/(uintmax_t)unitdivisor,
1419 if (!printed_small_fallback_splitsize_warning) {
1420 printed_small_fallback_splitsize_warning = TRUE;
1421 g_fprintf(outf, "%s", small_fallback_splitsize_warning);
1425 if (dp->data_path == DATA_PATH_DIRECTTCP) {
1426 if (dp->compress != COMP_NONE) {
1428 _("ERROR: %s %s: Can't compress directtcp data-path\n"),
1429 hostp->hostname, dp->name);
1432 if (dp->encrypt != ENCRYPT_NONE) {
1434 _("ERROR: %s %s: Can't encrypt directtcp data-path\n"),
1435 hostp->hostname, dp->name);
1438 if (dp->to_holdingdisk == HOLD_REQUIRED) {
1440 _("ERROR: %s %s: Holding disk can't be use for directtcp data-path\n"),
1441 hostp->hostname, dp->name);
1443 } else if (dp->to_holdingdisk == HOLD_AUTO) {
1445 _("WARNING: %s %s: Holding disk can't be use for directtcp data-path\n"),
1446 hostp->hostname, dp->name);
1451 for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
1452 pp_scriptlist = pp_scriptlist->next) {
1453 pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
1454 g_assert(pp_script != NULL);
1455 if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1456 pp_script_get_execute_on(pp_script) & EXECUTE_ON_PRE_HOST_BACKUP) {
1458 _("ERROR: %s %s: Can't run pre-host-backup script on client\n"),
1459 hostp->hostname, dp->name);
1460 } else if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1461 pp_script_get_execute_on(pp_script) & EXECUTE_ON_POST_HOST_BACKUP) {
1463 _("ERROR: %s %s: Can't run post-host-backup script on client\n"),
1464 hostp->hostname, dp->name);
1469 remove_disk(&origq, dp);
1472 amfree(hostindexdir);
1473 hostindexdir_checked = 0;
1476 amfree(hostinfodir);
1477 amfree(conf_infofile);
1478 amfree(conf_indexdir);
1483 g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
1486 g_debug("userbad: %d", userbad);
1487 g_debug("confbad: %d", confbad);
1488 g_debug("tapebad: %d", tapebad);
1489 g_debug("disklow: %d", disklow);
1490 g_debug("logbad: %d", logbad);
1491 g_debug("infobad: %d", infobad);
1492 g_debug("indexbad: %d", indexbad);
1493 g_debug("pgmbad: %d", pgmbad);
1507 /* --------------------------------------------------- */
1512 static void handle_result(void *, pkt_t *, security_handle_t *);
1513 void start_host(am_host_t *hostp);
1515 #define HOST_READY ((void *)0) /* must be 0 */
1516 #define HOST_ACTIVE ((void *)1)
1517 #define HOST_DONE ((void *)2)
1519 #define DISK_READY ((void *)0) /* must be 0 */
1520 #define DISK_ACTIVE ((void *)1)
1521 #define DISK_DONE ((void *)2)
1531 const security_driver_t *secdrv;
1532 char number[NUM_STR_SIZE];
1533 estimate_t estimate;
1535 if(hostp->up != HOST_READY) {
1540 * The first time through here we send a "noop" request. This will
1541 * return the feature list from the client if it supports that.
1542 * If it does not, handle_result() will set the feature list to an
1543 * empty structure. In either case, we do the disks on the second
1544 * (and subsequent) pass(es).
1547 if(hostp->features != NULL) { /* selfcheck service */
1548 int has_features = am_has_feature(hostp->features,
1549 fe_req_options_features);
1550 int has_hostname = am_has_feature(hostp->features,
1551 fe_req_options_hostname);
1552 int has_maxdumps = am_has_feature(hostp->features,
1553 fe_req_options_maxdumps);
1554 int has_config = am_has_feature(hostp->features,
1555 fe_req_options_config);
1557 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1558 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1560 _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
1562 g_fprintf(outf, _("Client might be of a very old version\n"));
1564 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1566 _("ERROR: Client %s does not support selfcheck REP packet.\n"),
1568 g_fprintf(outf, _("Client might be of a very old version\n"));
1570 if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1571 !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1572 !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1574 _("ERROR: Client %s does not support sendsize REQ packet.\n"),
1576 g_fprintf(outf, _("Client might be of a very old version\n"));
1578 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1580 _("ERROR: Client %s does not support sendsize REP packet.\n"),
1582 g_fprintf(outf, _("Client might be of a very old version\n"));
1584 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1585 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1587 _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
1589 g_fprintf(outf, _("Client might be of a very old version\n"));
1591 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1593 _("ERROR: Client %s does not support sendbackup REP packet.\n"),
1595 g_fprintf(outf, _("Client might be of a very old version\n"));
1598 g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1599 req = vstralloc("SERVICE ", "selfcheck", "\n",
1601 has_features ? "features=" : "",
1602 has_features ? our_feature_string : "",
1603 has_features ? ";" : "",
1604 has_maxdumps ? "maxdumps=" : "",
1605 has_maxdumps ? number : "",
1606 has_maxdumps ? ";" : "",
1607 has_hostname ? "hostname=" : "",
1608 has_hostname ? hostp->hostname : "",
1609 has_hostname ? ";" : "",
1610 has_config ? "config=" : "",
1611 has_config ? get_config_name() : "",
1612 has_config ? ";" : "",
1616 req_len = strlen(req);
1617 req_len += 128; /* room for SECURITY ... */
1618 req_len += 256; /* room for non-disk answers */
1619 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1625 char *qname, *b64disk;
1626 char *qdevice, *b64device = NULL;
1627 GPtrArray *errarray;
1630 if(dp->up != DISK_READY || dp->todo != 1) {
1633 qname = quote_string(dp->name);
1635 errarray = validate_optionstr(dp);
1636 if (errarray->len > 0) {
1637 for (i=0; i < errarray->len; i++) {
1638 g_fprintf(outf, _("ERROR: %s:%s %s\n"),
1639 hostp->hostname, qname,
1640 (char *)g_ptr_array_index(errarray, i));
1642 g_ptr_array_free(errarray, TRUE);
1646 } else if (am_has_feature(hostp->features, fe_req_xml)) {
1647 o = xml_optionstr(dp, 0);
1652 b64disk = amxml_format_tag("disk", dp->name);
1653 qdevice = quote_string(dp->device);
1655 b64device = amxml_format_tag("diskdevice", dp->device);
1656 if ((dp->name && qname[0] == '"') ||
1657 (dp->device && qdevice[0] == '"')) {
1658 if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1660 _("WARNING: %s:%s:%s host does not support quoted text\n"),
1661 hostp->hostname, qname, qdevice);
1662 g_fprintf(outf, _("You must upgrade amanda on the client to "
1663 "specify a quoted text/device in the disklist, "
1664 "or don't use quoted text for the device.\n"));
1669 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1671 _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
1672 hostp->hostname, qname, dp->device);
1673 g_fprintf(outf, _("You must upgrade amanda on the client to "
1674 "specify a diskdevice in the disklist "
1675 "or don't specify a diskdevice in the disklist.\n"));
1677 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1679 _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
1680 hostp->hostname, qname, dp->device);
1681 g_fprintf(outf, _("You must upgrade amanda on the client to "
1682 "specify a diskdevice in the disklist"
1683 " or don't specify a diskdevice in the disklist.\n"));
1685 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1687 _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
1688 hostp->hostname, qname, dp->device);
1689 g_fprintf(outf, _("You must upgrade amanda on the client to "
1690 "specify a diskdevice in the disklist"
1691 " or don't specify a diskdevice in the disklist.\n"));
1694 if (dp->data_path != DATA_PATH_AMANDA &&
1695 !am_has_feature(hostp->features, fe_xml_data_path)) {
1697 _("ERROR: Client %s does not support %s data-path\n"),
1698 hostp->hostname, data_path_to_string(dp->data_path));
1699 } else if (dp->data_path == DATA_PATH_DIRECTTCP &&
1700 !am_has_feature(hostp->features, fe_xml_directtcp_list)) {
1702 _("ERROR: Client %s does not support directtcp data-path\n"),
1707 (strcmp(dp->program,"DUMP") == 0 ||
1708 strcmp(dp->program,"GNUTAR") == 0)) {
1709 if(strcmp(dp->program, "DUMP") == 0 &&
1710 !am_has_feature(hostp->features, fe_program_dump)) {
1711 g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
1712 hostp->hostname, qname);
1713 g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
1714 "or you can use another program.\n"));
1716 if(strcmp(dp->program, "GNUTAR") == 0 &&
1717 !am_has_feature(hostp->features, fe_program_gnutar)) {
1718 g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
1719 hostp->hostname, qname);
1720 g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
1721 "or you can use another program.\n"));
1723 estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
1724 if(estimate == ES_CALCSIZE &&
1725 !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1726 g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
1727 "estimate, using CLIENT.\n"),
1728 hostp->hostname, qname);
1729 g_fprintf(outf, _("You must upgrade amanda on the client to use "
1730 "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
1731 estimate = ES_CLIENT;
1733 if(estimate == ES_CALCSIZE &&
1734 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1735 calcsize = "CALCSIZE ";
1739 if(dp->compress == COMP_CUST &&
1740 !am_has_feature(hostp->features, fe_options_compress_cust)) {
1742 _("ERROR: Client %s does not support custom compression.\n"),
1744 g_fprintf(outf, _("You must upgrade amanda on the client to "
1745 "use custom compression\n"));
1746 g_fprintf(outf, _("Otherwise you can use the default client "
1747 "compression program.\n"));
1749 if(dp->encrypt == ENCRYPT_CUST ) {
1750 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1752 _("ERROR: Client %s does not support data encryption.\n"),
1754 g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
1756 } else if ( dp->compress == COMP_SERVER_FAST ||
1757 dp->compress == COMP_SERVER_BEST ||
1758 dp->compress == COMP_SERVER_CUST ) {
1760 _("ERROR: %s: Client encryption with server compression "
1761 "is not supported. See amanda.conf(5) for detail.\n"),
1766 if (am_has_feature(hostp->features, fe_req_xml)) {
1767 l = vstralloc("<dle>\n"
1770 "</program>\n", NULL);
1771 es = xml_estimate(dp->estimatelist, hostp->features);
1772 vstrextend(&l, es, "\n", NULL);
1774 vstrextend(&l, " ", b64disk, "\n", NULL);
1776 vstrextend(&l, " ", b64device, "\n", NULL);
1777 vstrextend(&l, o, "</dle>\n", NULL);
1780 l = vstralloc(calcsize,
1789 l = vstralloc(calcsize,
1799 if (!am_has_feature(hostp->features, fe_program_application_api) ||
1800 !am_has_feature(hostp->features, fe_req_xml)) {
1801 g_fprintf(outf, _("ERROR: %s:%s does not support APPLICATION-API.\n"),
1802 hostp->hostname, qname);
1803 g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
1804 " It is case sensitive\n"));
1808 l = vstralloc("<dle>\n"
1809 " <program>APPLICATION</program>\n", NULL);
1810 if (dp->application) {
1811 application_t *application;
1814 application = lookup_application(dp->application);
1817 _("ERROR: application '%s' not found.\n"), dp->application);
1819 xml_app = xml_application(dp, application, hostp->features);
1820 vstrextend(&l, xml_app, NULL);
1824 if (dp->pp_scriptlist) {
1825 if (!am_has_feature(hostp->features, fe_pp_script)) {
1827 _("ERROR: %s:%s does not support SCRIPT-API.\n"),
1828 hostp->hostname, qname);
1831 es = xml_estimate(dp->estimatelist, hostp->features);
1832 vstrextend(&l, es, "\n", NULL);
1834 vstrextend(&l, " ", b64disk, "\n", NULL);
1836 vstrextend(&l, " ", b64device, "\n", NULL);
1837 vstrextend(&l, o, "</dle>\n", NULL);
1848 dp->up = DISK_ACTIVE;
1852 else { /* noop service */
1853 req = vstralloc("SERVICE ", "noop", "\n",
1855 "features=", our_feature_string, ";",
1858 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1859 if(dp->up != DISK_READY || dp->todo != 1) {
1866 if(disk_count == 0) {
1868 hostp->up = HOST_DONE;
1872 secdrv = security_getdriver(hostp->disks->auth);
1873 if (secdrv == NULL) {
1874 fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
1875 hostp->disks->auth, hostp->hostname);
1877 protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf,
1878 req, conf_ctimeout, handle_result, hostp);
1883 hostp->up = HOST_ACTIVE;
1887 start_client_checks(
1896 switch(pid = fork()) {
1898 error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
1911 set_pname("amcheck-clients");
1915 if((outf = fdopen(fd, "w")) == NULL) {
1916 error(_("fdopen %d: %s"), fd, strerror(errno));
1921 g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
1922 g_fprintf(outf, "--------------------------------\n");
1926 hostcount = remote_errors = 0;
1928 for(dp = origq.head; dp != NULL; dp = dp->next) {
1930 if(hostp->up == HOST_READY && dp->todo == 1) {
1931 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
1932 run_server_scripts(EXECUTE_ON_PRE_HOST_AMCHECK,
1933 get_config_name(), dp1, -1);
1935 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
1936 run_server_scripts(EXECUTE_ON_PRE_DLE_AMCHECK,
1937 get_config_name(), dp1, -1);
1947 g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."),
1948 _("Client check: %d hosts checked in %s seconds."),
1950 hostcount, walltime_str(curclock()));
1951 g_fprintf(outf, plural(_(" %d problem found.\n"),
1952 _(" %d problems found.\n"), remote_errors),
1956 exit(userbad || remote_errors > 0);
1965 security_handle_t * sech)
1975 hostp = (am_host_t *)datap;
1976 hostp->up = HOST_READY;
1980 _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
1981 security_geterror(sech));
1983 hostp->up = HOST_DONE;
1988 g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
1989 hostp->hostname, pkt->body);
1996 skip_quoted_line(s, ch);
1997 if (s[-2] == '\n') {
2001 if(strncmp_const(line, "OPTIONS ") == 0) {
2003 t = strstr(line, "features=");
2004 if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
2005 char *u = strchr(t, ';');
2008 t += SIZEOF("features=")-1;
2009 am_release_feature_set(hostp->features);
2010 if((hostp->features = am_string_to_feature(t)) == NULL) {
2011 g_fprintf(outf, _("ERROR: %s: bad features value: '%s'\n"),
2012 hostp->hostname, t);
2013 g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
2015 hostp->up = HOST_DONE;
2024 if(strncmp_const(line, "OK ") == 0) {
2029 if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
2030 skip_whitespace(t, tch);
2032 * If the "error" is that the "noop" service is unknown, it
2033 * just means the client is "old" (does not support the service).
2034 * We can ignore this.
2036 if(!((hostp->features == NULL) && (pkt->type == P_NAK)
2037 && ((strcmp(t - 1, "unknown service: noop") == 0)
2038 || (strcmp(t - 1, "noop: invalid service") == 0)))) {
2039 g_fprintf(outf, _("ERROR: %s%s: %s\n"),
2040 (pkt->type == P_NAK) ? "NAK " : "",
2044 hostp->up = HOST_DONE;
2049 g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
2050 hostp->hostname, line);
2052 hostp->up = HOST_DONE;
2054 if(hostp->up == HOST_READY && hostp->features == NULL) {
2056 * The client does not support the features list, so give it an
2059 dbprintf(_("no feature set from host %s\n"), hostp->hostname);
2060 hostp->features = am_set_default_feature_set();
2062 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2063 if(dp->up == DISK_ACTIVE) {
2068 if(hostp->up == HOST_DONE) {
2069 security_close_connection(sech, hostp->hostname);
2070 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2071 run_server_scripts(EXECUTE_ON_POST_DLE_AMCHECK,
2072 get_config_name(), dp, -1);
2074 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2075 run_server_scripts(EXECUTE_ON_POST_HOST_AMCHECK,
2076 get_config_name(), dp, -1);
2079 /* try to clean up any defunct processes, since Amanda doesn't wait() for
2081 while(waitpid(-1, NULL, WNOHANG)> 0);