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
46 #include "taperscan.h"
47 #include "server_util.h"
48 #include "pipespawn.h"
49 #include "amfeatures.h"
52 #include "timestamp.h"
54 #define BUFFER_SIZE 32768
56 static time_t conf_ctimeout;
59 static disklist_t origq;
61 static uid_t uid_dumpuser;
66 pid_t start_client_checks(int fd);
67 pid_t start_server_check(int fd, int do_localchk, int do_tapechk);
68 int main(int argc, char **argv);
69 int check_tapefile(FILE *outf, char *tapefile);
70 int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
75 error(_("Usage: amcheck%s [-am] [-w] [-sclt] [-M <address>] <conf> [host [disk]* ]* [-o configoption]*"), versionsuffix());
79 static am_feature_t *our_features = NULL;
80 static char *our_feature_string = NULL;
81 static char *displayunit;
82 static long int unitdivisor;
89 char buffer[BUFFER_SIZE];
91 char *mainfname = NULL;
92 char pid_str[NUM_STR_SIZE];
93 int do_clientchk, client_probs;
94 int do_localchk, do_tapechk, server_probs;
95 pid_t clientchk_pid, serverchk_pid;
96 int opt, tempfd, mainfd;
105 char *tempfname = NULL;
111 config_overwrites_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 erroutput_type = ERR_INTERACTIVE;
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 parse_conf(argc, argv, &new_argc, &new_argv);
156 /* process arguments */
158 cfg_ovr = new_config_overwrites(argc/2);
159 while((opt = getopt(argc, argv, "M:mawsclto:")) != EOF) {
161 case 'M': mailto=stralloc(optarg);
162 if(!validate_mailto(mailto)){
163 g_printf(_("Invalid characters in mail address\n"));
171 g_printf(_("You can't use -%c because configure didn't "
172 "find a mailer./usr/bin/mail not found\n"),
182 g_printf(_("You can't use -%c because configure didn't "
183 "find a mailer./usr/bin/mail not found\n"),
188 case 's': do_localchk = do_tapechk = 1;
190 case 'c': do_clientchk = 1;
192 case 'l': do_localchk = 1;
194 case 'w': overwrite = 1;
196 case 'o': add_config_overwrite_opt(cfg_ovr, optarg);
198 case 't': do_tapechk = 1;
205 argc -= optind, argv += optind;
206 if(argc < 1) usage();
209 if ((do_localchk | do_clientchk | do_tapechk) == 0) {
210 /* Check everything if individual checks were not asked for */
211 do_localchk = do_clientchk = do_tapechk = 1;
217 config_init(CONFIG_INIT_EXPLICIT_NAME|CONFIG_INIT_FATAL,
219 apply_config_overwrites(cfg_ovr);
220 dbrename(config_name, DBG_SUBDIR_SERVER);
222 if(mailout && !mailto &&
223 (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
224 g_printf(_("\nWARNING:No mail address configured in amanda.conf.\n"));
225 g_printf(_("To receive dump results by email configure the "
226 "\"mailto\" parameter in amanda.conf\n"));
228 g_printf(_("When using -a option please specify -Maddress also\n\n"));
230 g_printf(_("Use -Maddress instead of -m\n\n"));
233 if(mailout && !mailto)
235 if(getconf_seen(CNF_MAILTO) &&
236 strlen(getconf_str(CNF_MAILTO)) > 0) {
237 if(!validate_mailto(getconf_str(CNF_MAILTO))){
238 g_printf(_("\nMail address in amanda.conf has invalid characters"));
239 g_printf(_("\nNo email will be sent\n"));
244 g_printf(_("\nNo mail address configured in amanda.conf\n"));
246 g_printf(_("When using -a option please specify -Maddress also\n\n"));
248 g_printf(_("Use -Maddress instead of -m\n\n"));
253 conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
255 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
256 if(read_diskfile(conf_diskfile, &origq) < 0) {
257 error(_("could not load disklist %s. Make sure it exists and has correct permissions"), conf_diskfile);
260 errstr = match_disklist(&origq, argc-1, argv+1);
262 g_printf(_("%s"),errstr);
265 amfree(conf_diskfile);
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 (fullwrite(mainfd, wait_msg, strlen(wait_msg)) < 0) {
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 = fullread(tempfd, buffer, SIZEOF(buffer))) > 0) {
376 if (fullwrite(mainfd, buffer, (size_t)size) < 0) {
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 (fullwrite(mainfd, version_string, strlen(version_string)) < 0) {
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 */
401 #define MAILTO_LIMIT 10
403 if((server_probs || client_probs || alwaysmail) && mailout) {
414 char *extra_info = NULL;
419 if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
420 error(_("lseek main file: %s"), strerror(errno));
423 if(alwaysmail && !(server_probs || client_probs)) {
424 subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"),
425 getconf_str(CNF_ORG));
427 subject = vstrallocf(
428 _("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"),
429 getconf_str(CNF_ORG));
432 * Variable arg lists are hard to deal with when we do not know
433 * ourself how many args are involved. Split the address list
434 * and hope there are not more than 9 entries.
436 * Remember that split() returns the original input string in
437 * argv[0], so we have to skip over that.
439 a = (char **) alloc((MAILTO_LIMIT + 1) * SIZEOF(char *));
440 memset(a, 0, (MAILTO_LIMIT + 1) * SIZEOF(char *));
445 r = (ssize_t)split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " ");
448 if((nullfd = open("/dev/null", O_RDWR)) < 0) {
449 error("nullfd: /dev/null: %s", strerror(errno));
452 pipespawn(MAILER, STDIN_PIPE | STDERR_PIPE,
453 &mailfd, &nullfd, &errfd,
456 a[1], a[2], a[3], a[4],
457 a[5], a[6], a[7], a[8], a[9],
461 * There is the potential for a deadlock here since we are writing
462 * to the process and then reading stderr, but in the normal case,
463 * nothing should be coming back to us, and hopefully in error
464 * cases, the pipe will break and we will exit out of the loop.
466 signal(SIGPIPE, SIG_IGN);
467 while((r = fullread(mainfd, buffer, SIZEOF(buffer))) > 0) {
468 if((w = fullwrite(mailfd, buffer, (size_t)r)) != (ssize_t)r) {
469 if(w < 0 && errno == EPIPE) {
470 strappend(extra_info, _("EPIPE writing to mail process\n"));
473 error(_("mailfd write: %s"), strerror(errno));
476 error(_("mailfd write: wrote %zd instead of %zd"), w, r);
482 ferr = fdopen(errfd, "r");
484 error(_("Can't fdopen: %s"), strerror(errno));
487 for(; (line = agets(ferr)) != NULL; free(line)) {
490 strappend(extra_info, line);
491 strappend(extra_info, "\n");
496 while (wait(&retstat) != -1) {
497 if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) {
498 char *mailer_error = str_exit_status("mailer", retstat);
499 strappend(err, mailer_error);
500 amfree(mailer_error);
507 fputs(extra_info, stderr);
510 error(_("error running mailer %s: %s"), MAILER, err?err:"(unknown)");
515 free_new_argv(new_argc, new_argv);
516 free_server_config();
519 return (server_probs || client_probs);
522 /* --------------------------------------------------- */
524 static char *datestamp;
525 static FILE *errf = NULL;
535 if (stat(tapefile, &statbuf) == 0) {
536 if (!S_ISREG(statbuf.st_mode)) {
537 quoted = quote_string(tapefile);
538 g_fprintf(outf, _("ERROR: tapelist %s: should be a regular file.\n"),
542 } else if (access(tapefile, F_OK) != 0) {
543 quoted = quote_string(tapefile);
544 g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted);
547 } else if (access(tapefile, W_OK) != 0) {
548 quoted = quote_string(tapefile);
549 g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted);
569 pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL);
570 quoted = quote_string(pgm);
571 if(stat(pgm, &statbuf) == -1) {
572 g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
575 } else if (!S_ISREG(statbuf.st_mode)) {
576 g_fprintf(outf, _("ERROR: program %s: not a file\n"),
579 } else if (access(pgm, X_OK) == -1) {
580 g_fprintf(outf, _("ERROR: program %s: not executable\n"),
583 #ifndef SINGLE_USERID
586 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
587 g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"),
591 /* Quiet unused parameter warnings */
594 #endif /* SINGLE_USERID */
601 /* check that the tape is a valid amanda tape
602 Returns TRUE if all tests passed; FALSE otherwise. */
603 static gboolean test_tape_status(FILE * outf) {
606 GValue property_value;
608 char * tapename = NULL;
609 ReadLabelStatusFlags label_status;
611 bzero(&property_value, sizeof(property_value));
613 tapename = getconf_str(CNF_TAPEDEV);
614 g_return_val_if_fail(tapename != NULL, FALSE);
618 if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
620 _("WARNING: if a tape changer is not available, runtapes "
621 "must be set to 1\n"));
622 g_fprintf(outf, _("Change the value of the \"runtapes\" parameter in "
623 "amanda.conf or configure a tape changer\n"));
626 tape_status = taper_scan(NULL, &label, &datestamp, &tapename, NULL,
627 FILE_taperscan_output_callback, outf,
629 if (tape_status < 0) {
630 tape_t *exptape = lookup_last_reusable_tape(0);
631 g_fprintf(outf, _(" (expecting "));
632 if(exptape != NULL) g_fprintf(outf, _("tape %s or "), exptape->label);
633 g_fprintf(outf, _("a new tape)\n"));
638 device = device_open(tapename);
640 if (device == NULL) {
641 g_fprintf(outf, "ERROR: Could not open tape device.\n");
646 device_set_startup_properties_from_config(device);
647 label_status = device_read_label(device);
649 if (tape_status == 3 &&
650 !(label_status & READ_LABEL_STATUS_VOLUME_UNLABELED)) {
651 if (label_status == READ_LABEL_STATUS_SUCCESS) {
652 g_fprintf(outf, "WARNING: Volume was unlabeled, but now "
653 "is labeled \"%s\".\n", device->volume_label);
655 } else if (label_status != READ_LABEL_STATUS_SUCCESS && tape_status != 3) {
657 g_english_strjoinv_and_free
658 (g_flags_nick_to_strv(label_status &
659 (~READ_LABEL_STATUS_VOLUME_UNLABELED),
660 READ_LABEL_STATUS_FLAGS_TYPE), "or");
661 g_fprintf(outf, "WARNING: Reading label the second time failed: "
662 "One of %s.\n", errstr);
664 } else if (tape_status != 3 &&
665 (device->volume_label == NULL || label == NULL ||
666 strcmp(device->volume_label, label) != 0)) {
667 g_fprintf(outf, "WARNING: Label mismatch on re-read: "
668 "Got %s first, then %s.\n", label, device->volume_label);
671 /* If we can't get this property, it's not an error. Maybe the device
672 * doesn't support this property, or needs an actual volume to know
674 if (device_property_get(device, PROPERTY_MEDIUM_TYPE, &property_value)) {
675 g_assert(G_VALUE_TYPE(&property_value) == MEDIA_ACCESS_MODE_TYPE);
676 if (g_value_get_enum(&property_value) ==
677 MEDIA_ACCESS_MODE_WRITE_ONLY) {
678 g_fprintf(outf, "WARNING: Media access mode is WRITE_ONLY, "
679 "dumps will be thrown away.\n");
684 char *timestamp = get_undef_timestamp();
685 if (!device_start(device, ACCESS_WRITE, label, timestamp)) {
686 if (tape_status == 3) {
687 g_fprintf(outf, "ERROR: Could not label brand new tape.\n");
690 "ERROR: tape %s label ok, but is not writable.\n",
695 g_object_unref(device);
697 } else { /* Write succeeded. */
698 if (tape_status != 3) {
699 g_fprintf(outf, "Tape %s is writable; rewrote label.\n", label);
701 g_fprintf(outf, "Wrote label %s to brand new tape.\n", label);
705 } else { /* !overwrite */
706 g_fprintf(outf, "NOTE: skipping tape-writable test\n");
707 if (tape_status == 3) {
709 "Found a brand new tape, will label it %s.\n",
712 g_fprintf(outf, "Tape %s label ok\n", label);
715 g_object_unref(device);
726 struct fs_usage fsusage;
729 pid_t pid G_GNUC_UNUSED;
730 int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
731 int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
732 int testtape = do_tapechk;
733 tapetype_t *tp = NULL;
738 switch(pid = fork()) {
740 error(_("could not spawn a process for checking the server: %s"), strerror(errno));
741 g_assert_not_reached();
753 set_pname("amcheck-server");
757 if((outf = fdopen(fd, "w")) == NULL) {
758 error(_("fdopen %d: %s"), fd, strerror(errno));
763 g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
764 g_fprintf(outf, "-----------------------------\n");
766 if (do_localchk || testtape) {
767 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
771 * Check various server side config file settings.
778 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
779 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
780 g_fprintf(outf, _("ERROR: %s\n"), errstr);
784 lbl_templ = tapetype_get_lbl_templ(tp);
785 if(strcmp(lbl_templ, "") != 0) {
786 lbl_templ = config_dir_relative(lbl_templ);
787 if(access(lbl_templ, R_OK) == -1) {
789 _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
795 g_fprintf(outf, _("ERROR:lbl-templ set but no LPRCMD defined. You should reconfigure amanda\n and make sure it finds a lpr or lp command.\n"));
800 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
801 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) {
802 g_fprintf(outf, _("WARNING: flush_threshold_dumped (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
803 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED),
804 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
807 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
808 getconf_int(CNF_TAPERFLUSH)) {
809 g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
810 getconf_int(CNF_TAPERFLUSH),
811 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
814 if (getconf_int(CNF_TAPERFLUSH) > 0 &&
815 !getconf_boolean(CNF_AUTOFLUSH)) {
816 g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' if taperflush (%d) is greater that 0.\n"),
817 getconf_int(CNF_TAPERFLUSH));
820 /* Double-check that 'localhost' resolves properly */
821 if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) {
822 g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
828 * Look up the programs used on the server side.
832 * entreprise version will do planner/dumper suid check
834 if(access(amlibexecdir, X_OK) == -1) {
835 quoted = quote_string(amlibexecdir);
836 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
838 g_fprintf(outf, _("Check permissions\n"));
842 if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
844 if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
846 if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
848 if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
850 if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
852 if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
855 if(access(sbindir, X_OK) == -1) {
856 quoted = quote_string(sbindir);
857 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
859 g_fprintf(outf, _("Check permissions\n"));
863 if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
865 if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
867 if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
869 if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
872 if(access(COMPRESS_PATH, X_OK) == -1) {
873 quoted = quote_string(COMPRESS_PATH);
874 g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
875 "and indexing will not work. \n"),quoted);
876 g_fprintf(outf, _("Check permissions\n"));
882 * Check that the directory for the tapelist file is writable, as well
883 * as the tapelist file itself (if it already exists). Also, check for
884 * a "hold" file (just because it is convenient to do it here) and warn
885 * if tapedev is set to the null device.
888 if(do_localchk || do_tapechk) {
897 tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
899 * XXX There Really Ought to be some error-checking here... dhw
901 tape_dir = stralloc(tapefile);
902 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
905 * else whine Really Loudly about a path with no slashes??!?
908 if(access(tape_dir, W_OK) == -1) {
909 quoted = quote_string(tape_dir);
910 g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"),
915 else if(stat(tapefile, &statbuf) == -1) {
916 quoted = quote_string(tape_dir);
917 g_fprintf(outf, _("ERROR: tapelist %s (%s), "
918 "you must create an empty file.\n"),
919 quoted, strerror(errno));
924 tapebad |= check_tapefile(outf, tapefile);
925 if (tapebad == 0 && read_tapelist(tapefile)) {
926 quoted = quote_string(tapefile);
927 g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
931 newtapefile = stralloc2(tapefile, ".new");
932 tapebad |= check_tapefile(outf, newtapefile);
934 newtapefile = stralloc2(tapefile, ".amlabel");
935 tapebad |= check_tapefile(outf, newtapefile);
937 newtapefile = stralloc2(tapefile, ".amlabel.new");
938 tapebad |= check_tapefile(outf, newtapefile);
940 newtapefile = stralloc2(tapefile, ".yesterday");
941 tapebad |= check_tapefile(outf, newtapefile);
943 newtapefile = stralloc2(tapefile, ".yesterday.new");
944 tapebad |= check_tapefile(outf, newtapefile);
947 holdfile = config_dir_relative("hold");
948 if(access(holdfile, F_OK) != -1) {
949 quoted = quote_string(holdfile);
950 g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
951 g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
952 g_fprintf(outf, _("You might want to delete the existing hold file\n"));
958 tapename = getconf_str(CNF_TAPEDEV);
959 if (tapename == NULL) {
960 if (getconf_str(CNF_TPCHANGER) == NULL) {
961 g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
968 /* check available disk space */
971 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = holdingdisk_next(hdp)) {
972 quoted = quote_string(holdingdisk_get_diskdir(hdp));
973 if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
974 g_fprintf(outf, _("ERROR: holding dir %s (%s), "
975 "you must create a directory.\n"),
976 quoted, strerror(errno));
982 /* do the division first to avoid potential integer overflow */
983 if (fsusage.fsu_bavail_top_bit_set)
986 kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
988 if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
989 g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
990 quoted, strerror(errno));
991 g_fprintf(outf, _("Check permissions\n"));
994 else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
995 g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
996 quoted, strerror(errno));
997 g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
1000 else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
1003 _("WARNING: holding disk %s: "
1004 "no space available (%lld %sB requested)\n"), quoted,
1005 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1009 else if(kb_avail < holdingdisk_get_disksize(hdp)) {
1011 _("WARNING: holding disk %s: "
1012 "only %lld %sB available (%lld %sB requested)\n"), quoted,
1013 (long long)(kb_avail / (off_t)unitdivisor),
1015 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1021 _("Holding disk %s: %lld %sB disk space available,"
1022 " using %lld %sB as requested\n"),
1024 (long long)(kb_avail / (off_t)unitdivisor),
1026 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1031 if(kb_avail < -holdingdisk_get_disksize(hdp)) {
1033 _("WARNING: holding disk %s: "
1034 "only %lld %sB free, using nothing\n"),
1035 quoted, (long long)(kb_avail / (off_t)unitdivisor),
1037 g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
1042 _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
1044 (long long)(kb_avail/(off_t)unitdivisor),
1046 (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
1054 /* check that the log file is writable if it already exists */
1060 struct stat stat_old;
1061 struct stat statbuf;
1063 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
1064 logfile = vstralloc(conf_logdir, "/log", NULL);
1066 quoted = quote_string(conf_logdir);
1067 if(stat(conf_logdir, &statbuf) == -1) {
1068 g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
1069 quoted, strerror(errno));
1072 else if(access(conf_logdir, W_OK) == -1) {
1073 g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
1078 if(access(logfile, F_OK) == 0) {
1081 if(access(logfile, W_OK) != 0) {
1082 quoted = quote_string(logfile);
1083 g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
1088 olddir = vstralloc(conf_logdir, "/oldlog", NULL);
1089 quoted = quote_string(olddir);
1090 if (stat(olddir,&stat_old) == 0) { /* oldlog exist */
1091 if(!(S_ISDIR(stat_old.st_mode))) {
1092 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1094 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1097 if(access(olddir, W_OK) == -1) {
1098 g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
1099 g_fprintf(outf, _("Check permissions\n"));
1103 else if(lstat(olddir,&stat_old) == 0) {
1104 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1106 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1112 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
1113 if (access(logfile, F_OK) == 0) {
1121 amfree(conf_logdir);
1125 tapebad = !test_tape_status(outf);
1126 } else if (do_tapechk) {
1127 g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
1128 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1129 } else if (logbad == 2) {
1130 g_fprintf(outf, _("WARNING: amdump or amflush seem to be running\n"));
1131 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1133 g_fprintf(outf, _("NOTE: skipping tape checks\n"));
1137 * See if the information file and index directory for each client
1138 * and disk is OK. Since we may be seeing clients and/or disks for
1139 * the first time, these are just warnings, not errors.
1142 char *conf_infofile;
1143 char *conf_indexdir;
1144 char *hostinfodir = NULL;
1145 char *hostindexdir = NULL;
1146 char *diskdir = NULL;
1147 char *infofile = NULL;
1148 struct stat statbuf;
1151 int indexdir_checked = 0;
1152 int hostindexdir_checked = 0;
1155 int conf_tapecycle, conf_runspercycle;
1157 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1158 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1160 if(conf_tapecycle <= conf_runspercycle) {
1161 g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
1162 conf_tapecycle, conf_runspercycle);
1165 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
1166 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
1168 quoted = quote_string(conf_infofile);
1169 if(stat(conf_infofile, &statbuf) == -1) {
1170 if (errno == ENOENT) {
1171 g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
1173 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1175 g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
1176 quoted, strerror(errno));
1179 amfree(conf_infofile);
1180 } else if (!S_ISDIR(statbuf.st_mode)) {
1181 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
1182 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1183 amfree(conf_infofile);
1185 } else if (access(conf_infofile, W_OK) == -1) {
1186 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1187 g_fprintf(outf, _("Check permissions\n"));
1188 amfree(conf_infofile);
1191 char *errmsg = NULL;
1192 if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1193 g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1197 strappend(conf_infofile, "/");
1201 while(!empty(origq)) {
1202 hostp = origq.head->host;
1203 host = sanitise_filename(hostp->hostname);
1205 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1206 quoted = quote_string(hostinfodir);
1207 if(stat(hostinfodir, &statbuf) == -1) {
1208 if (errno == ENOENT) {
1209 g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
1212 _("NOTE: it will be created on the next run.\n"));
1214 g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
1215 quoted, strerror(errno));
1218 amfree(hostinfodir);
1219 } else if (!S_ISDIR(statbuf.st_mode)) {
1220 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1222 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1223 amfree(hostinfodir);
1225 } else if (access(hostinfodir, W_OK) == -1) {
1226 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1227 g_fprintf(outf, _("Check permissions\n"));
1228 amfree(hostinfodir);
1231 strappend(hostinfodir, "/");
1235 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1236 disk = sanitise_filename(dp->name);
1240 diskdir = newstralloc2(diskdir, hostinfodir, disk);
1241 infofile = vstralloc(diskdir, "/", "info", NULL);
1242 quoted = quote_string(diskdir);
1243 quotedif = quote_string(infofile);
1244 if(stat(diskdir, &statbuf) == -1) {
1245 if (errno == ENOENT) {
1246 g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
1249 _("NOTE: it will be created on the next run.\n"));
1251 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1252 quoted, strerror(errno));
1255 } else if (!S_ISDIR(statbuf.st_mode)) {
1256 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1258 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1260 } else if (access(diskdir, W_OK) == -1) {
1261 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
1263 g_fprintf(outf,_("Check permissions\n"));
1265 } else if(stat(infofile, &statbuf) == -1) {
1266 if (errno == ENOENT) {
1267 g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
1269 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1271 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1272 quoted, strerror(errno));
1275 } else if (!S_ISREG(statbuf.st_mode)) {
1276 g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
1278 g_fprintf(outf, _("Remove the entry and create a new file\n"));
1280 } else if (access(infofile, R_OK) == -1) {
1281 g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
1290 if(! indexdir_checked) {
1291 quoted = quote_string(conf_indexdir);
1292 if(stat(conf_indexdir, &statbuf) == -1) {
1293 if (errno == ENOENT) {
1294 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1296 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1298 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1299 quoted, strerror(errno));
1302 amfree(conf_indexdir);
1303 } else if (!S_ISDIR(statbuf.st_mode)) {
1304 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1306 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1307 amfree(conf_indexdir);
1309 } else if (access(conf_indexdir, W_OK) == -1) {
1310 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1312 amfree(conf_indexdir);
1315 strappend(conf_indexdir, "/");
1317 indexdir_checked = 1;
1321 if(! hostindexdir_checked) {
1322 hostindexdir = stralloc2(conf_indexdir, host);
1323 quoted = quote_string(hostindexdir);
1324 if(stat(hostindexdir, &statbuf) == -1) {
1325 if (errno == ENOENT) {
1326 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1328 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1330 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1331 quoted, strerror(errno));
1334 amfree(hostindexdir);
1335 } else if (!S_ISDIR(statbuf.st_mode)) {
1336 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1338 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1339 amfree(hostindexdir);
1341 } else if (access(hostindexdir, W_OK) == -1) {
1342 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1344 amfree(hostindexdir);
1347 strappend(hostindexdir, "/");
1349 hostindexdir_checked = 1;
1353 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1354 quoted = quote_string(diskdir);
1355 if(stat(diskdir, &statbuf) == -1) {
1356 if (errno == ENOENT) {
1357 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1359 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1361 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1362 quoted, strerror(errno));
1365 } else if (!S_ISDIR(statbuf.st_mode)) {
1366 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1368 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1370 } else if (access(diskdir, W_OK) == -1) {
1371 g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
1380 if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1381 if ( dp->srv_encrypt[0] == '\0' ) {
1382 g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
1383 g_fprintf(outf, _("Specify \"server_custom_encrypt\" in the dumptype\n"));
1386 else if(access(dp->srv_encrypt, X_OK) == -1) {
1387 g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
1389 g_fprintf(outf, _("Check file type\n"));
1393 if ( dp->compress == COMP_SERVER_CUST ) {
1394 if ( dp->srvcompprog[0] == '\0' ) {
1395 g_fprintf(outf, _("ERROR: server custom compression program "
1396 "not specified\n"));
1397 g_fprintf(outf, _("Specify \"server_custom_compress\" in "
1401 else if(access(dp->srvcompprog, X_OK) == -1) {
1402 quoted = quote_string(dp->srvcompprog);
1404 g_fprintf(outf, _("ERROR: %s is not executable, server custom "
1405 "compression will not work\n"),
1408 g_fprintf(outf, _("Check file type\n"));
1414 remove_disk(&origq, dp);
1417 amfree(hostindexdir);
1418 hostindexdir_checked = 0;
1421 amfree(hostinfodir);
1422 amfree(conf_infofile);
1423 amfree(conf_indexdir);
1428 g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
1444 /* --------------------------------------------------- */
1449 static void handle_result(void *, pkt_t *, security_handle_t *);
1450 void start_host(am_host_t *hostp);
1452 #define HOST_READY ((void *)0) /* must be 0 */
1453 #define HOST_ACTIVE ((void *)1)
1454 #define HOST_DONE ((void *)2)
1456 #define DISK_READY ((void *)0) /* must be 0 */
1457 #define DISK_ACTIVE ((void *)1)
1458 #define DISK_DONE ((void *)2)
1468 const security_driver_t *secdrv;
1469 char number[NUM_STR_SIZE];
1471 if(hostp->up != HOST_READY) {
1475 if (strcmp(hostp->hostname,"localhost") == 0) {
1477 _("WARNING: Usage of fully qualified hostname recommended for Client %s.\n"),
1482 * The first time through here we send a "noop" request. This will
1483 * return the feature list from the client if it supports that.
1484 * If it does not, handle_result() will set the feature list to an
1485 * empty structure. In either case, we do the disks on the second
1486 * (and subsequent) pass(es).
1489 if(hostp->features != NULL) { /* selfcheck service */
1490 int has_features = am_has_feature(hostp->features,
1491 fe_req_options_features);
1492 int has_hostname = am_has_feature(hostp->features,
1493 fe_req_options_hostname);
1494 int has_maxdumps = am_has_feature(hostp->features,
1495 fe_req_options_maxdumps);
1496 int has_config = am_has_feature(hostp->features,
1497 fe_req_options_config);
1499 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1500 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1502 _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
1504 g_fprintf(outf, _("Client might be of a very old version\n"));
1506 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1508 _("ERROR: Client %s does not support selfcheck REP packet.\n"),
1510 g_fprintf(outf, _("Client might be of a very old version\n"));
1512 if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1513 !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1514 !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1516 _("ERROR: Client %s does not support sendsize REQ packet.\n"),
1518 g_fprintf(outf, _("Client might be of a very old version\n"));
1520 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1522 _("ERROR: Client %s does not support sendsize REP packet.\n"),
1524 g_fprintf(outf, _("Client might be of a very old version\n"));
1526 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1527 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1529 _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
1531 g_fprintf(outf, _("Client might be of a very old version\n"));
1533 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1535 _("ERROR: Client %s does not support sendbackup REP packet.\n"),
1537 g_fprintf(outf, _("Client might be of a very old version\n"));
1540 g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1541 req = vstralloc("SERVICE ", "selfcheck", "\n",
1543 has_features ? "features=" : "",
1544 has_features ? our_feature_string : "",
1545 has_features ? ";" : "",
1546 has_maxdumps ? "maxdumps=" : "",
1547 has_maxdumps ? number : "",
1548 has_maxdumps ? ";" : "",
1549 has_hostname ? "hostname=" : "",
1550 has_hostname ? hostp->hostname : "",
1551 has_hostname ? ";" : "",
1552 has_config ? "config=" : "",
1553 has_config ? config_name : "",
1554 has_config ? ";" : "",
1558 req_len = strlen(req);
1559 req_len += 128; /* room for SECURITY ... */
1560 req_len += 256; /* room for non-disk answers */
1561 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1569 if(dp->up != DISK_READY || dp->todo != 1) {
1572 o = optionstr(dp, hostp->features, outf);
1577 qname = quote_string(dp->name);
1578 qdevice = quote_string(dp->device);
1579 if ((dp->name && qname[0] == '"') ||
1580 (dp->device && qdevice[0] == '"')) {
1581 if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1583 _("WARNING: %s:%s:%s host does not support quoted text\n"),
1584 hostp->hostname, qname, qdevice);
1585 g_fprintf(outf, _("You must upgrade amanda on the client to "
1586 "specify a quoted text/device in the disklist, "
1587 "or don't use quoted text for the device.\n"));
1592 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1594 _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
1595 hostp->hostname, qname, dp->device);
1596 g_fprintf(outf, _("You must upgrade amanda on the client to "
1597 "specify a diskdevice in the disklist "
1598 "or don't specify a diskdevice in the disklist.\n"));
1600 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1602 _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
1603 hostp->hostname, qname, dp->device);
1604 g_fprintf(outf, _("You must upgrade amanda on the client to "
1605 "specify a diskdevice in the disklist"
1606 " or don't specify a diskdevice in the disklist.\n"));
1608 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1610 _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
1611 hostp->hostname, qname, dp->device);
1612 g_fprintf(outf, _("You must upgrade amanda on the client to "
1613 "specify a diskdevice in the disklist"
1614 " or don't specify a diskdevice in the disklist.\n"));
1617 if(strcmp(dp->program,"DUMP") == 0 ||
1618 strcmp(dp->program,"GNUTAR") == 0) {
1619 if(strcmp(dp->program, "DUMP") == 0 &&
1620 !am_has_feature(hostp->features, fe_program_dump)) {
1621 g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
1622 hostp->hostname, qname);
1623 g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
1624 "or you can use another program.\n"));
1626 if(strcmp(dp->program, "GNUTAR") == 0 &&
1627 !am_has_feature(hostp->features, fe_program_gnutar)) {
1628 g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
1629 hostp->hostname, qname);
1630 g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
1631 "or you can use another program.\n"));
1633 if(dp->estimate == ES_CALCSIZE &&
1634 !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1635 g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
1636 "estimate, using CLIENT.\n"),
1637 hostp->hostname, qname);
1638 g_fprintf(outf, _("You must upgrade amanda on the client to use "
1639 "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
1640 dp->estimate = ES_CLIENT;
1642 if(dp->estimate == ES_CALCSIZE &&
1643 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1644 calcsize = "CALCSIZE ";
1648 if(dp->compress == COMP_CUST &&
1649 !am_has_feature(hostp->features, fe_options_compress_cust)) {
1651 _("ERROR: Client %s does not support custom compression.\n"),
1653 g_fprintf(outf, _("You must upgrade amanda on the client to "
1654 "use custom compression\n"));
1655 g_fprintf(outf, _("Otherwise you can use the default client "
1656 "compression program.\n"));
1658 if(dp->encrypt == ENCRYPT_CUST ) {
1659 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1661 _("ERROR: Client %s does not support data encryption.\n"),
1663 g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
1665 } else if ( dp->compress == COMP_SERVER_FAST ||
1666 dp->compress == COMP_SERVER_BEST ||
1667 dp->compress == COMP_SERVER_CUST ) {
1669 _("ERROR: %s: Client encryption with server compression "
1670 "is not supported. See amanda.conf(5) for detail.\n"),
1676 l = vstralloc(calcsize,
1686 l = vstralloc(calcsize,
1695 if(!am_has_feature(hostp->features, fe_program_backup_api)) {
1696 g_fprintf(outf, _("ERROR: %s:%s does not support BACKUP-API.\n"),
1697 hostp->hostname, qname);
1698 g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
1699 " It is case sensitive\n"));
1702 l = vstralloc("BACKUP ",
1713 l = vstralloc("BACKUP ",
1731 dp->up = DISK_ACTIVE;
1735 else { /* noop service */
1736 req = vstralloc("SERVICE ", "noop", "\n",
1738 "features=", our_feature_string, ";",
1741 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1742 if(dp->up != DISK_READY || dp->todo != 1) {
1749 if(disk_count == 0) {
1751 hostp->up = HOST_DONE;
1755 secdrv = security_getdriver(hostp->disks->security_driver);
1756 if (secdrv == NULL) {
1757 fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
1758 hostp->disks->security_driver, hostp->hostname);
1760 protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf,
1761 req, conf_ctimeout, handle_result, hostp);
1766 hostp->up = HOST_ACTIVE;
1770 start_client_checks(
1779 switch(pid = fork()) {
1781 error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
1794 set_pname("amcheck-clients");
1798 if((outf = fdopen(fd, "w")) == NULL) {
1799 error(_("fdopen %d: %s"), fd, strerror(errno));
1804 g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
1805 g_fprintf(outf, "--------------------------------\n");
1809 hostcount = remote_errors = 0;
1811 for(dp = origq.head; dp != NULL; dp = dp->next) {
1813 if(hostp->up == HOST_READY && dp->todo == 1) {
1822 g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."),
1823 _("Client check: %d hosts checked in %s seconds."),
1825 hostcount, walltime_str(curclock()));
1826 g_fprintf(outf, plural(_(" %d problem found.\n"),
1827 _(" %d problems found.\n"), remote_errors),
1831 exit(userbad || remote_errors > 0);
1840 security_handle_t * sech)
1850 hostp = (am_host_t *)datap;
1851 hostp->up = HOST_READY;
1855 _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
1856 security_geterror(sech));
1858 hostp->up = HOST_DONE;
1863 g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
1864 hostp->hostname, pkt->body);
1871 skip_quoted_line(s, ch);
1872 if (s[-2] == '\n') {
1876 if(strncmp_const(line, "OPTIONS ") == 0) {
1878 t = strstr(line, "features=");
1879 if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1880 t += SIZEOF("features=")-1;
1881 am_release_feature_set(hostp->features);
1882 if((hostp->features = am_string_to_feature(t)) == NULL) {
1883 g_fprintf(outf, _("ERROR: %s: bad features value: %s\n"),
1884 hostp->hostname, line);
1885 g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
1892 if(strncmp_const(line, "OK ") == 0) {
1897 if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
1898 skip_whitespace(t, tch);
1900 * If the "error" is that the "noop" service is unknown, it
1901 * just means the client is "old" (does not support the service).
1902 * We can ignore this.
1904 if(!((hostp->features == NULL) && (pkt->type == P_NAK)
1905 && ((strcmp(t - 1, "unknown service: noop") == 0)
1906 || (strcmp(t - 1, "noop: invalid service") == 0)))) {
1907 g_fprintf(outf, _("ERROR: %s%s: %s\n"),
1908 (pkt->type == P_NAK) ? "NAK " : "",
1912 hostp->up = HOST_DONE;
1917 g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
1918 hostp->hostname, line);
1920 hostp->up = HOST_DONE;
1922 if(hostp->up == HOST_READY && hostp->features == NULL) {
1924 * The client does not support the features list, so give it an
1927 dbprintf(_("no feature set from host %s\n"), hostp->hostname);
1928 hostp->features = am_set_default_feature_set();
1930 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1931 if(dp->up == DISK_ACTIVE) {
1936 if(hostp->up == HOST_DONE)
1937 security_close_connection(sech, hostp->hostname);
1938 /* try to clean up any defunct processes, since Amanda doesn't wait() for
1940 while(waitpid(-1, NULL, WNOHANG)> 0);