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
45 #include "taperscan.h"
46 #include "server_util.h"
47 #include "pipespawn.h"
48 #include "amfeatures.h"
51 #include "timestamp.h"
55 #define BUFFER_SIZE 32768
57 static time_t conf_ctimeout;
60 static disklist_t origq;
62 static uid_t uid_dumpuser;
67 pid_t start_client_checks(int fd);
68 pid_t start_server_check(int fd, int do_localchk, int do_tapechk);
69 int main(int argc, char **argv);
70 int check_tapefile(FILE *outf, char *tapefile);
71 int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
76 error(_("Usage: amcheck%s [-am] [-w] [-sclt] [-M <address>] <conf> [host [disk]* ]* [-o configoption]*"), versionsuffix());
80 static am_feature_t *our_features = NULL;
81 static char *our_feature_string = NULL;
82 static char *displayunit;
83 static long int unitdivisor;
90 char buffer[BUFFER_SIZE];
92 char *mainfname = NULL;
93 char pid_str[NUM_STR_SIZE];
94 int do_clientchk, client_probs;
95 int do_localchk, do_tapechk, server_probs;
96 pid_t clientchk_pid, serverchk_pid;
97 int opt, tempfd, mainfd;
106 char *tempfname = NULL;
112 config_overwrites_t *cfg_ovr;
116 * Configure program for internationalization:
117 * 1) Only set the message locale for now.
118 * 2) Set textdomain for all amanda related programs to "amanda"
119 * We don't want to be forced to support dozens of message catalogs.
121 setlocale(LC_MESSAGES, "C");
122 textdomain("amanda");
127 set_pname("amcheck");
128 /* drop root privileges */
129 if (!set_root_privs(0)) {
130 error(_("amcheck must be run setuid root"));
133 /* Don't die when child closes pipe */
134 signal(SIGPIPE, SIG_IGN);
136 dbopen(DBG_SUBDIR_SERVER);
138 memset(buffer, 0, sizeof(buffer));
140 g_snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid());
142 erroutput_type = ERR_INTERACTIVE;
144 our_features = am_init_feature_set();
145 our_feature_string = am_feature_to_string(our_features);
149 alwaysmail = mailout = overwrite = 0;
150 do_localchk = do_tapechk = do_clientchk = 0;
151 server_probs = client_probs = 0;
152 tempfd = mainfd = -1;
154 /* process arguments */
156 cfg_ovr = new_config_overwrites(argc/2);
157 while((opt = getopt(argc, argv, "M:mawsclto:")) != EOF) {
159 case 'M': if (mailto) {
160 g_printf(_("Multiple -M options\n"));
163 mailto=stralloc(optarg);
164 if(!validate_mailto(mailto)){
165 g_printf(_("Invalid characters in mail address\n"));
176 case 's': do_localchk = do_tapechk = 1;
178 case 'c': do_clientchk = 1;
180 case 'l': do_localchk = 1;
182 case 'w': overwrite = 1;
184 case 'o': add_config_overwrite_opt(cfg_ovr, optarg);
186 case 't': do_tapechk = 1;
193 argc -= optind, argv += optind;
194 if(argc < 1) usage();
197 if ((do_localchk | do_clientchk | do_tapechk) == 0) {
198 /* Check everything if individual checks were not asked for */
199 do_localchk = do_clientchk = do_tapechk = 1;
205 config_init(CONFIG_INIT_EXPLICIT_NAME, argv[0]);
206 apply_config_overwrites(cfg_ovr);
207 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
209 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
210 read_diskfile(conf_diskfile, &origq);
211 amfree(conf_diskfile);
213 if (config_errors(NULL) >= CFGERR_WARNINGS) {
214 config_print_errors();
215 if (config_errors(NULL) >= CFGERR_ERRORS) {
216 g_critical(_("errors processing config file"));
220 mailer = getconf_str(CNF_MAILER);
221 if ((!mailer || *mailer == '\0') && mailout == 1) {
222 if (alwaysmail == 1) {
223 g_printf(_("You can't use -a because a mailer is not defined\n"));
225 g_printf(_("You can't use -m because a mailer is not defined\n"));
229 if(mailout && !mailto &&
230 (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
231 g_printf(_("\nWARNING:No mail address configured in amanda.conf.\n"));
232 g_printf(_("To receive dump results by email configure the "
233 "\"mailto\" parameter in amanda.conf\n"));
235 g_printf(_("When using -a option please specify -Maddress also\n\n"));
237 g_printf(_("Use -Maddress instead of -m\n\n"));
240 if(mailout && !mailto)
242 if(getconf_seen(CNF_MAILTO) &&
243 strlen(getconf_str(CNF_MAILTO)) > 0) {
244 if(!validate_mailto(getconf_str(CNF_MAILTO))){
245 g_printf(_("\nMail address in amanda.conf has invalid characters"));
246 g_printf(_("\nNo email will be sent\n"));
251 g_printf(_("\nNo mail address configured in amanda.conf\n"));
253 g_printf(_("When using -a option please specify -Maddress also\n\n"));
255 g_printf(_("Use -Maddress instead of -m\n\n"));
260 conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
262 errstr = match_disklist(&origq, argc-1, argv+1);
264 g_printf(_("%s"),errstr);
269 * Make sure we are running as the dump user. Don't use
270 * check_running_as(..) here, because we want to produce more
271 * verbose error messages.
273 dumpuser = getconf_str(CNF_DUMPUSER);
274 if ((pw = getpwnam(dumpuser)) == NULL) {
275 error(_("amanda.conf has dump user configured to \"%s\", but that user does not exist."), dumpuser);
278 uid_dumpuser = pw->pw_uid;
279 if ((pw = getpwuid(uid_me)) == NULL) {
280 error(_("cannot get username for running user, uid %ld is not in your user database."),
285 if (uid_me != uid_dumpuser) {
286 error(_("running as user \"%s\" instead of \"%s\".\n"
287 "Change user to \"%s\" or change dump user to \"%s\" in amanda.conf"),
288 pw->pw_name, dumpuser, dumpuser, pw->pw_name);
293 displayunit = getconf_str(CNF_DISPLAYUNIT);
294 unitdivisor = getconf_unit_divisor();
297 * If both server and client side checks are being done, the server
298 * check output goes to the main output, while the client check output
299 * goes to a temporary file and is copied to the main output when done.
301 * If the output is to be mailed, the main output is also a disk file,
302 * otherwise it is stdout.
304 if(do_clientchk && (do_localchk || do_tapechk)) {
305 /* we need the temp file */
306 tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
307 if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
308 error(_("could not open temporary amcheck output file %s: %s. Check permissions"), tempfname, strerror(errno));
311 unlink(tempfname); /* so it goes away on close */
316 /* the main fd is a file too */
317 mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
318 if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
319 error(_("could not open amcheck server output file %s: %s. Check permissions"), mainfname, strerror(errno));
322 unlink(mainfname); /* so it goes away on close */
326 /* just use stdout */
329 /* start server side checks */
331 if(do_localchk || do_tapechk)
332 serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
336 /* start client side checks */
339 clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
344 /* wait for child processes and note any problems */
347 if((pid = wait(&retstat)) == -1) {
348 if(errno == EINTR) continue;
350 } else if(pid == clientchk_pid) {
351 client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
353 } else if(pid == serverchk_pid) {
354 server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
357 char *wait_msg = NULL;
359 wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid);
360 if (full_write(mainfd, wait_msg, strlen(wait_msg)) < strlen(wait_msg)) {
361 error(_("write main file: %s"), strerror(errno));
368 /* copy temp output to main output and write tagline */
370 if(do_clientchk && (do_localchk || do_tapechk)) {
371 if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) {
372 error(_("seek temp file: %s"), strerror(errno));
376 while((size = full_read(tempfd, buffer, SIZEOF(buffer))) > 0) {
377 if (full_write(mainfd, buffer, size) < size) {
378 error(_("write main file: %s"), strerror(errno));
383 error(_("read temp file: %s"), strerror(errno));
389 version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), version());
390 if (full_write(mainfd, version_string, strlen(version_string)) < strlen(version_string)) {
391 error(_("write main file: %s"), strerror(errno));
394 amfree(version_string);
395 amfree(our_feature_string);
396 am_release_feature_set(our_features);
399 /* send mail if requested, but only if there were problems */
401 if((server_probs || client_probs || alwaysmail) && mailout) {
413 char *extra_info = NULL;
418 if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
419 error(_("lseek main file: %s"), strerror(errno));
422 if(alwaysmail && !(server_probs || client_probs)) {
423 subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"),
424 getconf_str(CNF_ORG));
426 subject = vstrallocf(
427 _("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"),
428 getconf_str(CNF_ORG));
431 a = (char **) g_new0(char *, 2);
432 a[1] = stralloc(mailto);
435 /* (note that validate_mailto doesn't allow any quotes, so this
436 * is really just splitting regular old strings) */
437 a = split_quoted_strings(getconf_str(CNF_MAILTO));
439 if((nullfd = open("/dev/null", O_RDWR)) < 0) {
440 error("nullfd: /dev/null: %s", strerror(errno));
444 /* assemble the command line for the mailer */
445 pipeargs = g_ptr_array_sized_new(4);
446 g_ptr_array_add(pipeargs, mailer);
447 g_ptr_array_add(pipeargs, "-s");
448 g_ptr_array_add(pipeargs, subject);
450 g_ptr_array_add(pipeargs, *b);
451 g_ptr_array_add(pipeargs, NULL);
453 pipespawnv(mailer, STDIN_PIPE | STDERR_PIPE, 0,
454 &mailfd, &nullfd, &errfd,
455 (char **)pipeargs->pdata);
457 g_ptr_array_free(pipeargs, FALSE);
463 * There is the potential for a deadlock here since we are writing
464 * to the process and then reading stderr, but in the normal case,
465 * nothing should be coming back to us, and hopefully in error
466 * cases, the pipe will break and we will exit out of the loop.
468 signal(SIGPIPE, SIG_IGN);
469 while((r = full_read(mainfd, buffer, SIZEOF(buffer))) > 0) {
470 if((w = full_write(mailfd, buffer, r)) != r) {
472 strappend(extra_info, _("EPIPE writing to mail process\n"));
474 } else if(errno != 0) {
475 error(_("mailfd write: %s"), strerror(errno));
478 error(_("mailfd write: wrote %zd instead of %zd"), w, r);
484 ferr = fdopen(errfd, "r");
486 error(_("Can't fdopen: %s"), strerror(errno));
489 for(; (line = agets(ferr)) != NULL; free(line)) {
492 strappend(extra_info, line);
493 strappend(extra_info, "\n");
498 while (wait(&retstat) != -1) {
499 if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) {
500 char *mailer_error = str_exit_status("mailer", retstat);
501 strappend(err, mailer_error);
502 amfree(mailer_error);
509 fputs(extra_info, stderr);
512 error(_("error running mailer %s: %s"), mailer, err?err:"(unknown)");
518 return (server_probs || client_probs);
521 /* --------------------------------------------------- */
523 static char *datestamp;
524 static FILE *errf = NULL;
534 if (stat(tapefile, &statbuf) == 0) {
535 if (!S_ISREG(statbuf.st_mode)) {
536 quoted = quote_string(tapefile);
537 g_fprintf(outf, _("ERROR: tapelist %s: should be a regular file.\n"),
541 } else if (access(tapefile, F_OK) != 0) {
542 quoted = quote_string(tapefile);
543 g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted);
546 } else if (access(tapefile, W_OK) != 0) {
547 quoted = quote_string(tapefile);
548 g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted);
568 pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL);
569 quoted = quote_string(pgm);
570 if(stat(pgm, &statbuf) == -1) {
571 g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
574 } else if (!S_ISREG(statbuf.st_mode)) {
575 g_fprintf(outf, _("ERROR: program %s: not a file\n"),
578 } else if (access(pgm, X_OK) == -1) {
579 g_fprintf(outf, _("ERROR: program %s: not executable\n"),
582 #ifndef SINGLE_USERID
585 && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
586 g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"),
590 /* Quiet unused parameter warnings */
593 #endif /* SINGLE_USERID */
600 /* check that the tape is a valid amanda tape
601 Returns TRUE if all tests passed; FALSE otherwise. */
602 static gboolean test_tape_status(FILE * outf) {
605 GValue property_value;
607 char * tapename = NULL;
608 DeviceStatusFlags device_status;
610 bzero(&property_value, sizeof(property_value));
612 tapename = getconf_str(CNF_TAPEDEV);
613 g_return_val_if_fail(tapename != NULL, FALSE);
617 if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
619 _("WARNING: if a tape changer is not available, runtapes "
620 "must be set to 1\n"));
621 g_fprintf(outf, _("Change the value of the \"runtapes\" parameter in "
622 "amanda.conf or configure a tape changer\n"));
625 tape_status = taper_scan(NULL, &label, &datestamp, &tapename, NULL,
626 FILE_taperscan_output_callback, outf,
628 if (tape_status < 0) {
629 tape_t *exptape = lookup_last_reusable_tape(0);
630 g_fprintf(outf, _(" (expecting "));
631 if(exptape != NULL) g_fprintf(outf, _("tape %s or "), exptape->label);
632 g_fprintf(outf, _("a new tape)\n"));
637 device = device_open(tapename);
638 g_assert(device != NULL);
640 if (device->status != DEVICE_STATUS_SUCCESS) {
641 g_fprintf(outf, "ERROR: Could not open tape device: %s.\n",
642 device_error(device));
647 if (!device_configure(device, TRUE)) {
648 g_fprintf(outf, "ERROR: Could not configure device: %s.\n",
649 device_error_or_status(device));
654 device_status = device_read_label(device);
656 if (tape_status == 3 &&
657 !(device_status & DEVICE_STATUS_VOLUME_UNLABELED)) {
658 if (device_status == DEVICE_STATUS_SUCCESS) {
659 g_fprintf(outf, "WARNING: Volume was unlabeled, but now "
660 "is labeled \"%s\".\n", device->volume_label);
662 } else if (device_status != DEVICE_STATUS_SUCCESS && tape_status != 3) {
664 _("WARNING: Reading label the second time failed: %s.\n"),
665 device_error_or_status(device));
666 } else if (tape_status != 3 &&
667 (device->volume_label == NULL || label == NULL ||
668 strcmp(device->volume_label, label) != 0)) {
669 g_fprintf(outf, "WARNING: Label mismatch on re-read: "
670 "Got %s first, then %s.\n", label, device->volume_label);
673 /* If we can't get this property, it's not an error. Maybe the device
674 * doesn't support this property, or needs an actual volume to know
676 if (device_property_get(device, PROPERTY_MEDIUM_ACCESS_TYPE, &property_value)) {
677 g_assert(G_VALUE_TYPE(&property_value) == MEDIA_ACCESS_MODE_TYPE);
678 if (g_value_get_enum(&property_value) ==
679 MEDIA_ACCESS_MODE_WRITE_ONLY) {
680 g_fprintf(outf, "WARNING: Media access mode is WRITE_ONLY; "
681 "dumps may not be recoverable.\n");
686 char *timestamp = get_undef_timestamp();
687 if (!device_start(device, ACCESS_WRITE, label, timestamp)) {
688 if (tape_status == 3) {
689 g_fprintf(outf, "ERROR: Could not label brand new tape");
692 "ERROR: tape %s label ok, but is not writable",
695 g_fprintf(outf, ": %s.\n", device_error(device));
698 g_object_unref(device);
700 } else { /* Write succeeded. */
701 if (tape_status != 3) {
702 g_fprintf(outf, "Tape %s is writable; rewrote label.\n", label);
704 g_fprintf(outf, "Wrote label %s to brand new tape.\n", label);
708 } else { /* !overwrite */
709 g_fprintf(outf, "NOTE: skipping tape-writable test\n");
710 if (tape_status == 3) {
712 "Found a brand new tape, will label it %s.\n",
715 g_fprintf(outf, "Tape %s label ok\n", label);
718 g_object_unref(device);
729 struct fs_usage fsusage;
732 pid_t pid G_GNUC_UNUSED;
733 int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
734 int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
735 int testtape = do_tapechk;
736 tapetype_t *tp = NULL;
742 switch(pid = fork()) {
744 error(_("could not spawn a process for checking the server: %s"), strerror(errno));
745 g_assert_not_reached();
757 set_pname("amcheck-server");
761 if((outf = fdopen(fd, "w")) == NULL) {
762 error(_("fdopen %d: %s"), fd, strerror(errno));
767 g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
768 g_fprintf(outf, "-----------------------------\n");
770 if (do_localchk || testtape) {
771 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
775 * Check various server side config file settings.
782 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
783 if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
784 g_fprintf(outf, _("ERROR: %s\n"), errstr);
788 lbl_templ = tapetype_get_lbl_templ(tp);
789 if(strcmp(lbl_templ, "") != 0) {
790 lbl_templ = config_dir_relative(lbl_templ);
791 if(access(lbl_templ, R_OK) == -1) {
793 _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
799 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"));
804 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
805 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) {
806 g_fprintf(outf, _("WARNING: flush_threshold_dumped (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
807 getconf_int(CNF_FLUSH_THRESHOLD_DUMPED),
808 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
811 if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
812 getconf_int(CNF_TAPERFLUSH)) {
813 g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush_threshold_scheduled (%d).\n"),
814 getconf_int(CNF_TAPERFLUSH),
815 getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
818 if (getconf_int(CNF_TAPERFLUSH) > 0 &&
819 !getconf_boolean(CNF_AUTOFLUSH)) {
820 g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' if taperflush (%d) is greater that 0.\n"),
821 getconf_int(CNF_TAPERFLUSH));
824 /* Double-check that 'localhost' resolves properly */
825 if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) {
826 g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
832 * Look up the programs used on the server side.
836 * entreprise version will do planner/dumper suid check
838 if(access(amlibexecdir, X_OK) == -1) {
839 quoted = quote_string(amlibexecdir);
840 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
842 g_fprintf(outf, _("Check permissions\n"));
846 if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
848 if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
850 if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
852 if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
854 if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
856 if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
859 if(access(sbindir, X_OK) == -1) {
860 quoted = quote_string(sbindir);
861 g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
863 g_fprintf(outf, _("Check permissions\n"));
867 if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
869 if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
871 if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
873 if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
876 if(access(COMPRESS_PATH, X_OK) == -1) {
877 quoted = quote_string(COMPRESS_PATH);
878 g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
879 "and indexing will not work. \n"),quoted);
880 g_fprintf(outf, _("Check permissions\n"));
886 * Check that the directory for the tapelist file is writable, as well
887 * as the tapelist file itself (if it already exists). Also, check for
888 * a "hold" file (just because it is convenient to do it here) and warn
889 * if tapedev is set to the null device.
892 if(do_localchk || do_tapechk) {
901 tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
903 * XXX There Really Ought to be some error-checking here... dhw
905 tape_dir = stralloc(tapefile);
906 if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
909 * else whine Really Loudly about a path with no slashes??!?
912 if(access(tape_dir, W_OK) == -1) {
913 quoted = quote_string(tape_dir);
914 g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"),
919 else if(stat(tapefile, &statbuf) == -1) {
920 if (errno != ENOENT) {
921 quoted = quote_string(tape_dir);
922 g_fprintf(outf, _("ERROR: tapelist %s (%s), "
923 "you must create an empty file.\n"),
924 quoted, strerror(errno));
928 g_fprintf(outf, _("NOTE: tapelist will be created on the next run.\n"));
931 tapebad |= check_tapefile(outf, tapefile);
932 if (tapebad == 0 && read_tapelist(tapefile)) {
933 quoted = quote_string(tapefile);
934 g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
938 newtapefile = stralloc2(tapefile, ".new");
939 tapebad |= check_tapefile(outf, newtapefile);
941 newtapefile = stralloc2(tapefile, ".amlabel");
942 tapebad |= check_tapefile(outf, newtapefile);
944 newtapefile = stralloc2(tapefile, ".amlabel.new");
945 tapebad |= check_tapefile(outf, newtapefile);
947 newtapefile = stralloc2(tapefile, ".yesterday");
948 tapebad |= check_tapefile(outf, newtapefile);
950 newtapefile = stralloc2(tapefile, ".yesterday.new");
951 tapebad |= check_tapefile(outf, newtapefile);
954 holdfile = config_dir_relative("hold");
955 if(access(holdfile, F_OK) != -1) {
956 quoted = quote_string(holdfile);
957 g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
958 g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
959 g_fprintf(outf, _("You might want to delete the existing hold file\n"));
965 tapename = getconf_str(CNF_TAPEDEV);
966 if (tapename == NULL) {
967 if (getconf_str(CNF_TPCHANGER) == NULL) {
968 g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
975 /* check available disk space */
978 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = holdingdisk_next(hdp)) {
979 quoted = quote_string(holdingdisk_get_diskdir(hdp));
980 if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
981 g_fprintf(outf, _("ERROR: holding dir %s (%s), "
982 "you must create a directory.\n"),
983 quoted, strerror(errno));
989 /* do the division first to avoid potential integer overflow */
990 if (fsusage.fsu_bavail_top_bit_set)
993 kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
995 if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
996 g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
997 quoted, strerror(errno));
998 g_fprintf(outf, _("Check permissions\n"));
1001 else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
1002 g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
1003 quoted, strerror(errno));
1004 g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
1007 else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
1010 _("WARNING: holding disk %s: "
1011 "no space available (%lld %sB requested)\n"), quoted,
1012 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1016 else if(kb_avail < holdingdisk_get_disksize(hdp)) {
1018 _("WARNING: holding disk %s: "
1019 "only %lld %sB available (%lld %sB requested)\n"), quoted,
1020 (long long)(kb_avail / (off_t)unitdivisor),
1022 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1028 _("Holding disk %s: %lld %sB disk space available,"
1029 " using %lld %sB as requested\n"),
1031 (long long)(kb_avail / (off_t)unitdivisor),
1033 (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1038 if(kb_avail < -holdingdisk_get_disksize(hdp)) {
1040 _("WARNING: holding disk %s: "
1041 "only %lld %sB free, using nothing\n"),
1042 quoted, (long long)(kb_avail / (off_t)unitdivisor),
1044 g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
1049 _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
1051 (long long)(kb_avail/(off_t)unitdivisor),
1053 (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
1061 /* check that the log file is writable if it already exists */
1067 struct stat stat_old;
1068 struct stat statbuf;
1070 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
1071 logfile = vstralloc(conf_logdir, "/log", NULL);
1073 quoted = quote_string(conf_logdir);
1074 if(stat(conf_logdir, &statbuf) == -1) {
1075 g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
1076 quoted, strerror(errno));
1079 else if(access(conf_logdir, W_OK) == -1) {
1080 g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
1085 if(access(logfile, F_OK) == 0) {
1088 if(access(logfile, W_OK) != 0) {
1089 quoted = quote_string(logfile);
1090 g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
1095 olddir = vstralloc(conf_logdir, "/oldlog", NULL);
1096 quoted = quote_string(olddir);
1097 if (stat(olddir,&stat_old) == 0) { /* oldlog exist */
1098 if(!(S_ISDIR(stat_old.st_mode))) {
1099 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1101 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1104 if(access(olddir, W_OK) == -1) {
1105 g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
1106 g_fprintf(outf, _("Check permissions\n"));
1110 else if(lstat(olddir,&stat_old) == 0) {
1111 g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1113 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1119 logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
1120 if (access(logfile, F_OK) == 0) {
1128 amfree(conf_logdir);
1132 tapebad = !test_tape_status(outf);
1133 } else if (do_tapechk) {
1134 g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
1135 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1136 } else if (logbad == 2) {
1137 g_fprintf(outf, _("WARNING: amdump or amflush seem to be running\n"));
1138 g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1140 g_fprintf(outf, _("NOTE: skipping tape checks\n"));
1144 * See if the information file and index directory for each client
1145 * and disk is OK. Since we may be seeing clients and/or disks for
1146 * the first time, these are just warnings, not errors.
1149 char *conf_infofile;
1150 char *conf_indexdir;
1151 char *hostinfodir = NULL;
1152 char *hostindexdir = NULL;
1153 char *diskdir = NULL;
1154 char *infofile = NULL;
1155 struct stat statbuf;
1158 int indexdir_checked = 0;
1159 int hostindexdir_checked = 0;
1162 int conf_tapecycle, conf_runspercycle;
1164 conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1165 conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1167 if(conf_tapecycle <= conf_runspercycle) {
1168 g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
1169 conf_tapecycle, conf_runspercycle);
1172 conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
1173 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
1175 quoted = quote_string(conf_infofile);
1176 if(stat(conf_infofile, &statbuf) == -1) {
1177 if (errno == ENOENT) {
1178 g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
1180 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1182 g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
1183 quoted, strerror(errno));
1186 amfree(conf_infofile);
1187 } else if (!S_ISDIR(statbuf.st_mode)) {
1188 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
1189 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1190 amfree(conf_infofile);
1192 } else if (access(conf_infofile, W_OK) == -1) {
1193 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1194 g_fprintf(outf, _("Check permissions\n"));
1195 amfree(conf_infofile);
1198 char *errmsg = NULL;
1199 if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1200 g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1204 strappend(conf_infofile, "/");
1208 while(!empty(origq)) {
1209 hostp = origq.head->host;
1210 host = sanitise_filename(hostp->hostname);
1212 hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1213 quoted = quote_string(hostinfodir);
1214 if(stat(hostinfodir, &statbuf) == -1) {
1215 if (errno == ENOENT) {
1216 g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
1219 _("NOTE: it will be created on the next run.\n"));
1221 g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
1222 quoted, strerror(errno));
1225 amfree(hostinfodir);
1226 } else if (!S_ISDIR(statbuf.st_mode)) {
1227 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1229 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1230 amfree(hostinfodir);
1232 } else if (access(hostinfodir, W_OK) == -1) {
1233 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1234 g_fprintf(outf, _("Check permissions\n"));
1235 amfree(hostinfodir);
1238 strappend(hostinfodir, "/");
1242 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1243 disk = sanitise_filename(dp->name);
1247 diskdir = newstralloc2(diskdir, hostinfodir, disk);
1248 infofile = vstralloc(diskdir, "/", "info", NULL);
1249 quoted = quote_string(diskdir);
1250 quotedif = quote_string(infofile);
1251 if(stat(diskdir, &statbuf) == -1) {
1252 if (errno == ENOENT) {
1253 g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
1256 _("NOTE: it will be created on the next run.\n"));
1258 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1259 quoted, strerror(errno));
1262 } else if (!S_ISDIR(statbuf.st_mode)) {
1263 g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1265 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1267 } else if (access(diskdir, W_OK) == -1) {
1268 g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
1270 g_fprintf(outf,_("Check permissions\n"));
1272 } else if(stat(infofile, &statbuf) == -1) {
1273 if (errno == ENOENT) {
1274 g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
1276 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1278 g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1279 quoted, strerror(errno));
1282 } else if (!S_ISREG(statbuf.st_mode)) {
1283 g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
1285 g_fprintf(outf, _("Remove the entry and create a new file\n"));
1287 } else if (access(infofile, R_OK) == -1) {
1288 g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
1297 if(! indexdir_checked) {
1298 quoted = quote_string(conf_indexdir);
1299 if(stat(conf_indexdir, &statbuf) == -1) {
1300 if (errno == ENOENT) {
1301 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1303 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1305 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1306 quoted, strerror(errno));
1309 amfree(conf_indexdir);
1310 } else if (!S_ISDIR(statbuf.st_mode)) {
1311 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1313 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1314 amfree(conf_indexdir);
1316 } else if (access(conf_indexdir, W_OK) == -1) {
1317 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1319 amfree(conf_indexdir);
1322 strappend(conf_indexdir, "/");
1324 indexdir_checked = 1;
1328 if(! hostindexdir_checked) {
1329 hostindexdir = stralloc2(conf_indexdir, host);
1330 quoted = quote_string(hostindexdir);
1331 if(stat(hostindexdir, &statbuf) == -1) {
1332 if (errno == ENOENT) {
1333 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1335 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1337 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1338 quoted, strerror(errno));
1341 amfree(hostindexdir);
1342 } else if (!S_ISDIR(statbuf.st_mode)) {
1343 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1345 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1346 amfree(hostindexdir);
1348 } else if (access(hostindexdir, W_OK) == -1) {
1349 g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1351 amfree(hostindexdir);
1354 strappend(hostindexdir, "/");
1356 hostindexdir_checked = 1;
1360 diskdir = newstralloc2(diskdir, hostindexdir, disk);
1361 quoted = quote_string(diskdir);
1362 if(stat(diskdir, &statbuf) == -1) {
1363 if (errno == ENOENT) {
1364 g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1366 g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1368 g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1369 quoted, strerror(errno));
1372 } else if (!S_ISDIR(statbuf.st_mode)) {
1373 g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1375 g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1377 } else if (access(diskdir, W_OK) == -1) {
1378 g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
1387 if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1388 if ( dp->srv_encrypt[0] == '\0' ) {
1389 g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
1390 g_fprintf(outf, _("Specify \"server_custom_encrypt\" in the dumptype\n"));
1393 else if(access(dp->srv_encrypt, X_OK) == -1) {
1394 g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
1396 g_fprintf(outf, _("Check file type\n"));
1400 if ( dp->compress == COMP_SERVER_CUST ) {
1401 if ( dp->srvcompprog[0] == '\0' ) {
1402 g_fprintf(outf, _("ERROR: server custom compression program "
1403 "not specified\n"));
1404 g_fprintf(outf, _("Specify \"server_custom_compress\" in "
1408 else if(access(dp->srvcompprog, X_OK) == -1) {
1409 quoted = quote_string(dp->srvcompprog);
1411 g_fprintf(outf, _("ERROR: %s is not executable, server custom "
1412 "compression will not work\n"),
1415 g_fprintf(outf, _("Check file type\n"));
1420 /* check tape_splitsize */
1421 tape_size = tapetype_get_length(tp);
1422 if (dp->tape_splitsize > tape_size) {
1424 _("ERROR: %s %s: tape_splitsize > tape size\n"),
1425 hostp->hostname, dp->name);
1428 if (dp->fallback_splitsize * 1024 > physmem_total()) {
1430 _("ERROR: %s %s: fallback_splitsize > total available memory\n"),
1431 hostp->hostname, dp->name);
1434 if (dp->fallback_splitsize > tape_size) {
1436 _("ERROR: %s %s: fallback_splitsize > tape size\n"),
1437 hostp->hostname, dp->name);
1441 remove_disk(&origq, dp);
1444 amfree(hostindexdir);
1445 hostindexdir_checked = 0;
1448 amfree(hostinfodir);
1449 amfree(conf_infofile);
1450 amfree(conf_indexdir);
1455 g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
1471 /* --------------------------------------------------- */
1476 static void handle_result(void *, pkt_t *, security_handle_t *);
1477 void start_host(am_host_t *hostp);
1479 #define HOST_READY ((void *)0) /* must be 0 */
1480 #define HOST_ACTIVE ((void *)1)
1481 #define HOST_DONE ((void *)2)
1483 #define DISK_READY ((void *)0) /* must be 0 */
1484 #define DISK_ACTIVE ((void *)1)
1485 #define DISK_DONE ((void *)2)
1495 const security_driver_t *secdrv;
1496 char number[NUM_STR_SIZE];
1498 if(hostp->up != HOST_READY) {
1502 if (strcmp(hostp->hostname,"localhost") == 0) {
1504 _("WARNING: Usage of fully qualified hostname recommended for Client %s.\n"),
1509 * The first time through here we send a "noop" request. This will
1510 * return the feature list from the client if it supports that.
1511 * If it does not, handle_result() will set the feature list to an
1512 * empty structure. In either case, we do the disks on the second
1513 * (and subsequent) pass(es).
1516 if(hostp->features != NULL) { /* selfcheck service */
1517 int has_features = am_has_feature(hostp->features,
1518 fe_req_options_features);
1519 int has_hostname = am_has_feature(hostp->features,
1520 fe_req_options_hostname);
1521 int has_maxdumps = am_has_feature(hostp->features,
1522 fe_req_options_maxdumps);
1523 int has_config = am_has_feature(hostp->features,
1524 fe_req_options_config);
1526 if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1527 !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1529 _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
1531 g_fprintf(outf, _("Client might be of a very old version\n"));
1533 if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1535 _("ERROR: Client %s does not support selfcheck REP packet.\n"),
1537 g_fprintf(outf, _("Client might be of a very old version\n"));
1539 if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1540 !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1541 !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1543 _("ERROR: Client %s does not support sendsize REQ packet.\n"),
1545 g_fprintf(outf, _("Client might be of a very old version\n"));
1547 if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1549 _("ERROR: Client %s does not support sendsize REP packet.\n"),
1551 g_fprintf(outf, _("Client might be of a very old version\n"));
1553 if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1554 !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1556 _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
1558 g_fprintf(outf, _("Client might be of a very old version\n"));
1560 if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1562 _("ERROR: Client %s does not support sendbackup REP packet.\n"),
1564 g_fprintf(outf, _("Client might be of a very old version\n"));
1567 g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1568 req = vstralloc("SERVICE ", "selfcheck", "\n",
1570 has_features ? "features=" : "",
1571 has_features ? our_feature_string : "",
1572 has_features ? ";" : "",
1573 has_maxdumps ? "maxdumps=" : "",
1574 has_maxdumps ? number : "",
1575 has_maxdumps ? ";" : "",
1576 has_hostname ? "hostname=" : "",
1577 has_hostname ? hostp->hostname : "",
1578 has_hostname ? ";" : "",
1579 has_config ? "config=" : "",
1580 has_config ? get_config_name() : "",
1581 has_config ? ";" : "",
1585 req_len = strlen(req);
1586 req_len += 128; /* room for SECURITY ... */
1587 req_len += 256; /* room for non-disk answers */
1588 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1593 char *qname, *b64disk;
1594 char *qdevice, *b64device = NULL;
1596 if(dp->up != DISK_READY || dp->todo != 1) {
1599 if (am_has_feature(hostp->features, fe_req_xml))
1600 o = xml_optionstr(dp, hostp->features, outf, 0);
1602 o = optionstr(dp, hostp->features, outf);
1607 qname = quote_string(dp->name);
1608 b64disk = amxml_format_tag("disk", dp->name);
1609 qdevice = quote_string(dp->device);
1611 b64device = amxml_format_tag("diskdevice", dp->device);
1612 if ((dp->name && qname[0] == '"') ||
1613 (dp->device && qdevice[0] == '"')) {
1614 if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1616 _("WARNING: %s:%s:%s host does not support quoted text\n"),
1617 hostp->hostname, qname, qdevice);
1618 g_fprintf(outf, _("You must upgrade amanda on the client to "
1619 "specify a quoted text/device in the disklist, "
1620 "or don't use quoted text for the device.\n"));
1625 if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1627 _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
1628 hostp->hostname, qname, dp->device);
1629 g_fprintf(outf, _("You must upgrade amanda on the client to "
1630 "specify a diskdevice in the disklist "
1631 "or don't specify a diskdevice in the disklist.\n"));
1633 if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1635 _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
1636 hostp->hostname, qname, dp->device);
1637 g_fprintf(outf, _("You must upgrade amanda on the client to "
1638 "specify a diskdevice in the disklist"
1639 " or don't specify a diskdevice in the disklist.\n"));
1641 if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1643 _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
1644 hostp->hostname, qname, dp->device);
1645 g_fprintf(outf, _("You must upgrade amanda on the client to "
1646 "specify a diskdevice in the disklist"
1647 " or don't specify a diskdevice in the disklist.\n"));
1651 (strcmp(dp->program,"DUMP") == 0 ||
1652 strcmp(dp->program,"GNUTAR") == 0)) {
1653 if(strcmp(dp->program, "DUMP") == 0 &&
1654 !am_has_feature(hostp->features, fe_program_dump)) {
1655 g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
1656 hostp->hostname, qname);
1657 g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
1658 "or you can use another program.\n"));
1660 if(strcmp(dp->program, "GNUTAR") == 0 &&
1661 !am_has_feature(hostp->features, fe_program_gnutar)) {
1662 g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
1663 hostp->hostname, qname);
1664 g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
1665 "or you can use another program.\n"));
1667 if(dp->estimate == ES_CALCSIZE &&
1668 !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1669 g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
1670 "estimate, using CLIENT.\n"),
1671 hostp->hostname, qname);
1672 g_fprintf(outf, _("You must upgrade amanda on the client to use "
1673 "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
1674 dp->estimate = ES_CLIENT;
1676 if(dp->estimate == ES_CALCSIZE &&
1677 am_has_feature(hostp->features, fe_selfcheck_calcsize))
1678 calcsize = "CALCSIZE ";
1682 if(dp->compress == COMP_CUST &&
1683 !am_has_feature(hostp->features, fe_options_compress_cust)) {
1685 _("ERROR: Client %s does not support custom compression.\n"),
1687 g_fprintf(outf, _("You must upgrade amanda on the client to "
1688 "use custom compression\n"));
1689 g_fprintf(outf, _("Otherwise you can use the default client "
1690 "compression program.\n"));
1692 if(dp->encrypt == ENCRYPT_CUST ) {
1693 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1695 _("ERROR: Client %s does not support data encryption.\n"),
1697 g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
1699 } else if ( dp->compress == COMP_SERVER_FAST ||
1700 dp->compress == COMP_SERVER_BEST ||
1701 dp->compress == COMP_SERVER_CUST ) {
1703 _("ERROR: %s: Client encryption with server compression "
1704 "is not supported. See amanda.conf(5) for detail.\n"),
1709 if (am_has_feature(hostp->features, fe_req_xml)) {
1710 l = vstralloc("<dle>\n"
1713 "</program>\n", NULL);
1714 if (strlen(calcsize) > 0)
1715 vstrextend(&l, " <calcsize>YES</calcsize>\n", NULL);
1716 vstrextend(&l, " ", b64disk, "\n", NULL);
1718 vstrextend(&l, " ", b64device, "\n", NULL);
1719 vstrextend(&l, o, "</dle>\n", NULL);
1722 l = vstralloc(calcsize,
1731 l = vstralloc(calcsize,
1741 if (!am_has_feature(hostp->features, fe_program_application_api) ||
1742 !am_has_feature(hostp->features, fe_req_xml)) {
1743 g_fprintf(outf, _("ERROR: %s:%s does not support APPLICATION-API.\n"),
1744 hostp->hostname, qname);
1745 g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
1746 " It is case sensitive\n"));
1749 l = vstralloc("<dle>\n"
1750 " <program>APPLICATION</program>\n", NULL);
1751 if (dp->application) {
1752 char *xml_app = xml_application(dp->application,
1754 vstrextend(&l, xml_app, NULL);
1757 if (dp->pp_scriptlist) {
1758 if (!am_has_feature(hostp->features, fe_pp_script)) {
1760 _("ERROR: %s:%s does not support SCRIPT-API.\n"),
1761 hostp->hostname, qname);
1764 vstrextend(&l, " ", b64disk, "\n", NULL);
1766 vstrextend(&l, " ", b64device, "\n", NULL);
1767 vstrextend(&l, o, "</dle>\n", NULL);
1778 dp->up = DISK_ACTIVE;
1782 else { /* noop service */
1783 req = vstralloc("SERVICE ", "noop", "\n",
1785 "features=", our_feature_string, ";",
1788 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1789 if(dp->up != DISK_READY || dp->todo != 1) {
1796 if(disk_count == 0) {
1798 hostp->up = HOST_DONE;
1802 secdrv = security_getdriver(hostp->disks->security_driver);
1803 if (secdrv == NULL) {
1804 fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
1805 hostp->disks->security_driver, hostp->hostname);
1807 protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf,
1808 req, conf_ctimeout, handle_result, hostp);
1813 hostp->up = HOST_ACTIVE;
1817 start_client_checks(
1826 switch(pid = fork()) {
1828 error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
1841 set_pname("amcheck-clients");
1845 if((outf = fdopen(fd, "w")) == NULL) {
1846 error(_("fdopen %d: %s"), fd, strerror(errno));
1851 g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
1852 g_fprintf(outf, "--------------------------------\n");
1856 hostcount = remote_errors = 0;
1858 for(dp = origq.head; dp != NULL; dp = dp->next) {
1860 if(hostp->up == HOST_READY && dp->todo == 1) {
1861 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
1862 run_server_scripts(EXECUTE_ON_PRE_HOST_AMCHECK,
1863 get_config_name(), dp1, -1);
1865 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
1866 run_server_scripts(EXECUTE_ON_PRE_DLE_AMCHECK,
1867 get_config_name(), dp1, -1);
1877 g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."),
1878 _("Client check: %d hosts checked in %s seconds."),
1880 hostcount, walltime_str(curclock()));
1881 g_fprintf(outf, plural(_(" %d problem found.\n"),
1882 _(" %d problems found.\n"), remote_errors),
1886 exit(userbad || remote_errors > 0);
1895 security_handle_t * sech)
1905 hostp = (am_host_t *)datap;
1906 hostp->up = HOST_READY;
1910 _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
1911 security_geterror(sech));
1913 hostp->up = HOST_DONE;
1918 g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
1919 hostp->hostname, pkt->body);
1926 skip_quoted_line(s, ch);
1927 if (s[-2] == '\n') {
1931 if(strncmp_const(line, "OPTIONS ") == 0) {
1933 t = strstr(line, "features=");
1934 if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
1935 char *u = strchr(t, ';');
1938 t += SIZEOF("features=")-1;
1939 am_release_feature_set(hostp->features);
1940 if((hostp->features = am_string_to_feature(t)) == NULL) {
1941 g_fprintf(outf, _("ERROR: %s: bad features value: '%s'\n"),
1942 hostp->hostname, t);
1943 g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
1945 hostp->up = HOST_DONE;
1954 if(strncmp_const(line, "OK ") == 0) {
1959 if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
1960 skip_whitespace(t, tch);
1962 * If the "error" is that the "noop" service is unknown, it
1963 * just means the client is "old" (does not support the service).
1964 * We can ignore this.
1966 if(!((hostp->features == NULL) && (pkt->type == P_NAK)
1967 && ((strcmp(t - 1, "unknown service: noop") == 0)
1968 || (strcmp(t - 1, "noop: invalid service") == 0)))) {
1969 g_fprintf(outf, _("ERROR: %s%s: %s\n"),
1970 (pkt->type == P_NAK) ? "NAK " : "",
1974 hostp->up = HOST_DONE;
1979 g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
1980 hostp->hostname, line);
1982 hostp->up = HOST_DONE;
1984 if(hostp->up == HOST_READY && hostp->features == NULL) {
1986 * The client does not support the features list, so give it an
1989 dbprintf(_("no feature set from host %s\n"), hostp->hostname);
1990 hostp->features = am_set_default_feature_set();
1992 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1993 if(dp->up == DISK_ACTIVE) {
1998 if(hostp->up == HOST_DONE) {
1999 security_close_connection(sech, hostp->hostname);
2000 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2001 run_server_scripts(EXECUTE_ON_POST_DLE_AMCHECK,
2002 get_config_name(), dp, -1);
2004 for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2005 run_server_scripts(EXECUTE_ON_POST_HOST_AMCHECK,
2006 get_config_name(), dp, -1);
2009 /* try to clean up any defunct processes, since Amanda doesn't wait() for
2011 while(waitpid(-1, NULL, WNOHANG)> 0);