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 /* process arguments */
154 cfg_ovr = new_config_overwrites(argc/2);
155 while((opt = getopt(argc, argv, "M:mawsclto:")) != EOF) {
157 case 'M': mailto=stralloc(optarg);
158 if(!validate_mailto(mailto)){
159 g_printf(_("Invalid characters in mail address\n"));
167 g_printf(_("You can't use -%c because configure didn't "
168 "find a mailer./usr/bin/mail not found\n"),
178 g_printf(_("You can't use -%c because configure didn't "
179 "find a mailer./usr/bin/mail not found\n"),
184 case 's': do_localchk = do_tapechk = 1;
186 case 'c': do_clientchk = 1;
188 case 'l': do_localchk = 1;
190 case 'w': overwrite = 1;
192 case 'o': add_config_overwrite_opt(cfg_ovr, optarg);
194 case 't': do_tapechk = 1;
201 argc -= optind, argv += optind;
202 if(argc < 1) usage();
205 if ((do_localchk | do_clientchk | do_tapechk) == 0) {
206 /* Check everything if individual checks were not asked for */
207 do_localchk = do_clientchk = do_tapechk = 1;
213 config_init(CONFIG_INIT_EXPLICIT_NAME|CONFIG_INIT_FATAL,
215 apply_config_overwrites(cfg_ovr);
216 dbrename(config_name, DBG_SUBDIR_SERVER);
218 if(mailout && !mailto &&
219 (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
220 g_printf(_("\nWARNING:No mail address configured in amanda.conf.\n"));
221 g_printf(_("To receive dump results by email configure the "
222 "\"mailto\" parameter in amanda.conf\n"));
224 g_printf(_("When using -a option please specify -Maddress also\n\n"));
226 g_printf(_("Use -Maddress instead of -m\n\n"));
229 if(mailout && !mailto)
231 if(getconf_seen(CNF_MAILTO) &&
232 strlen(getconf_str(CNF_MAILTO)) > 0) {
233 if(!validate_mailto(getconf_str(CNF_MAILTO))){
234 g_printf(_("\nMail address in amanda.conf has invalid characters"));
235 g_printf(_("\nNo email will be sent\n"));
240 g_printf(_("\nNo mail address configured in amanda.conf\n"));
242 g_printf(_("When using -a option please specify -Maddress also\n\n"));
244 g_printf(_("Use -Maddress instead of -m\n\n"));
249 conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
251 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
252 if(read_diskfile(conf_diskfile, &origq) < 0) {
253 error(_("could not load disklist %s. Make sure it exists and has correct permissions"), conf_diskfile);
256 errstr = match_disklist(&origq, argc-1, argv+1);
258 g_printf(_("%s"),errstr);
261 amfree(conf_diskfile);
264 * Make sure we are running as the dump user. Don't use
265 * check_running_as(..) here, because we want to produce more
266 * verbose error messages.
268 dumpuser = getconf_str(CNF_DUMPUSER);
269 if ((pw = getpwnam(dumpuser)) == NULL) {
270 error(_("amanda.conf has dump user configured to \"%s\", but that user does not exist."), dumpuser);
273 uid_dumpuser = pw->pw_uid;
274 if ((pw = getpwuid(uid_me)) == NULL) {
275 error(_("cannot get username for running user, uid %ld is not in your user database."),
280 if (uid_me != uid_dumpuser) {
281 error(_("running as user \"%s\" instead of \"%s\".\n"
282 "Change user to \"%s\" or change dump user to \"%s\" in amanda.conf"),
283 pw->pw_name, dumpuser, dumpuser, pw->pw_name);
288 displayunit = getconf_str(CNF_DISPLAYUNIT);
289 unitdivisor = getconf_unit_divisor();
292 * If both server and client side checks are being done, the server
293 * check output goes to the main output, while the client check output
294 * goes to a temporary file and is copied to the main output when done.
296 * If the output is to be mailed, the main output is also a disk file,
297 * otherwise it is stdout.
299 if(do_clientchk && (do_localchk || do_tapechk)) {
300 /* we need the temp file */
301 tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
302 if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
303 error(_("could not open temporary amcheck output file %s: %s. Check permissions"), tempfname, strerror(errno));
306 unlink(tempfname); /* so it goes away on close */
311 /* the main fd is a file too */
312 mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
313 if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
314 error(_("could not open amcheck server output file %s: %s. Check permissions"), mainfname, strerror(errno));
317 unlink(mainfname); /* so it goes away on close */
321 /* just use stdout */
324 /* start server side checks */
326 if(do_localchk || do_tapechk)
327 serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
331 /* start client side checks */
334 clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
339 /* wait for child processes and note any problems */
342 if((pid = wait(&retstat)) == -1) {
343 if(errno == EINTR) continue;
345 } else if(pid == clientchk_pid) {
346 client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
348 } else if(pid == serverchk_pid) {
349 server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
352 char *wait_msg = NULL;
354 wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid);
355 if (fullwrite(mainfd, wait_msg, strlen(wait_msg)) < 0) {
356 error(_("write main file: %s"), strerror(errno));
363 /* copy temp output to main output and write tagline */
365 if(do_clientchk && (do_localchk || do_tapechk)) {
366 if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) {
367 error(_("seek temp file: %s"), strerror(errno));
371 while((size = fullread(tempfd, buffer, SIZEOF(buffer))) > 0) {
372 if (fullwrite(mainfd, buffer, (size_t)size) < 0) {
373 error(_("write main file: %s"), strerror(errno));
378 error(_("read temp file: %s"), strerror(errno));
384 version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), version());
385 if (fullwrite(mainfd, version_string, strlen(version_string)) < 0) {
386 error(_("write main file: %s"), strerror(errno));
389 amfree(version_string);
390 amfree(our_feature_string);
391 am_release_feature_set(our_features);
394 /* send mail if requested, but only if there were problems */
397 #define MAILTO_LIMIT 10
399 if((server_probs || client_probs || alwaysmail) && mailout) {
410 char *extra_info = NULL;
415 if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
416 error(_("lseek main file: %s"), strerror(errno));
419 if(alwaysmail && !(server_probs || client_probs)) {
420 subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"),
421 getconf_str(CNF_ORG));
423 subject = vstrallocf(
424 _("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"),
425 getconf_str(CNF_ORG));
428 * Variable arg lists are hard to deal with when we do not know
429 * ourself how many args are involved. Split the address list
430 * and hope there are not more than 9 entries.
432 * Remember that split() returns the original input string in
433 * argv[0], so we have to skip over that.
435 a = (char **) alloc((MAILTO_LIMIT + 1) * SIZEOF(char *));
436 memset(a, 0, (MAILTO_LIMIT + 1) * SIZEOF(char *));
441 r = (ssize_t)split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " ");
444 if((nullfd = open("/dev/null", O_RDWR)) < 0) {
445 error("nullfd: /dev/null: %s", strerror(errno));
448 pipespawn(MAILER, STDIN_PIPE | STDERR_PIPE,
449 &mailfd, &nullfd, &errfd,
452 a[1], a[2], a[3], a[4],
453 a[5], a[6], a[7], a[8], a[9],
457 * There is the potential for a deadlock here since we are writing
458 * to the process and then reading stderr, but in the normal case,
459 * nothing should be coming back to us, and hopefully in error
460 * cases, the pipe will break and we will exit out of the loop.
462 signal(SIGPIPE, SIG_IGN);
463 while((r = fullread(mainfd, buffer, SIZEOF(buffer))) > 0) {
464 if((w = fullwrite(mailfd, buffer, (size_t)r)) != (ssize_t)r) {
465 if(w < 0 && errno == EPIPE) {
466 strappend(extra_info, _("EPIPE writing to mail process\n"));
469 error(_("mailfd write: %s"), strerror(errno));
472 error(_("mailfd write: wrote %zd instead of %zd"), w, r);
478 ferr = fdopen(errfd, "r");
480 error(_("Can't fdopen: %s"), strerror(errno));
483 for(; (line = agets(ferr)) != NULL; free(line)) {
486 strappend(extra_info, line);
487 strappend(extra_info, "\n");
492 while (wait(&retstat) != -1) {
493 if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) {
494 char *mailer_error = str_exit_status("mailer", retstat);
495 strappend(err, mailer_error);
496 amfree(mailer_error);
503 fputs(extra_info, stderr);
506 error(_("error running mailer %s: %s"), MAILER, err?err:"(unknown)");
512 return (server_probs || client_probs);
515 /* --------------------------------------------------- */
517 static char *datestamp;
518 static FILE *errf = NULL;
528 if (stat(tapefile, &statbuf) == 0) {
529 if (!S_ISREG(statbuf.st_mode)) {
530 quoted = quote_string(tapefile);
531 g_fprintf(outf, _("ERROR: tapelist %s: should be a regular file.\n"),
535 } else if (access(tapefile, F_OK) != 0) {
536 quoted = quote_string(tapefile);
537 g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted);
540 } else if (access(tapefile, W_OK) != 0) {
541 quoted = quote_string(tapefile);
542 g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted);
562 pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL);
563 quoted = quote_string(pgm);
564 if(stat(pgm, &statbuf) == -1) {
565 g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
568 } else if (!S_ISREG(statbuf.st_mode)) {
569 g_fprintf(outf, _("ERROR: program %s: not a file\n"),
572 } else if (access(pgm, X_OK) == -1) {
573 g_fprintf(outf, _("ERROR: program %s: not executable\n"),
576 #ifndef SINGLE_USERID
579 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
580 g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"),
584 /* Quiet unused parameter warnings */
587 #endif /* SINGLE_USERID */
594 /* check that the tape is a valid amanda tape
595 Returns TRUE if all tests passed; FALSE otherwise. */
596 static gboolean test_tape_status(FILE * outf) {
599 GValue property_value;
601 char * tapename = NULL;
602 ReadLabelStatusFlags label_status;
604 bzero(&property_value, sizeof(property_value));
606 tapename = getconf_str(CNF_TAPEDEV);
607 g_return_val_if_fail(tapename != NULL, FALSE);
611 if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
613 _("WARNING: if a tape changer is not available, runtapes "
614 "must be set to 1\n"));
615 g_fprintf(outf, _("Change the value of the \"runtapes\" parameter in "
616 "amanda.conf or configure a tape changer\n"));
619 tape_status = taper_scan(NULL, &label, &datestamp, &tapename, NULL,
620 FILE_taperscan_output_callback, outf,
622 if (tape_status < 0) {
623 tape_t *exptape = lookup_last_reusable_tape(0);
624 g_fprintf(outf, _(" (expecting "));
625 if(exptape != NULL) g_fprintf(outf, _("tape %s or "), exptape->label);
626 g_fprintf(outf, _("a new tape)\n"));
631 device = device_open(tapename);
633 if (device == NULL) {
634 g_fprintf(outf, "ERROR: Could not open tape device.\n");
639 device_set_startup_properties_from_config(device);
640 label_status = device_read_label(device);
642 if (tape_status == 3 &&
643 !(label_status & READ_LABEL_STATUS_VOLUME_UNLABELED)) {
644 if (label_status == READ_LABEL_STATUS_SUCCESS) {
645 g_fprintf(outf, "WARNING: Volume was unlabeled, but now "
646 "is labeled \"%s\".\n", device->volume_label);
648 } else if (label_status != READ_LABEL_STATUS_SUCCESS && tape_status != 3) {
650 g_english_strjoinv_and_free
651 (g_flags_nick_to_strv(label_status &
652 (~READ_LABEL_STATUS_VOLUME_UNLABELED),
653 READ_LABEL_STATUS_FLAGS_TYPE), "or");
654 g_fprintf(outf, "WARNING: Reading label the second time failed: "
655 "One of %s.\n", errstr);
657 } else if (tape_status != 3 &&
658 (device->volume_label == NULL || label == NULL ||
659 strcmp(device->volume_label, label) != 0)) {
660 g_fprintf(outf, "WARNING: Label mismatch on re-read: "
661 "Got %s first, then %s.\n", label, device->volume_label);
664 /* If we can't get this property, it's not an error. Maybe the device
665 * doesn't support this property, or needs an actual volume to know
667 if (device_property_get(device, PROPERTY_MEDIUM_TYPE, &property_value)) {
668 g_assert(G_VALUE_TYPE(&property_value) == MEDIA_ACCESS_MODE_TYPE);
669 if (g_value_get_enum(&property_value) ==
670 MEDIA_ACCESS_MODE_WRITE_ONLY) {
671 g_fprintf(outf, "WARNING: Media access mode is WRITE_ONLY, "
672 "dumps will be thrown away.\n");
677 char *timestamp = get_undef_timestamp();
678 if (!device_start(device, ACCESS_WRITE, label, timestamp)) {
679 if (tape_status == 3) {
680 g_fprintf(outf, "ERROR: Could not label brand new tape.\n");
683 "ERROR: tape %s label ok, but is not writable.\n",
688 g_object_unref(device);
690 } else { /* Write succeeded. */
691 if (tape_status != 3) {
692 g_fprintf(outf, "Tape %s is writable; rewrote label.\n", label);
694 g_fprintf(outf, "Wrote label %s to brand new tape.\n", label);
698 } else { /* !overwrite */
699 g_fprintf(outf, "NOTE: skipping tape-writable test\n");
700 if (tape_status == 3) {
702 "Found a brand new tape, will label it %s.\n",
705 g_fprintf(outf, "Tape %s label ok\n", label);
708 g_object_unref(device);
719 struct fs_usage fsusage;
722 pid_t pid G_GNUC_UNUSED;
723 int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
724 int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
725 int testtape = do_tapechk;
726 tapetype_t *tp = NULL;
731 switch(pid = fork()) {
733 error(_("could not spawn a process for checking the server: %s"), strerror(errno));
734 g_assert_not_reached();
746 set_pname("amcheck-server");
750 if((outf = fdopen(fd, "w")) == NULL) {
751 error(_("fdopen %d: %s"), fd, strerror(errno));
756 g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
757 g_fprintf(outf, "-----------------------------\n");
759 if (do_localchk || testtape) {
760 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
764 * Check various server side config file settings.
771 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
772 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
773 g_fprintf(outf, _("ERROR: %s\n"), errstr);
777 lbl_templ = tapetype_get_lbl_templ(tp);
778 if(strcmp(lbl_templ, "") != 0) {
779 lbl_templ = config_dir_relative(lbl_templ);
780 if(access(lbl_templ, R_OK) == -1) {
782 _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
788 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"));
793 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
794 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) {
795 g_fprintf(outf, _("WARNING: flush_threshold_dumped (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
796 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED),
797 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
800 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
801 getconf_int(CNF_TAPERFLUSH)) {
802 g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
803 getconf_int(CNF_TAPERFLUSH),
804 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
807 if (getconf_int(CNF_TAPERFLUSH) > 0 &&
808 !getconf_boolean(CNF_AUTOFLUSH)) {
809 g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' if taperflush (%d) is greater that 0.\n"),
810 getconf_int(CNF_TAPERFLUSH));
813 /* Double-check that 'localhost' resolves properly */
814 if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) {
815 g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
821 * Look up the programs used on the server side.
825 * entreprise version will do planner/dumper suid check
827 if(access(amlibexecdir, X_OK) == -1) {
828 quoted = quote_string(amlibexecdir);
829 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
831 g_fprintf(outf, _("Check permissions\n"));
835 if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
837 if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
839 if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
841 if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
843 if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
845 if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
848 if(access(sbindir, X_OK) == -1) {
849 quoted = quote_string(sbindir);
850 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
852 g_fprintf(outf, _("Check permissions\n"));
856 if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
858 if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
860 if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
862 if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
865 if(access(COMPRESS_PATH, X_OK) == -1) {
866 quoted = quote_string(COMPRESS_PATH);
867 g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
868 "and indexing will not work. \n"),quoted);
869 g_fprintf(outf, _("Check permissions\n"));
875 * Check that the directory for the tapelist file is writable, as well
876 * as the tapelist file itself (if it already exists). Also, check for
877 * a "hold" file (just because it is convenient to do it here) and warn
878 * if tapedev is set to the null device.
881 if(do_localchk || do_tapechk) {
890 tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
892 * XXX There Really Ought to be some error-checking here... dhw
894 tape_dir = stralloc(tapefile);
895 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
898 * else whine Really Loudly about a path with no slashes??!?
901 if(access(tape_dir, W_OK) == -1) {
902 quoted = quote_string(tape_dir);
903 g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"),
908 else if(stat(tapefile, &statbuf) == -1) {
909 quoted = quote_string(tape_dir);
910 g_fprintf(outf, _("ERROR: tapelist %s (%s), "
911 "you must create an empty file.\n"),
912 quoted, strerror(errno));
917 tapebad |= check_tapefile(outf, tapefile);
918 if (tapebad == 0 && read_tapelist(tapefile)) {
919 quoted = quote_string(tapefile);
920 g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
924 newtapefile = stralloc2(tapefile, ".new");
925 tapebad |= check_tapefile(outf, newtapefile);
927 newtapefile = stralloc2(tapefile, ".amlabel");
928 tapebad |= check_tapefile(outf, newtapefile);
930 newtapefile = stralloc2(tapefile, ".amlabel.new");
931 tapebad |= check_tapefile(outf, newtapefile);
933 newtapefile = stralloc2(tapefile, ".yesterday");
934 tapebad |= check_tapefile(outf, newtapefile);
936 newtapefile = stralloc2(tapefile, ".yesterday.new");
937 tapebad |= check_tapefile(outf, newtapefile);
940 holdfile = config_dir_relative("hold");
941 if(access(holdfile, F_OK) != -1) {
942 quoted = quote_string(holdfile);
943 g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
944 g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
945 g_fprintf(outf, _("You might want to delete the existing hold file\n"));
951 tapename = getconf_str(CNF_TAPEDEV);
952 if (tapename == NULL) {
953 if (getconf_str(CNF_TPCHANGER) == NULL) {
954 g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
961 /* check available disk space */
964 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = holdingdisk_next(hdp)) {
965 quoted = quote_string(holdingdisk_get_diskdir(hdp));
966 if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
967 g_fprintf(outf, _("ERROR: holding dir %s (%s), "
968 "you must create a directory.\n"),
969 quoted, strerror(errno));
975 /* do the division first to avoid potential integer overflow */
976 if (fsusage.fsu_bavail_top_bit_set)
979 kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
981 if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
982 g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
983 quoted, strerror(errno));
984 g_fprintf(outf, _("Check permissions\n"));
987 else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
988 g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
989 quoted, strerror(errno));
990 g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
993 else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
996 _("WARNING: holding disk %s: "
997 "no space available (%lld %sB requested)\n"), quoted,
998 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1002 else if(kb_avail < holdingdisk_get_disksize(hdp)) {
1004 _("WARNING: holding disk %s: "
1005 "only %lld %sB available (%lld %sB requested)\n"), quoted,
1006 (long long)(kb_avail / (off_t)unitdivisor),
1008 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1014 _("Holding disk %s: %lld %sB disk space available,"
1015 " using %lld %sB as requested\n"),
1017 (long long)(kb_avail / (off_t)unitdivisor),
1019 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1024 if(kb_avail < -holdingdisk_get_disksize(hdp)) {
1026 _("WARNING: holding disk %s: "
1027 "only %lld %sB free, using nothing\n"),
1028 quoted, (long long)(kb_avail / (off_t)unitdivisor),
1030 g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
1035 _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
1037 (long long)(kb_avail/(off_t)unitdivisor),
1039 (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
1047 /* check that the log file is writable if it already exists */
1053 struct stat stat_old;
1054 struct stat statbuf;
1056 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
1057 logfile = vstralloc(conf_logdir, "/log", NULL);
1059 quoted = quote_string(conf_logdir);
1060 if(stat(conf_logdir, &statbuf) == -1) {
1061 g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
1062 quoted, strerror(errno));
1065 else if(access(conf_logdir, W_OK) == -1) {
1066 g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
1071 if(access(logfile, F_OK) == 0) {
1074 if(access(logfile, W_OK) != 0) {
1075 quoted = quote_string(logfile);
1076 g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
1081 olddir = vstralloc(conf_logdir, "/oldlog", NULL);
1082 quoted = quote_string(olddir);
1083 if (stat(olddir,&stat_old) == 0) { /* oldlog exist */
1084 if(!(S_ISDIR(stat_old.st_mode))) {
1085 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1087 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1090 if(access(olddir, W_OK) == -1) {
1091 g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
1092 g_fprintf(outf, _("Check permissions\n"));
1096 else if(lstat(olddir,&stat_old) == 0) {
1097 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1099 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1105 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
1106 if (access(logfile, F_OK) == 0) {
1114 amfree(conf_logdir);
1118 tapebad = !test_tape_status(outf);
1119 } else if (do_tapechk) {
1120 g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
1121 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1122 } else if (logbad == 2) {
1123 g_fprintf(outf, _("WARNING: amdump or amflush seem to be running\n"));
1124 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1126 g_fprintf(outf, _("NOTE: skipping tape checks\n"));
1130 * See if the information file and index directory for each client
1131 * and disk is OK. Since we may be seeing clients and/or disks for
1132 * the first time, these are just warnings, not errors.
1135 char *conf_infofile;
1136 char *conf_indexdir;
1137 char *hostinfodir = NULL;
1138 char *hostindexdir = NULL;
1139 char *diskdir = NULL;
1140 char *infofile = NULL;
1141 struct stat statbuf;
1144 int indexdir_checked = 0;
1145 int hostindexdir_checked = 0;
1148 int conf_tapecycle, conf_runspercycle;
1150 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1151 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1153 if(conf_tapecycle <= conf_runspercycle) {
1154 g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
1155 conf_tapecycle, conf_runspercycle);
1158 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
1159 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
1161 quoted = quote_string(conf_infofile);
1162 if(stat(conf_infofile, &statbuf) == -1) {
1163 if (errno == ENOENT) {
1164 g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
1166 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1168 g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
1169 quoted, strerror(errno));
1172 amfree(conf_infofile);
1173 } else if (!S_ISDIR(statbuf.st_mode)) {
1174 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
1175 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1176 amfree(conf_infofile);
1178 } else if (access(conf_infofile, W_OK) == -1) {
1179 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1180 g_fprintf(outf, _("Check permissions\n"));
1181 amfree(conf_infofile);
1184 char *errmsg = NULL;
1185 if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1186 g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1190 strappend(conf_infofile, "/");
1194 while(!empty(origq)) {
1195 hostp = origq.head->host;
1196 host = sanitise_filename(hostp->hostname);
1198 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1199 quoted = quote_string(hostinfodir);
1200 if(stat(hostinfodir, &statbuf) == -1) {
1201 if (errno == ENOENT) {
1202 g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
1205 _("NOTE: it will be created on the next run.\n"));
1207 g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
1208 quoted, strerror(errno));
1211 amfree(hostinfodir);
1212 } else if (!S_ISDIR(statbuf.st_mode)) {
1213 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1215 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1216 amfree(hostinfodir);
1218 } else if (access(hostinfodir, W_OK) == -1) {
1219 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1220 g_fprintf(outf, _("Check permissions\n"));
1221 amfree(hostinfodir);
1224 strappend(hostinfodir, "/");
1228 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1229 disk = sanitise_filename(dp->name);
1233 diskdir = newstralloc2(diskdir, hostinfodir, disk);
1234 infofile = vstralloc(diskdir, "/", "info", NULL);
1235 quoted = quote_string(diskdir);
1236 quotedif = quote_string(infofile);
1237 if(stat(diskdir, &statbuf) == -1) {
1238 if (errno == ENOENT) {
1239 g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
1242 _("NOTE: it will be created on the next run.\n"));
1244 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1245 quoted, strerror(errno));
1248 } else if (!S_ISDIR(statbuf.st_mode)) {
1249 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1251 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1253 } else if (access(diskdir, W_OK) == -1) {
1254 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
1256 g_fprintf(outf,_("Check permissions\n"));
1258 } else if(stat(infofile, &statbuf) == -1) {
1259 if (errno == ENOENT) {
1260 g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
1262 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1264 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1265 quoted, strerror(errno));
1268 } else if (!S_ISREG(statbuf.st_mode)) {
1269 g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
1271 g_fprintf(outf, _("Remove the entry and create a new file\n"));
1273 } else if (access(infofile, R_OK) == -1) {
1274 g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
1283 if(! indexdir_checked) {
1284 quoted = quote_string(conf_indexdir);
1285 if(stat(conf_indexdir, &statbuf) == -1) {
1286 if (errno == ENOENT) {
1287 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1289 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1291 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1292 quoted, strerror(errno));
1295 amfree(conf_indexdir);
1296 } else if (!S_ISDIR(statbuf.st_mode)) {
1297 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1299 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1300 amfree(conf_indexdir);
1302 } else if (access(conf_indexdir, W_OK) == -1) {
1303 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1305 amfree(conf_indexdir);
1308 strappend(conf_indexdir, "/");
1310 indexdir_checked = 1;
1314 if(! hostindexdir_checked) {
1315 hostindexdir = stralloc2(conf_indexdir, host);
1316 quoted = quote_string(hostindexdir);
1317 if(stat(hostindexdir, &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 amfree(hostindexdir);
1328 } else if (!S_ISDIR(statbuf.st_mode)) {
1329 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1331 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1332 amfree(hostindexdir);
1334 } else if (access(hostindexdir, W_OK) == -1) {
1335 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1337 amfree(hostindexdir);
1340 strappend(hostindexdir, "/");
1342 hostindexdir_checked = 1;
1346 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1347 quoted = quote_string(diskdir);
1348 if(stat(diskdir, &statbuf) == -1) {
1349 if (errno == ENOENT) {
1350 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1352 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1354 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1355 quoted, strerror(errno));
1358 } else if (!S_ISDIR(statbuf.st_mode)) {
1359 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1361 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1363 } else if (access(diskdir, W_OK) == -1) {
1364 g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
1373 if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1374 if ( dp->srv_encrypt[0] == '\0' ) {
1375 g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
1376 g_fprintf(outf, _("Specify \"server_custom_encrypt\" in the dumptype\n"));
1379 else if(access(dp->srv_encrypt, X_OK) == -1) {
1380 g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
1382 g_fprintf(outf, _("Check file type\n"));
1386 if ( dp->compress == COMP_SERVER_CUST ) {
1387 if ( dp->srvcompprog[0] == '\0' ) {
1388 g_fprintf(outf, _("ERROR: server custom compression program "
1389 "not specified\n"));
1390 g_fprintf(outf, _("Specify \"server_custom_compress\" in "
1394 else if(access(dp->srvcompprog, X_OK) == -1) {
1395 quoted = quote_string(dp->srvcompprog);
1397 g_fprintf(outf, _("ERROR: %s is not executable, server custom "
1398 "compression will not work\n"),
1401 g_fprintf(outf, _("Check file type\n"));
1407 remove_disk(&origq, dp);
1410 amfree(hostindexdir);
1411 hostindexdir_checked = 0;
1414 amfree(hostinfodir);
1415 amfree(conf_infofile);
1416 amfree(conf_indexdir);
1421 g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
1437 /* --------------------------------------------------- */
1442 static void handle_result(void *, pkt_t *, security_handle_t *);
1443 void start_host(am_host_t *hostp);
1445 #define HOST_READY ((void *)0) /* must be 0 */
1446 #define HOST_ACTIVE ((void *)1)
1447 #define HOST_DONE ((void *)2)
1449 #define DISK_READY ((void *)0) /* must be 0 */
1450 #define DISK_ACTIVE ((void *)1)
1451 #define DISK_DONE ((void *)2)
1461 const security_driver_t *secdrv;
1462 char number[NUM_STR_SIZE];
1464 if(hostp->up != HOST_READY) {
1468 if (strcmp(hostp->hostname,"localhost") == 0) {
1470 _("WARNING: Usage of fully qualified hostname recommended for Client %s.\n"),
1475 * The first time through here we send a "noop" request. This will
1476 * return the feature list from the client if it supports that.
1477 * If it does not, handle_result() will set the feature list to an
1478 * empty structure. In either case, we do the disks on the second
1479 * (and subsequent) pass(es).
1482 if(hostp->features != NULL) { /* selfcheck service */
1483 int has_features = am_has_feature(hostp->features,
1484 fe_req_options_features);
1485 int has_hostname = am_has_feature(hostp->features,
1486 fe_req_options_hostname);
1487 int has_maxdumps = am_has_feature(hostp->features,
1488 fe_req_options_maxdumps);
1489 int has_config = am_has_feature(hostp->features,
1490 fe_req_options_config);
1492 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1493 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1495 _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
1497 g_fprintf(outf, _("Client might be of a very old version\n"));
1499 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1501 _("ERROR: Client %s does not support selfcheck REP packet.\n"),
1503 g_fprintf(outf, _("Client might be of a very old version\n"));
1505 if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1506 !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1507 !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1509 _("ERROR: Client %s does not support sendsize REQ packet.\n"),
1511 g_fprintf(outf, _("Client might be of a very old version\n"));
1513 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1515 _("ERROR: Client %s does not support sendsize REP packet.\n"),
1517 g_fprintf(outf, _("Client might be of a very old version\n"));
1519 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1520 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1522 _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
1524 g_fprintf(outf, _("Client might be of a very old version\n"));
1526 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1528 _("ERROR: Client %s does not support sendbackup REP packet.\n"),
1530 g_fprintf(outf, _("Client might be of a very old version\n"));
1533 g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1534 req = vstralloc("SERVICE ", "selfcheck", "\n",
1536 has_features ? "features=" : "",
1537 has_features ? our_feature_string : "",
1538 has_features ? ";" : "",
1539 has_maxdumps ? "maxdumps=" : "",
1540 has_maxdumps ? number : "",
1541 has_maxdumps ? ";" : "",
1542 has_hostname ? "hostname=" : "",
1543 has_hostname ? hostp->hostname : "",
1544 has_hostname ? ";" : "",
1545 has_config ? "config=" : "",
1546 has_config ? config_name : "",
1547 has_config ? ";" : "",
1551 req_len = strlen(req);
1552 req_len += 128; /* room for SECURITY ... */
1553 req_len += 256; /* room for non-disk answers */
1554 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1562 if(dp->up != DISK_READY || dp->todo != 1) {
1565 o = optionstr(dp, hostp->features, outf);
1570 qname = quote_string(dp->name);
1571 qdevice = quote_string(dp->device);
1572 if ((dp->name && qname[0] == '"') ||
1573 (dp->device && qdevice[0] == '"')) {
1574 if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1576 _("WARNING: %s:%s:%s host does not support quoted text\n"),
1577 hostp->hostname, qname, qdevice);
1578 g_fprintf(outf, _("You must upgrade amanda on the client to "
1579 "specify a quoted text/device in the disklist, "
1580 "or don't use quoted text for the device.\n"));
1585 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1587 _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
1588 hostp->hostname, qname, dp->device);
1589 g_fprintf(outf, _("You must upgrade amanda on the client to "
1590 "specify a diskdevice in the disklist "
1591 "or don't specify a diskdevice in the disklist.\n"));
1593 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1595 _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
1596 hostp->hostname, qname, dp->device);
1597 g_fprintf(outf, _("You must upgrade amanda on the client to "
1598 "specify a diskdevice in the disklist"
1599 " or don't specify a diskdevice in the disklist.\n"));
1601 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1603 _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
1604 hostp->hostname, qname, dp->device);
1605 g_fprintf(outf, _("You must upgrade amanda on the client to "
1606 "specify a diskdevice in the disklist"
1607 " or don't specify a diskdevice in the disklist.\n"));
1610 if(strcmp(dp->program,"DUMP") == 0 ||
1611 strcmp(dp->program,"GNUTAR") == 0) {
1612 if(strcmp(dp->program, "DUMP") == 0 &&
1613 !am_has_feature(hostp->features, fe_program_dump)) {
1614 g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
1615 hostp->hostname, qname);
1616 g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
1617 "or you can use another program.\n"));
1619 if(strcmp(dp->program, "GNUTAR") == 0 &&
1620 !am_has_feature(hostp->features, fe_program_gnutar)) {
1621 g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
1622 hostp->hostname, qname);
1623 g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
1624 "or you can use another program.\n"));
1626 if(dp->estimate == ES_CALCSIZE &&
1627 !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1628 g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
1629 "estimate, using CLIENT.\n"),
1630 hostp->hostname, qname);
1631 g_fprintf(outf, _("You must upgrade amanda on the client to use "
1632 "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
1633 dp->estimate = ES_CLIENT;
1635 if(dp->estimate == ES_CALCSIZE &&
1636 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1637 calcsize = "CALCSIZE ";
1641 if(dp->compress == COMP_CUST &&
1642 !am_has_feature(hostp->features, fe_options_compress_cust)) {
1644 _("ERROR: Client %s does not support custom compression.\n"),
1646 g_fprintf(outf, _("You must upgrade amanda on the client to "
1647 "use custom compression\n"));
1648 g_fprintf(outf, _("Otherwise you can use the default client "
1649 "compression program.\n"));
1651 if(dp->encrypt == ENCRYPT_CUST ) {
1652 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1654 _("ERROR: Client %s does not support data encryption.\n"),
1656 g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
1658 } else if ( dp->compress == COMP_SERVER_FAST ||
1659 dp->compress == COMP_SERVER_BEST ||
1660 dp->compress == COMP_SERVER_CUST ) {
1662 _("ERROR: %s: Client encryption with server compression "
1663 "is not supported. See amanda.conf(5) for detail.\n"),
1669 l = vstralloc(calcsize,
1679 l = vstralloc(calcsize,
1688 if(!am_has_feature(hostp->features, fe_program_backup_api)) {
1689 g_fprintf(outf, _("ERROR: %s:%s does not support BACKUP-API.\n"),
1690 hostp->hostname, qname);
1691 g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
1692 " It is case sensitive\n"));
1695 l = vstralloc("BACKUP ",
1706 l = vstralloc("BACKUP ",
1724 dp->up = DISK_ACTIVE;
1728 else { /* noop service */
1729 req = vstralloc("SERVICE ", "noop", "\n",
1731 "features=", our_feature_string, ";",
1734 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1735 if(dp->up != DISK_READY || dp->todo != 1) {
1742 if(disk_count == 0) {
1744 hostp->up = HOST_DONE;
1748 secdrv = security_getdriver(hostp->disks->security_driver);
1749 if (secdrv == NULL) {
1750 fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
1751 hostp->disks->security_driver, hostp->hostname);
1753 protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf,
1754 req, conf_ctimeout, handle_result, hostp);
1759 hostp->up = HOST_ACTIVE;
1763 start_client_checks(
1772 switch(pid = fork()) {
1774 error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
1787 set_pname("amcheck-clients");
1791 if((outf = fdopen(fd, "w")) == NULL) {
1792 error(_("fdopen %d: %s"), fd, strerror(errno));
1797 g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
1798 g_fprintf(outf, "--------------------------------\n");
1802 hostcount = remote_errors = 0;
1804 for(dp = origq.head; dp != NULL; dp = dp->next) {
1806 if(hostp->up == HOST_READY && dp->todo == 1) {
1815 g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."),
1816 _("Client check: %d hosts checked in %s seconds."),
1818 hostcount, walltime_str(curclock()));
1819 g_fprintf(outf, plural(_(" %d problem found.\n"),
1820 _(" %d problems found.\n"), remote_errors),
1824 exit(userbad || remote_errors > 0);
1833 security_handle_t * sech)
1843 hostp = (am_host_t *)datap;
1844 hostp->up = HOST_READY;
1848 _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
1849 security_geterror(sech));
1851 hostp->up = HOST_DONE;
1856 g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
1857 hostp->hostname, pkt->body);
1864 skip_quoted_line(s, ch);
1865 if (s[-2] == '\n') {
1869 if(strncmp_const(line, "OPTIONS ") == 0) {
1871 t = strstr(line, "features=");
1872 if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1873 t += SIZEOF("features=")-1;
1874 am_release_feature_set(hostp->features);
1875 if((hostp->features = am_string_to_feature(t)) == NULL) {
1876 g_fprintf(outf, _("ERROR: %s: bad features value: %s\n"),
1877 hostp->hostname, line);
1878 g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
1885 if(strncmp_const(line, "OK ") == 0) {
1890 if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
1891 skip_whitespace(t, tch);
1893 * If the "error" is that the "noop" service is unknown, it
1894 * just means the client is "old" (does not support the service).
1895 * We can ignore this.
1897 if(!((hostp->features == NULL) && (pkt->type == P_NAK)
1898 && ((strcmp(t - 1, "unknown service: noop") == 0)
1899 || (strcmp(t - 1, "noop: invalid service") == 0)))) {
1900 g_fprintf(outf, _("ERROR: %s%s: %s\n"),
1901 (pkt->type == P_NAK) ? "NAK " : "",
1905 hostp->up = HOST_DONE;
1910 g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
1911 hostp->hostname, line);
1913 hostp->up = HOST_DONE;
1915 if(hostp->up == HOST_READY && hostp->features == NULL) {
1917 * The client does not support the features list, so give it an
1920 dbprintf(_("no feature set from host %s\n"), hostp->hostname);
1921 hostp->features = am_set_default_feature_set();
1923 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1924 if(dp->up == DISK_ACTIVE) {
1929 if(hostp->up == HOST_DONE)
1930 security_close_connection(sech, hostp->hostname);
1931 /* try to clean up any defunct processes, since Amanda doesn't wait() for
1933 while(waitpid(-1, NULL, WNOHANG)> 0);