1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-2000 University of Maryland at College Park
4  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /*
28  * $Id: amcheck.c,v 1.149 2006/08/24 01:57:16 paddy_s Exp $
29  *
30  * checks for common problems in server and clients
31  */
32 #include "amanda.h"
33 #include "util.h"
34 #include "conffile.h"
35 #include "columnar.h"
36 #include "fsusage.h"
37 #include "diskfile.h"
38 #include "tapefile.h"
39 #include "packet.h"
40 #include "security.h"
41 #include "protocol.h"
42 #include "clock.h"
43 #include "amindex.h"
44 #include "server_util.h"
45 #include "pipespawn.h"
46 #include "amfeatures.h"
47 #include "device.h"
48 #include "property.h"
49 #include "timestamp.h"
50 #include "amxml.h"
51 #include "physmem.h"
52 #include <getopt.h>
53 
54 #define BUFFER_SIZE	32768
55 
56 static time_t conf_ctimeout;
57 static int overwrite;
58 
59 static disklist_t origq;
60 
61 static uid_t uid_dumpuser;
62 static gboolean who_check_host_setting = TRUE;//  TRUE => local check auth
63                                       // FALSE => clent check auth
64 
65 /* local functions */
66 
67 void usage(void);
68 pid_t start_client_checks(int fd);
69 pid_t start_server_check(int fd, int do_localchk, int do_tapechk);
70 int main(int argc, char **argv);
71 int check_tapefile(FILE *outf, char *tapefile);
72 int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
73 static int check_host_setting(FILE *outf);
74 
75 void
usage(void)76 usage(void)
77 {
78     g_printf(_("Usage: amcheck [--version] [-am] [-w] [-sclt] [-M <address>] [--client-verbose] [--exact_match] [-o configoption]* <conf> [host [disk]* ]*\n"));
79     exit(1);
80     /*NOTREACHED*/
81 }
82 
83 static am_feature_t *our_features = NULL;
84 static char *our_feature_string = NULL;
85 static char *displayunit;
86 static long int unitdivisor;
87 static gboolean dev_amanda_data_path = TRUE;
88 static gboolean dev_directtcp_data_path = TRUE;
89 
90 static int client_verbose = FALSE;
91 static gboolean exact_match = FALSE;
92 static struct option long_options[] = {
93     {"client-verbose", 0, NULL,  1},
94     {"version"       , 0, NULL,  2},
95     {"exact-match"   , 0, NULL,  3},
96     {NULL, 0, NULL, 0}
97 };
98 
99 int
main(int argc,char ** argv)100 main(
101     int		argc,
102     char **	argv)
103 {
104     char buffer[BUFFER_SIZE];
105     char *version_string;
106     char *mainfname = NULL;
107     char pid_str[NUM_STR_SIZE];
108     int do_clientchk, client_probs;
109     int do_localchk, do_tapechk, server_probs;
110     pid_t clientchk_pid, serverchk_pid;
111     int tempfd, mainfd;
112     size_t size;
113     amwait_t retstat;
114     pid_t pid;
115     extern int optind;
116     char *mailto = NULL;
117     extern char *optarg;
118     int mailout;
119     int alwaysmail;
120     char *tempfname = NULL;
121     char *conf_diskfile;
122     char *dumpuser;
123     struct passwd *pw;
124     uid_t uid_me;
125     char *errstr;
126     config_overrides_t *cfg_ovr;
127     char *mailer;
128 
129     /*
130      * Configure program for internationalization:
131      *   1) Only set the message locale for now.
132      *   2) Set textdomain for all amanda related programs to "amanda"
133      *      We don't want to be forced to support dozens of message catalogs.
134      */
135     setlocale(LC_MESSAGES, "C");
136     textdomain("amanda");
137 
138     safe_fd(-1, 0);
139     safe_cd();
140     make_crc_table();
141 
142     set_pname("amcheck");
143     /* drop root privileges */
144     if (!set_root_privs(0)) {
145 	error(_("amcheck must be run setuid root"));
146     }
147 
148     /* Don't die when child closes pipe */
149     signal(SIGPIPE, SIG_IGN);
150 
151     dbopen(DBG_SUBDIR_SERVER);
152 
153     memset(buffer, 0, sizeof(buffer));
154 
155     g_snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid());
156 
157     add_amanda_log_handler(amanda_log_stderr);
158 
159     our_features = am_init_feature_set();
160     our_feature_string = am_feature_to_string(our_features);
161 
162     uid_me = geteuid();
163 
164     alwaysmail = mailout = overwrite = 0;
165     do_localchk = do_tapechk = do_clientchk = 0;
166     server_probs = client_probs = 0;
167     tempfd = mainfd = -1;
168 
169     /* process arguments */
170 
171     cfg_ovr = new_config_overrides(argc/2);
172     while (1) {
173 	int option_index = 0;
174 	int c;
175 	c = getopt_long (argc, argv, "M:mawsclto:", long_options, &option_index);
176 	if (c == -1) {
177 	    break;
178 	}
179 
180 	switch(c) {
181 	case 1:		client_verbose = TRUE;
182 			break;
183 	case 2:		printf("amcheck-%s\n", VERSION);
184 			return(0);
185 			break;
186 	case 3:		exact_match = TRUE;
187 			break;
188 	case 'M':	if (mailto) {
189 			    g_printf(_("Multiple -M options\n"));
190 			    exit(1);
191 			}
192 			mailto=stralloc(optarg);
193 			if(!validate_mailto(mailto)){
194 			   g_printf(_("Invalid characters in mail address\n"));
195 			   exit(1);
196 			}
197 			/*FALLTHROUGH*/
198 	case 'm':	mailout = 1;
199 			break;
200 	case 'a':	mailout = 1;
201 			alwaysmail = 1;
202 			break;
203 	case 's':	do_localchk = do_tapechk = 1;
204 			break;
205 	case 'c':	do_clientchk = 1;
206 			break;
207 	case 'l':	do_localchk = 1;
208 			break;
209 	case 'w':	overwrite = 1;
210 			break;
211 	case 'o':	add_config_override_opt(cfg_ovr, optarg);
212 			break;
213 	case 't':	do_tapechk = 1;
214 			break;
215 	case '?':
216 	default:
217 	    usage();
218 	}
219     }
220     argc -= optind, argv += optind;
221     if(argc < 1) usage();
222 
223 
224     if ((do_localchk | do_clientchk | do_tapechk) == 0) {
225 	/* Check everything if individual checks were not asked for */
226 	do_localchk = do_clientchk = do_tapechk = 1;
227     }
228 
229     if(overwrite)
230 	do_tapechk = 1;
231 
232     set_config_overrides(cfg_ovr);
233     config_init(CONFIG_INIT_EXPLICIT_NAME, argv[0]);
234     dbrename(get_config_name(), DBG_SUBDIR_SERVER);
235 
236     conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
237     read_diskfile(conf_diskfile, &origq);
238     disable_skip_disk(&origq);
239     amfree(conf_diskfile);
240 
241     if (config_errors(NULL) >= CFGERR_WARNINGS) {
242 	config_print_errors();
243 	if (config_errors(NULL) >= CFGERR_ERRORS) {
244 	    g_critical(_("errors processing config file"));
245 	}
246     }
247 
248     mailer = getconf_str(CNF_MAILER);
249     if ((!mailer || *mailer == '\0') && mailout == 1) {
250 	if (alwaysmail == 1) {
251 	    g_printf(_("You can't use -a because a mailer is not defined\n"));
252 	} else {
253 	    g_printf(_("You can't use -m because a mailer is not defined\n"));
254 	}
255 	exit(1);
256     }
257     if(mailout && !mailto &&
258        (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
259 	g_printf(_("\nWARNING:No mail address configured in  amanda.conf.\n"));
260 	g_printf(_("To receive dump results by email configure the "
261 		 "\"mailto\" parameter in amanda.conf\n"));
262         if(alwaysmail)
263 		g_printf(_("When using -a option please specify -Maddress also\n\n"));
264 	else
265 		g_printf(_("Use -Maddress instead of -m\n\n"));
266 	exit(1);
267     }
268     if(mailout && !mailto)
269     {
270        if(getconf_seen(CNF_MAILTO) &&
271           strlen(getconf_str(CNF_MAILTO)) > 0) {
272           if(!validate_mailto(getconf_str(CNF_MAILTO))){
273 		g_printf(_("\nMail address in amanda.conf has invalid characters"));
274 		g_printf(_("\nNo email will be sent\n"));
275                 mailout = 0;
276           }
277        }
278        else {
279 	  g_printf(_("\nNo mail address configured in  amanda.conf\n"));
280           if(alwaysmail)
281 		g_printf(_("When using -a option please specify -Maddress also\n\n"));
282 	  else
283 		g_printf(_("Use -Maddress instead of -m\n\n"));
284 	  exit(1);
285       }
286     }
287 
288     conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
289 
290     errstr = match_disklist(&origq, exact_match, argc-1, argv+1);
291     if (errstr) {
292 	g_printf(_("%s"),errstr);
293 	amfree(errstr);
294     }
295 
296     /*
297      * Make sure we are running as the dump user.  Don't use
298      * check_running_as(..) here, because we want to produce more
299      * verbose error messages.
300      */
301     dumpuser = getconf_str(CNF_DUMPUSER);
302     if ((pw = getpwnam(dumpuser)) == NULL) {
303 	error(_("amanda.conf has dump user configured to \"%s\", but that user does not exist."), dumpuser);
304 	/*NOTREACHED*/
305     }
306     uid_dumpuser = pw->pw_uid;
307     if ((pw = getpwuid(uid_me)) == NULL) {
308 	error(_("cannot get username for running user, uid %ld is not in your user database."),
309 	    (long)uid_me);
310 	/*NOTREACHED*/
311     }
312 #ifdef CHECK_USERID
313     if (uid_me != uid_dumpuser) {
314 	error("must be executed as the \"%s\" user instead of the \"%s\" user.\n"
315 		"Change user to \"%s\" or change dumpuser to \"%s\" in amanda.conf",
316 	      dumpuser, pw->pw_name, dumpuser, pw->pw_name);
317         /*NOTREACHED*/
318     }
319 #endif
320 
321     displayunit = getconf_str(CNF_DISPLAYUNIT);
322     unitdivisor = getconf_unit_divisor();
323 
324     /*
325      * If both server and client side checks are being done, the server
326      * check output goes to the main output, while the client check output
327      * goes to a temporary file and is copied to the main output when done.
328      *
329      * If the output is to be mailed, the main output is also a disk file,
330      * otherwise it is stdout.
331      */
332     if(do_clientchk && (do_localchk || do_tapechk)) {
333 	/* we need the temp file */
334 	tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
335 	if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
336 	    error(_("could not open temporary amcheck output file %s: %s. Check permissions"), tempfname, strerror(errno));
337 	    /*NOTREACHED*/
338 	}
339 	unlink(tempfname);			/* so it goes away on close */
340 	amfree(tempfname);
341     }
342 
343     if(mailout) {
344 	/* the main fd is a file too */
345 	mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
346 	if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
347 	    error(_("could not open amcheck server output file %s: %s. Check permissions"), mainfname, strerror(errno));
348 	    /*NOTREACHED*/
349 	}
350 	unlink(mainfname);			/* so it goes away on close */
351 	amfree(mainfname);
352     }
353     else
354 	/* just use stdout */
355 	mainfd = 1;
356 
357     who_check_host_setting = do_localchk;
358 
359     /* start server side checks */
360 
361     if(do_localchk || do_tapechk)
362 	serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
363     else
364 	serverchk_pid = 0;
365 
366     /* start client side checks */
367 
368     if(do_clientchk) {
369 	clientchk_pid = start_client_checks((do_localchk || do_tapechk) ? tempfd : mainfd);
370     } else {
371 	clientchk_pid = 0;
372     }
373 
374     /* wait for child processes and note any problems */
375 
376     while(1) {
377 	if((pid = wait(&retstat)) == -1) {
378 	    if(errno == EINTR) continue;
379 	    else break;
380 	} else if(pid == clientchk_pid) {
381 	    client_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
382 	    clientchk_pid = 0;
383 	} else if(pid == serverchk_pid) {
384 	    server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
385 	    serverchk_pid = 0;
386 	} else {
387 	    char *wait_msg = NULL;
388 
389 	    wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid);
390 	    if (full_write(mainfd, wait_msg, strlen(wait_msg)) < strlen(wait_msg)) {
391 		error(_("write main file: %s"), strerror(errno));
392 		/*NOTREACHED*/
393 	    }
394 	    amfree(wait_msg);
395 	}
396     }
397 
398     /* copy temp output to main output and write tagline */
399 
400     if(do_clientchk && (do_localchk || do_tapechk)) {
401 	if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) {
402 	    error(_("seek temp file: %s"), strerror(errno));
403 	    /*NOTREACHED*/
404 	}
405 
406 	while((size = full_read(tempfd, buffer, SIZEOF(buffer))) > 0) {
407 	    if (full_write(mainfd, buffer, size) < size) {
408 		error(_("write main file: %s"), strerror(errno));
409 		/*NOTREACHED*/
410 	    }
411 	}
412 	if(errno != 0) {
413 	    error(_("read temp file: %s"), strerror(errno));
414 	    /*NOTREACHED*/
415 	}
416 	aclose(tempfd);
417     }
418 
419     version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), VERSION);
420     if (full_write(mainfd, version_string, strlen(version_string)) < strlen(version_string)) {
421 	error(_("write main file: %s"), strerror(errno));
422 	/*NOTREACHED*/
423     }
424     amfree(version_string);
425     amfree(our_feature_string);
426     am_release_feature_set(our_features);
427     our_features = NULL;
428 
429     /* send mail if requested, but only if there were problems */
430 
431     if((server_probs || client_probs || alwaysmail) && mailout) {
432 	int mailfd;
433 	int nullfd;
434 	int errfd;
435 	FILE *ferr;
436 	char *subject;
437 	char **a, **b;
438 	GPtrArray *pipeargs;
439 	amwait_t retstat;
440 	size_t r;
441 	size_t w;
442 	char *err = NULL;
443 	char *extra_info = NULL;
444 	char *line = NULL;
445 	int rc;
446 
447 	fflush(stdout);
448 	if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
449 	    error(_("lseek main file: %s"), strerror(errno));
450 	    /*NOTREACHED*/
451 	}
452 	if(alwaysmail && !(server_probs || client_probs)) {
453 	    subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"),
454 			getconf_str(CNF_ORG));
455 	} else {
456 	    subject = vstrallocf(
457 			_("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"),
458 			getconf_str(CNF_ORG));
459 	}
460 	if(mailto) {
461 	    a = (char **) g_new0(char *, 2);
462 	    a[0] = stralloc(mailto);
463 	    a[1] = NULL;
464 	} else {
465 	    /* (note that validate_mailto doesn't allow any quotes, so this
466 	     * is really just splitting regular old strings) */
467 	    a = split_quoted_strings(getconf_str(CNF_MAILTO));
468 	}
469 	if((nullfd = open("/dev/null", O_RDWR)) < 0) {
470 	    error("nullfd: /dev/null: %s", strerror(errno));
471 	    /*NOTREACHED*/
472 	}
473 
474 	/* assemble the command line for the mailer */
475 	pipeargs = g_ptr_array_sized_new(4);
476 	g_ptr_array_add(pipeargs, mailer);
477 	g_ptr_array_add(pipeargs, "-s");
478 	g_ptr_array_add(pipeargs, subject);
479 	for (b = a; *b; b++)
480 	    g_ptr_array_add(pipeargs, *b);
481 	g_ptr_array_add(pipeargs, NULL);
482 
483 	pipespawnv(mailer, STDIN_PIPE | STDERR_PIPE, 0,
484 		   &mailfd, &nullfd, &errfd,
485 		   (char **)pipeargs->pdata);
486 
487 	g_ptr_array_free(pipeargs, FALSE);
488 	amfree(subject);
489 	amfree(mailto);
490 	g_strfreev(a);
491 
492 	/*
493 	 * There is the potential for a deadlock here since we are writing
494 	 * to the process and then reading stderr, but in the normal case,
495 	 * nothing should be coming back to us, and hopefully in error
496 	 * cases, the pipe will break and we will exit out of the loop.
497 	 */
498 	signal(SIGPIPE, SIG_IGN);
499 	while((r = full_read(mainfd, buffer, SIZEOF(buffer))) > 0) {
500 	    if((w = full_write(mailfd, buffer, r)) != r) {
501 		if(errno == EPIPE) {
502 		    strappend(extra_info, _("EPIPE writing to mail process\n"));
503 		    break;
504 		} else if(errno != 0) {
505 		    error(_("mailfd write: %s"), strerror(errno));
506 		    /*NOTREACHED*/
507 		} else {
508 		    error(_("mailfd write: wrote %zd instead of %zd"), w, r);
509 		    /*NOTREACHED*/
510 		}
511 	    }
512 	}
513 	aclose(mailfd);
514 	ferr = fdopen(errfd, "r");
515 	if (!ferr) {
516 	    error(_("Can't fdopen: %s"), strerror(errno));
517 	    /*NOTREACHED*/
518 	}
519 	for(; (line = agets(ferr)) != NULL; free(line)) {
520 	    if (line[0] == '\0')
521 		continue;
522 	    strappend(extra_info, line);
523 	    strappend(extra_info, "\n");
524 	}
525 	afclose(ferr);
526 	errfd = -1;
527 	rc = 0;
528 	while (wait(&retstat) != -1) {
529 	    if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) {
530 		char *mailer_error = str_exit_status("mailer", retstat);
531 		strappend(err, mailer_error);
532 		amfree(mailer_error);
533 
534 		rc = 1;
535 	    }
536 	}
537 	if (rc != 0) {
538 	    if(extra_info) {
539 		fputs(extra_info, stderr);
540 		amfree(extra_info);
541 	    }
542 	    error(_("error running mailer %s: %s"), mailer, err?err:"(unknown)");
543 	    /*NOTREACHED*/
544 	}
545     }
546 
547     dbclose();
548     return (server_probs || client_probs);
549 }
550 
551 /* --------------------------------------------------- */
552 
553 static char *datestamp;
554 static FILE *errf = NULL;
555 
check_tapefile(FILE * outf,char * tapefile)556 int check_tapefile(
557     FILE *outf,
558     char *tapefile)
559 {
560     struct stat statbuf;
561     char *quoted;
562     int tapebad = 0;
563 
564     if (stat(tapefile, &statbuf) == 0) {
565 	if (!S_ISREG(statbuf.st_mode)) {
566 	    quoted = quote_string(tapefile);
567 	    g_fprintf(outf, _("ERROR: tapelist %s: should be a regular file.\n"),
568 		    quoted);
569 	    tapebad = 1;
570 	    amfree(quoted);
571 	} else if (access(tapefile, F_OK) != 0) {
572 	    quoted = quote_string(tapefile);
573 	    g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted);
574 	    tapebad = 1;
575 	    amfree(quoted);
576 	} else if (access(tapefile, W_OK) != 0) {
577 	    quoted = quote_string(tapefile);
578 	    g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted);
579 	    tapebad = 1;
580 	    amfree(quoted);
581 	}
582     }
583     return tapebad;
584 }
585 
586 int
test_server_pgm(FILE * outf,char * dir,char * pgm,int suid,uid_t dumpuid)587 test_server_pgm(
588     FILE *	outf,
589     char *	dir,
590     char *	pgm,
591     int		suid,
592     uid_t	dumpuid)
593 {
594     struct stat statbuf;
595     int pgmbad = 0;
596     char *quoted;
597 
598     pgm = vstralloc(dir, "/", pgm, NULL);
599     quoted = quote_string(pgm);
600     if(stat(pgm, &statbuf) == -1) {
601 	g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
602 		quoted);
603 	pgmbad = 1;
604     } else if (!S_ISREG(statbuf.st_mode)) {
605 	g_fprintf(outf, _("ERROR: program %s: not a file\n"),
606 		quoted);
607 	pgmbad = 1;
608     } else if (access(pgm, X_OK) == -1) {
609 	g_fprintf(outf, _("ERROR: program %s: not executable\n"),
610 		quoted);
611 	pgmbad = 1;
612 #ifndef SINGLE_USERID
613     } else if (suid \
614 	       && dumpuid != 0
615 	       && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
616 	g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"),
617 		quoted);
618 	pgmbad = 1;
619 #else
620     /* Quiet unused parameter warnings */
621     (void)suid;
622     (void)dumpuid;
623 #endif /* SINGLE_USERID */
624     }
625     amfree(quoted);
626     amfree(pgm);
627     return pgmbad;
628 }
629 
630 /* check that the tape is a valid amanda tape
631    Returns TRUE if all tests passed; FALSE otherwise. */
test_tape_status(FILE * outf)632 static gboolean test_tape_status(FILE * outf) {
633     int dev_outfd = -1;
634     FILE *dev_outf = NULL;
635     int outfd;
636     int nullfd = -1;
637     pid_t devpid;
638     char *amcheck_device = NULL;
639     char *line;
640     gchar **args;
641     amwait_t wait_status;
642     gboolean success;
643 
644     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
645 	return FALSE;
646     }
647 
648     fflush(outf);
649     outfd = fileno(outf);
650 
651     amcheck_device = vstralloc(amlibexecdir, "/", "amcheck-device", NULL);
652     args = get_config_options(overwrite? 3 : 2);
653     args[0] = amcheck_device; /* steal the reference */
654     args[1] = g_strdup(get_config_name());
655     if (overwrite)
656 	args[2] = g_strdup("-w");
657 
658     /* run libexecdir/amcheck-device.pl, capturing STDERR and STDOUT to outf */
659     devpid = pipespawnv(amcheck_device, STDOUT_PIPE, 0,
660 	    &nullfd, &dev_outfd, &outfd,
661 	    (char **)args);
662 
663     dev_outf = fdopen(dev_outfd, "r");
664     if (dev_outf == NULL) {
665 	g_debug("Can't fdopen amcheck-device stdout: %s", strerror(errno));
666 	aclose(dev_outfd);
667     } else {
668 	while ((line = agets(dev_outf)) != NULL) {
669 	    if (strncmp(line, "DATA-PATH", 9) == 0) {
670 		char *c = line;
671 		dev_amanda_data_path = FALSE;
672 		dev_directtcp_data_path = FALSE;
673 		while ((c = strchr(c, ' ')) != NULL) {
674 		    c++;
675 		    if (strncmp(c, "AMANDA", 6) == 0) {
676 			dev_amanda_data_path = TRUE;
677 		    } else if (strncmp(c, "DIRECTTCP", 9) == 0) {
678 			dev_directtcp_data_path = TRUE;
679 		    }
680 		}
681 	    } else {
682 		if (full_write(outfd, line, strlen(line)) != strlen(line)) {
683 		    g_debug("Failed to print amcheck-device output to stdout: %s", strerror(errno));
684 		}
685 		if (full_write(outfd, "\n", 1) != 1) {
686 		    g_debug("Failed to print amcheck-device \\n to stdout: %s", strerror(errno));
687 		}
688 	    }
689 	    g_free(line);
690 	}
691     }
692 
693     /* and immediately wait for it to die */
694     waitpid(devpid, &wait_status, 0);
695 
696     if (WIFSIGNALED(wait_status)) {
697 	g_fprintf(outf, _("amcheck-device terminated with signal %d\n"),
698 		  WTERMSIG(wait_status));
699 	success = FALSE;
700     } else if (WIFEXITED(wait_status)) {
701 	success = (WEXITSTATUS(wait_status) == 0);
702     } else {
703 	success = FALSE;
704     }
705 
706     g_strfreev(args);
707     close(nullfd);
708 
709     return success;
710 }
711 
712 pid_t
start_server_check(int fd,int do_localchk,int do_tapechk)713 start_server_check(
714     int		fd,
715     int		do_localchk,
716     int		do_tapechk)
717 {
718     struct fs_usage fsusage;
719     FILE *outf = NULL;
720     pid_t pid G_GNUC_UNUSED;
721     int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
722     int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
723     int testtape = do_tapechk;
724     tapetype_t *tp = NULL;
725     char *quoted;
726     int res;
727     intmax_t kb_avail, kb_needed;
728     off_t tape_size;
729     gboolean printed_small_part_size_warning = FALSE;
730     char *small_part_size_warning =
731 	_(" This may create > 1000 parts, severely degrading backup/restore performance.\n"
732 	" See http://wiki.zmanda.com/index.php/Splitsize_too_small for more information.\n");
733 
734     switch(pid = fork()) {
735     case -1:
736 	error(_("could not spawn a process for checking the server: %s"), strerror(errno));
737         g_assert_not_reached();
738 
739     case 0:
740 	break;
741 
742     default:
743 	return pid;
744     }
745 
746     dup2(fd, 1);
747     dup2(fd, 2);
748 
749     set_pname("amcheck-server");
750 
751     startclock();
752 
753     /* server does not need root privileges, and the access() calls below use the real userid,
754      * so totally drop privileges at this point (making the userid equal to the dumpuser) */
755     set_root_privs(-1);
756 
757     if((outf = fdopen(fd, "w")) == NULL) {
758 	error(_("fdopen %d: %s"), fd, strerror(errno));
759 	/*NOTREACHED*/
760     }
761     errf = outf;
762 
763     g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
764     g_fprintf(outf, "-----------------------------\n");
765 
766     if (do_localchk || testtape) {
767         tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
768     }
769 
770     if (who_check_host_setting) {
771 	check_host_setting(outf);
772     }
773 
774     /*
775      * Check various server side config file settings.
776      */
777     if(do_localchk) {
778 	char *ColumnSpec;
779 	char *errstr = NULL;
780 	char *lbl_templ;
781 
782 	ColumnSpec = getconf_str(CNF_COLUMNSPEC);
783 	if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
784 	    g_fprintf(outf, _("ERROR: %s\n"), errstr);
785 	    amfree(errstr);
786 	    confbad = 1;
787 	}
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) {
792 		g_fprintf(outf,
793 			_("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
794 			lbl_templ,
795 			strerror(errno));
796 		confbad = 1;
797 	    }
798 #if !defined(HAVE_LPR_CMD)
799 	    g_fprintf(outf, _("ERROR: lbl-templ set but no LPR command defined. You should reconfigure amanda\n       and make sure it finds a lpr or lp command.\n"));
800 	    confbad = 1;
801 #endif
802 	}
803 
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));
809 	}
810 
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));
816 	}
817 
818 	if (getconf_int(CNF_TAPERFLUSH) > 0 &&
819 	    !getconf_no_yes_all(CNF_AUTOFLUSH)) {
820 	    g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' or 'all' if taperflush (%d) is greater that 0.\n"),
821 		      getconf_int(CNF_TAPERFLUSH));
822 	}
823 
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));
827 	    confbad = 1;
828 	}
829 
830 	if (!getconf_seen(CNF_TAPETYPE)) {
831 	    g_fprintf(outf,
832 		      _("ERROR: no tapetype specified; you must give a value for "
833 			"the 'tapetype' parameter\n"));
834 	    confbad = 1;
835 	}
836 
837 	{
838 	    uintmax_t kb_avail = physmem_total() / 1024;
839 	    uintmax_t kb_needed = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE) / 1024;
840 	    if (kb_avail < kb_needed) {
841 		g_fprintf(outf,
842 		    "ERROR: system has %ju %sB memory, but device-output-buffer-size needs %ju %sB\n",
843 		kb_avail/(uintmax_t)unitdivisor, displayunit,
844 		kb_needed/(uintmax_t)unitdivisor, displayunit);
845 	    }
846 	}
847     }
848 
849     /*
850      * Look up the programs used on the server side.
851      */
852     if(do_localchk) {
853 	/*
854 	 * entreprise version will do planner/dumper suid check
855 	 */
856 	if(access(amlibexecdir, X_OK) == -1) {
857 	    quoted = quote_string(amlibexecdir);
858 	    g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
859 		    quoted);
860 	    g_fprintf(outf, _("Check permissions\n"));
861 	    pgmbad = 1;
862 	    amfree(quoted);
863 	} else {
864 	    if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
865 		pgmbad = 1;
866 	    if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
867 		pgmbad = 1;
868 	    if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
869 		pgmbad = 1;
870 	    if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
871 		pgmbad = 1;
872 	    if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
873 		pgmbad = 1;
874 	    if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
875 		pgmbad = 1;
876 	}
877 	if(access(sbindir, X_OK) == -1) {
878 	    quoted = quote_string(sbindir);
879 	    g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
880 		    sbindir);
881 	    g_fprintf(outf, _("Check permissions\n"));
882 	    pgmbad = 1;
883 	    amfree(quoted);
884 	} else {
885 	    if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
886 		pgmbad = 1;
887 	    if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
888 		pgmbad = 1;
889 	    if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
890 		pgmbad = 1;
891 	    if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
892 		pgmbad = 1;
893 	}
894 	if(access(COMPRESS_PATH, X_OK) == -1) {
895 	    quoted = quote_string(COMPRESS_PATH);
896 	    g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
897 			    "and indexing will not work. \n"),quoted);
898 	    g_fprintf(outf, _("Check permissions\n"));
899 	    amfree(quoted);
900 	}
901     }
902 
903     /*
904      * Check that the directory for the tapelist file is writable, as well
905      * as the tapelist file itself (if it already exists).  Also, check for
906      * a "hold" file (just because it is convenient to do it here) and warn
907      * if tapedev is set to the null device.
908      */
909 
910     if(do_localchk || do_tapechk) {
911 	char *tapefile;
912 	char *newtapefile;
913 	char *tape_dir;
914 	char *lastslash;
915 	char *holdfile;
916         char * tapename;
917 	struct stat statbuf;
918 	guint64 part_size, part_cache_max_size, tape_size;
919 	part_cache_type_t part_cache_type;
920 	char *part_cache_dir;
921 
922 	tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
923 	/*
924 	 * XXX There Really Ought to be some error-checking here... dhw
925 	 */
926 	tape_dir = stralloc(tapefile);
927 	if ((lastslash = strrchr((const char *)tape_dir, '/')) != NULL) {
928 	    *lastslash = '\0';
929 	/*
930 	 * else whine Really Loudly about a path with no slashes??!?
931 	 */
932 	}
933 	if(access(tape_dir, W_OK) == -1) {
934 	    quoted = quote_string(tape_dir);
935 	    g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"),
936 		    quoted);
937 	    tapebad = 1;
938 	    amfree(quoted);
939 	}
940 	else if(stat(tapefile, &statbuf) == -1) {
941 	    if (errno != ENOENT) {
942 		quoted = quote_string(tape_dir);
943 		g_fprintf(outf, _("ERROR: tapelist %s (%s), "
944 			"you must create an empty file.\n"),
945 			quoted, strerror(errno));
946 		tapebad = 1;
947 		amfree(quoted);
948 	    } else {
949 		g_fprintf(outf, _("NOTE: tapelist will be created on the next run.\n"));
950 	    }
951 	} else {
952 	    tapebad |= check_tapefile(outf, tapefile);
953 	    if (tapebad == 0 && read_tapelist(tapefile)) {
954 		quoted = quote_string(tapefile);
955 		g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
956 		tapebad = 1;
957 		amfree(quoted);
958 	    }
959 	    newtapefile = stralloc2(tapefile, ".new");
960 	    tapebad |= check_tapefile(outf, newtapefile);
961 	    amfree(newtapefile);
962 	    newtapefile = stralloc2(tapefile, ".amlabel");
963 	    tapebad |= check_tapefile(outf, newtapefile);
964 	    amfree(newtapefile);
965 	    newtapefile = stralloc2(tapefile, ".amlabel.new");
966 	    tapebad |= check_tapefile(outf, newtapefile);
967 	    amfree(newtapefile);
968 	    newtapefile = stralloc2(tapefile, ".yesterday");
969 	    tapebad |= check_tapefile(outf, newtapefile);
970 	    amfree(newtapefile);
971 	    newtapefile = stralloc2(tapefile, ".yesterday.new");
972 	    tapebad |= check_tapefile(outf, newtapefile);
973 	    amfree(newtapefile);
974 	}
975 	holdfile = config_dir_relative("hold");
976 	if(access(holdfile, F_OK) != -1) {
977 	    quoted = quote_string(holdfile);
978 	    g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
979 	    g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
980 	    g_fprintf(outf, _("You might want to delete the existing hold file\n"));
981 	    amfree(quoted);
982 	}
983 	amfree(tapefile);
984 	amfree(tape_dir);
985 	amfree(holdfile);
986 	tapename = getconf_str(CNF_TAPEDEV);
987 	if (tapename == NULL) {
988 	    if (getconf_str(CNF_TPCHANGER) == NULL) {
989 		g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
990 		testtape = 0;
991 		do_tapechk = 0;
992 	    }
993 	}
994 
995 	/* check tapetype-based splitting parameters */
996 	part_size = tapetype_get_part_size(tp);
997 	part_cache_type = tapetype_get_part_cache_type(tp);
998 	part_cache_dir = tapetype_get_part_cache_dir(tp);
999 	part_cache_max_size = tapetype_get_part_cache_max_size(tp);
1000 
1001 	if (!tapetype_seen(tp, TAPETYPE_PART_SIZE)) {
1002 	    if (tapetype_seen(tp, TAPETYPE_PART_CACHE_TYPE)) {
1003 		g_fprintf(outf, "ERROR: part-cache-type specified, but no part-size\n");
1004 		tapebad = 1;
1005 	    }
1006 	    if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
1007 		g_fprintf(outf, "ERROR: part-cache-dir specified, but no part-size\n");
1008 		tapebad = 1;
1009 	    }
1010 	    if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
1011 		g_fprintf(outf, "ERROR: part-cache-max-size specified, but no part-size\n");
1012 		tapebad = 1;
1013 	    }
1014 	} else {
1015 	    switch (part_cache_type) {
1016 	    case PART_CACHE_TYPE_DISK:
1017 		if (!tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)
1018 			    || !part_cache_dir || !*part_cache_dir) {
1019 		    g_fprintf(outf,
1020 			"ERROR: part-cache-type is DISK, but no part-cache-dir specified\n");
1021 		    tapebad = 1;
1022 		} else {
1023 		    if(get_fs_usage(part_cache_dir, NULL, &fsusage) == -1) {
1024 			g_fprintf(outf, "ERROR: part-cache-dir '%s': %s\n",
1025 				part_cache_dir, strerror(errno));
1026 			tapebad = 1;
1027 		    } else {
1028 			kb_avail = fsusage.fsu_bavail_top_bit_set?
1029 			    0 : fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
1030 			kb_needed = part_size;
1031 			if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
1032 			    kb_needed = part_cache_max_size;
1033 			}
1034 			if (kb_avail < kb_needed) {
1035 			    g_fprintf(outf,
1036 				"ERROR: part-cache-dir has %ju %sB available, but needs %ju %sB\n",
1037 				kb_avail/(uintmax_t)unitdivisor, displayunit,
1038 				kb_needed/(uintmax_t)unitdivisor, displayunit);
1039 			    tapebad = 1;
1040 			}
1041 		    }
1042 		}
1043 		break;
1044 
1045 	    case PART_CACHE_TYPE_MEMORY:
1046 		kb_avail = physmem_total() / 1024;
1047 		kb_needed = part_size;
1048 		if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
1049 		    kb_needed = part_cache_max_size;
1050 		}
1051 		if (kb_avail < kb_needed) {
1052 		    g_fprintf(outf,
1053 			"ERROR: system has %ju %sB memory, but part cache needs %ju %sB\n",
1054 			kb_avail/(uintmax_t)unitdivisor, displayunit,
1055 			kb_needed/(uintmax_t)unitdivisor, displayunit);
1056 		    tapebad = 1;
1057 		}
1058 
1059 		/* FALL THROUGH */
1060 
1061 	    case PART_CACHE_TYPE_NONE:
1062 		if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
1063 		    g_fprintf(outf,
1064 			"ERROR: part-cache-dir specified, but part-cache-type is not DISK\n");
1065 		    tapebad = 1;
1066 		}
1067 		break;
1068 	    }
1069 	}
1070 
1071 	if (tapetype_seen(tp, TAPETYPE_PART_SIZE) && part_size == 0
1072 		&& part_cache_type != PART_CACHE_TYPE_NONE) {
1073 	    g_fprintf(outf,
1074 		    "ERROR: part_size is zero, but part-cache-type is not 'none'\n");
1075 	    tapebad = 1;
1076 	}
1077 
1078 	if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
1079 	    if (part_cache_type == PART_CACHE_TYPE_NONE) {
1080 		g_fprintf(outf,
1081 		    "ERROR: part-cache-max-size is specified but no part cache is in use\n");
1082 		tapebad = 1;
1083 	    }
1084 
1085 	    if (part_cache_max_size > part_size) {
1086 		g_fprintf(outf,
1087 		    "WARNING: part-cache-max-size is greater than part-size\n");
1088 	    }
1089 	}
1090 
1091 	tape_size = tapetype_get_length(tp);
1092 	if (part_size && part_size * 1000 < tape_size) {
1093 	    g_fprintf(outf,
1094 		      _("WARNING: part-size of %ju %sB < 0.1%% of tape length.\n"),
1095 		      (uintmax_t)part_size/(uintmax_t)unitdivisor, displayunit);
1096 	    if (!printed_small_part_size_warning) {
1097 		printed_small_part_size_warning = TRUE;
1098 		g_fprintf(outf, "%s", small_part_size_warning);
1099 	    }
1100 	} else if (part_cache_max_size && part_cache_max_size * 1000 < tape_size) {
1101 	    g_fprintf(outf,
1102 		      _("WARNING: part-cache-max-size of %ju %sB < 0.1%% of tape length.\n"),
1103 		      (uintmax_t)part_cache_max_size/(uintmax_t)unitdivisor, displayunit);
1104 	    if (!printed_small_part_size_warning) {
1105 		printed_small_part_size_warning = TRUE;
1106 		g_fprintf(outf, "%s", small_part_size_warning);
1107 	    }
1108 	}
1109     }
1110 
1111     /* check available disk space */
1112 
1113     if(do_localchk) {
1114 	identlist_t    il;
1115 	holdingdisk_t *hdp;
1116 
1117 	for (il = getconf_identlist(CNF_HOLDINGDISK);
1118 		il != NULL;
1119 		il = il->next) {
1120 	    hdp = lookup_holdingdisk(il->data);
1121 	    quoted = quote_string(holdingdisk_get_diskdir(hdp));
1122 	    if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
1123 		g_fprintf(outf, _("ERROR: holding dir %s (%s), "
1124 			"you must create a directory.\n"),
1125 			quoted, strerror(errno));
1126 		disklow = 1;
1127 		amfree(quoted);
1128 		continue;
1129 	    }
1130 
1131 	    /* do the division first to avoid potential integer overflow */
1132 	    if (fsusage.fsu_bavail_top_bit_set)
1133 		kb_avail = 0;
1134 	    else
1135 		kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
1136 
1137 	    if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
1138 		g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
1139 			quoted, strerror(errno));
1140 		g_fprintf(outf, _("Check permissions\n"));
1141 		disklow = 1;
1142 	    }
1143 	    else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
1144 		g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
1145 			quoted, strerror(errno));
1146 		g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
1147 		disklow = 1;
1148 	    }
1149 	    else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
1150 		if(kb_avail == 0) {
1151 		    g_fprintf(outf,
1152 			    _("WARNING: holding disk %s: "
1153 			    "no space available (%lld %sB requested)\n"), quoted,
1154 			    (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1155 			    displayunit);
1156 		    disklow = 1;
1157 		}
1158 		else if(kb_avail < holdingdisk_get_disksize(hdp)) {
1159 		    g_fprintf(outf,
1160 			    _("WARNING: holding disk %s: "
1161 			    "only %lld %sB available (%lld %sB requested)\n"), quoted,
1162 			    (long long)(kb_avail / (off_t)unitdivisor),
1163 			    displayunit,
1164 			    (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1165 			    displayunit);
1166 		    disklow = 1;
1167 		}
1168 		else {
1169 		    g_fprintf(outf,
1170 			    _("Holding disk %s: %lld %sB disk space available,"
1171 			    " using %lld %sB as requested\n"),
1172 			    quoted,
1173 			    (long long)(kb_avail / (off_t)unitdivisor),
1174 			    displayunit,
1175 			    (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
1176 			    displayunit);
1177 		}
1178 	    }
1179 	    else {
1180 		if(kb_avail < -holdingdisk_get_disksize(hdp)) {
1181 		    g_fprintf(outf,
1182 			    _("WARNING: holding disk %s: "
1183 			    "only %lld %sB free, using nothing\n"),
1184 			    quoted, (long long)(kb_avail / (off_t)unitdivisor),
1185 			    displayunit);
1186 	            g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
1187 		    disklow = 1;
1188 		}
1189 		else {
1190 		    g_fprintf(outf,
1191 			    _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
1192 			    quoted,
1193 			    (long long)(kb_avail/(off_t)unitdivisor),
1194 			    displayunit,
1195 			    (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
1196 			    displayunit);
1197 		}
1198 	    }
1199 	    amfree(quoted);
1200 	}
1201     }
1202 
1203     /* check that the log file is writable if it already exists */
1204 
1205     if(do_localchk) {
1206 	char *conf_logdir;
1207 	char *logfile;
1208 	char *olddir;
1209 	struct stat stat_old;
1210 	struct stat statbuf;
1211 
1212 	conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
1213 	logfile = vstralloc(conf_logdir, "/log", NULL);
1214 
1215 	quoted = quote_string(conf_logdir);
1216 	if(stat(conf_logdir, &statbuf) == -1) {
1217 	    g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
1218 		    quoted, strerror(errno));
1219 	    disklow = 1;
1220 	}
1221 	else if(access(conf_logdir, W_OK) == -1) {
1222 	    g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
1223 	    logbad = 1;
1224 	}
1225 	amfree(quoted);
1226 
1227 	if(logbad == 0 && access(logfile, F_OK) == 0) {
1228 	    testtape = 0;
1229 	    logbad = 2;
1230 	    if(access(logfile, W_OK) != 0) {
1231 		quoted = quote_string(logfile);
1232 		g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
1233 		amfree(quoted);
1234 	    }
1235 	}
1236 
1237 	olddir = vstralloc(conf_logdir, "/oldlog", NULL);
1238 	quoted = quote_string(olddir);
1239 	if (logbad == 0 && stat(olddir,&stat_old) == 0) { /* oldlog exist */
1240 	    if(!(S_ISDIR(stat_old.st_mode))) {
1241 		g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1242 			quoted);
1243 		g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1244 		logbad = 1;
1245 	    }
1246 	    if(logbad == 0 && access(olddir, W_OK) == -1) {
1247 		g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
1248 		g_fprintf(outf, _("Check permissions\n"));
1249 		logbad = 1;
1250 	    }
1251 	}
1252 	else if(logbad == 0 && lstat(olddir,&stat_old) == 0) {
1253 	    g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
1254 		    quoted);
1255 		g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1256 	    logbad = 1;
1257 	}
1258 	amfree(quoted);
1259 
1260 	if (logbad == 0 && testtape) {
1261 	    logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
1262 	    if (access(logfile, F_OK) == 0) {
1263 		testtape = 0;
1264 		logbad = 2;
1265 	    }
1266 	}
1267 
1268 	amfree(olddir);
1269 	amfree(logfile);
1270 	amfree(conf_logdir);
1271     }
1272 
1273     if (testtape) {
1274         tapebad = !test_tape_status(outf);
1275     } else if (do_tapechk) {
1276 	g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
1277 	g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
1278 	dev_amanda_data_path = TRUE;
1279 	dev_directtcp_data_path = TRUE;
1280     } else if (logbad == 2) {
1281 	g_fprintf(outf, _("NOTE: amdump or amflush seem to be running\n"));
1282 	g_fprintf(outf, _("NOTE: if they are not, you must run amcleanup\n"));
1283 
1284 	/* we skipped the tape checks, but this is just a NOTE and
1285 	 * should not result in a nonzero exit status, so reset logbad to 0 */
1286 	logbad = 0;
1287 	dev_amanda_data_path = TRUE;
1288 	dev_directtcp_data_path = TRUE;
1289     } else {
1290 	g_fprintf(outf, _("NOTE: skipping tape checks\n"));
1291 	dev_amanda_data_path = TRUE;
1292 	dev_directtcp_data_path = TRUE;
1293     }
1294 
1295     /*
1296      * See if the information file and index directory for each client
1297      * and disk is OK.  Since we may be seeing clients and/or disks for
1298      * the first time, these are just warnings, not errors.
1299      */
1300     if(do_localchk) {
1301 	char *conf_infofile;
1302 	char *conf_indexdir;
1303 	char *hostinfodir = NULL;
1304 	char *hostindexdir = NULL;
1305 	char *diskdir = NULL;
1306 	char *infofile = NULL;
1307 	struct stat statbuf;
1308 	disk_t *dp;
1309 	am_host_t *hostp;
1310 	int indexdir_checked = 0;
1311 	int hostindexdir_checked = 0;
1312 	char *host;
1313 	char *disk;
1314 	int conf_tapecycle;
1315 	int conf_runspercycle;
1316 	int conf_runtapes;
1317 	identlist_t pp_scriptlist;
1318 
1319 	conf_tapecycle = getconf_int(CNF_TAPECYCLE);
1320 	conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
1321 	conf_runtapes = getconf_int(CNF_RUNTAPES);
1322 
1323 	if (conf_tapecycle <= conf_runspercycle) {
1324 		g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
1325 			conf_tapecycle, conf_runspercycle);
1326 	}
1327 
1328 	if (conf_tapecycle <= conf_runtapes) {
1329 		g_fprintf(outf, _("WARNING: tapecycle (%d) <= runtapes (%d).\n"),
1330 			conf_tapecycle, conf_runtapes);
1331 	}
1332 
1333 	conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
1334 	conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
1335 
1336 	quoted = quote_string(conf_infofile);
1337 	if(stat(conf_infofile, &statbuf) == -1) {
1338 	    if (errno == ENOENT) {
1339 		g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
1340 			quoted);
1341 		g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1342 	    } else {
1343 		g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
1344 			quoted, strerror(errno));
1345 		infobad = 1;
1346 	    }
1347 	    amfree(conf_infofile);
1348 	} else if (!S_ISDIR(statbuf.st_mode)) {
1349 	    g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
1350 	    g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1351 	    amfree(conf_infofile);
1352 	    infobad = 1;
1353 	} else if (access(conf_infofile, W_OK) == -1) {
1354 	    g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1355 	    g_fprintf(outf, _("Check permissions\n"));
1356 	    amfree(conf_infofile);
1357 	    infobad = 1;
1358 	} else {
1359 	    char *errmsg = NULL;
1360 	    if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
1361 		g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
1362 		infobad = 1;
1363 		amfree(errmsg);
1364 	    }
1365 	    strappend(conf_infofile, "/");
1366 	}
1367 	amfree(quoted);
1368 
1369 	while(!empty(origq)) {
1370 	    hostp = origq.head->host;
1371 	    host = sanitise_filename(hostp->hostname);
1372 	    if(conf_infofile) {
1373 		hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
1374 		quoted = quote_string(hostinfodir);
1375 		if(stat(hostinfodir, &statbuf) == -1) {
1376 		    if (errno == ENOENT) {
1377 			g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
1378 				quoted);
1379 			g_fprintf(outf,
1380 				_("NOTE: it will be created on the next run.\n"));
1381 		    } else {
1382 			g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
1383 				quoted, strerror(errno));
1384 			infobad = 1;
1385 		    }
1386 		    amfree(hostinfodir);
1387 		} else if (!S_ISDIR(statbuf.st_mode)) {
1388 		    g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1389 			    quoted);
1390 		    g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1391 		    amfree(hostinfodir);
1392 		    infobad = 1;
1393 		} else if (access(hostinfodir, W_OK) == -1) {
1394 		    g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
1395 		    g_fprintf(outf, _("Check permissions\n"));
1396 		    amfree(hostinfodir);
1397 		    infobad = 1;
1398 		} else {
1399 		    strappend(hostinfodir, "/");
1400 		}
1401 		amfree(quoted);
1402 	    }
1403 	    for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1404 		disk = sanitise_filename(dp->name);
1405 		if(hostinfodir) {
1406 		    char *quotedif;
1407 
1408 		    diskdir = newstralloc2(diskdir, hostinfodir, disk);
1409 		    infofile = vstralloc(diskdir, "/", "info", NULL);
1410 		    quoted = quote_string(diskdir);
1411 		    quotedif = quote_string(infofile);
1412 		    if(stat(diskdir, &statbuf) == -1) {
1413 			if (errno == ENOENT) {
1414 			    g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
1415 				quoted);
1416 			    g_fprintf(outf,
1417 				_("NOTE: it will be created on the next run.\n"));
1418 			} else {
1419 			    g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1420 				    quoted, strerror(errno));
1421 			    infobad = 1;
1422 			}
1423 		    } else if (!S_ISDIR(statbuf.st_mode)) {
1424 			g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
1425 				quoted);
1426 			g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1427 			infobad = 1;
1428 		    } else if (access(diskdir, W_OK) == -1) {
1429 			g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
1430 				quoted);
1431 			g_fprintf(outf,_("Check permissions\n"));
1432 			infobad = 1;
1433 		    } else if(stat(infofile, &statbuf) == -1) {
1434 			if (errno == ENOENT) {
1435 			    g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
1436 				    quotedif);
1437 			    g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1438 			} else {
1439 			    g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
1440 				    quoted, strerror(errno));
1441 			    infobad = 1;
1442 			}
1443 		    } else if (!S_ISREG(statbuf.st_mode)) {
1444 			g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
1445 				quotedif);
1446 			g_fprintf(outf, _("Remove the entry and create a new file\n"));
1447 			infobad = 1;
1448 		    } else if (access(infofile, R_OK) == -1) {
1449 			g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
1450 				quotedif);
1451 			infobad = 1;
1452 		    }
1453 		    amfree(quotedif);
1454 		    amfree(quoted);
1455 		    amfree(infofile);
1456 		}
1457 		if(dp->index) {
1458 		    if(! indexdir_checked) {
1459 			quoted = quote_string(conf_indexdir);
1460 			if(stat(conf_indexdir, &statbuf) == -1) {
1461 			    if (errno == ENOENT) {
1462 				g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1463 				        quoted);
1464 				g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1465 			    } else {
1466 				g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1467 					quoted, strerror(errno));
1468 				indexbad = 1;
1469 			    }
1470 			    amfree(conf_indexdir);
1471 			} else if (!S_ISDIR(statbuf.st_mode)) {
1472 			    g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1473 				    quoted);
1474 			    g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1475 			    amfree(conf_indexdir);
1476 			    indexbad = 1;
1477 			} else if (access(conf_indexdir, W_OK) == -1) {
1478 			    g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1479 				    quoted);
1480 			    amfree(conf_indexdir);
1481 			    indexbad = 1;
1482 			} else {
1483 			    strappend(conf_indexdir, "/");
1484 			}
1485 			indexdir_checked = 1;
1486 			amfree(quoted);
1487 		    }
1488 		    if(conf_indexdir) {
1489 			if(! hostindexdir_checked) {
1490 			    hostindexdir = stralloc2(conf_indexdir, host);
1491 			    quoted = quote_string(hostindexdir);
1492 			    if(stat(hostindexdir, &statbuf) == -1) {
1493 				if (errno == ENOENT) {
1494 				    g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1495 				            quoted);
1496 				    g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1497 			        } else {
1498 				    g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1499 					    quoted, strerror(errno));
1500 				    indexbad = 1;
1501 				}
1502 			        amfree(hostindexdir);
1503 			    } else if (!S_ISDIR(statbuf.st_mode)) {
1504 			        g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1505 				        quoted);
1506 				g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1507 			        amfree(hostindexdir);
1508 			        indexbad = 1;
1509 			    } else if (access(hostindexdir, W_OK) == -1) {
1510 			        g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
1511 				        quoted);
1512 			        amfree(hostindexdir);
1513 			        indexbad = 1;
1514 			    } else {
1515 				strappend(hostindexdir, "/");
1516 			    }
1517 			    hostindexdir_checked = 1;
1518 			    amfree(quoted);
1519 			}
1520 			if(hostindexdir) {
1521 			    diskdir = newstralloc2(diskdir, hostindexdir, disk);
1522 			    quoted = quote_string(diskdir);
1523 			    if(stat(diskdir, &statbuf) == -1) {
1524 				if (errno == ENOENT) {
1525 				    g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
1526 					    quoted);
1527 				    g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
1528 				} else {
1529 				    g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
1530 					quoted, strerror(errno));
1531 				    indexbad = 1;
1532 				}
1533 			    } else if (!S_ISDIR(statbuf.st_mode)) {
1534 				g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
1535 					quoted);
1536 				g_fprintf(outf, _("Remove the entry and create a new directory\n"));
1537 				indexbad = 1;
1538 			    } else if (access(diskdir, W_OK) == -1) {
1539 				g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
1540 					quoted);
1541 				indexbad = 1;
1542 			    }
1543 			    amfree(quoted);
1544 			}
1545 		    }
1546 		}
1547 
1548 		if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
1549 		  if ( dp->srv_encrypt[0] == '\0' ) {
1550 		    g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
1551 		    g_fprintf(outf, _("Specify \"server-custom-encrypt\" in the dumptype\n"));
1552 		    pgmbad = 1;
1553 		  }
1554 		  else if(access(dp->srv_encrypt, X_OK) == -1) {
1555 		    g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
1556 			    dp->srv_encrypt );
1557 		   g_fprintf(outf, _("Check file type\n"));
1558 		    pgmbad = 1;
1559 		  }
1560 		}
1561 		if ( dp->compress == COMP_SERVER_CUST ) {
1562 		  if ( dp->srvcompprog[0] == '\0' ) {
1563 		    g_fprintf(outf, _("ERROR: server custom compression program "
1564 				    "not specified\n"));
1565 		    g_fprintf(outf, _("Specify \"server-custom-compress\" in "
1566 				    "the dumptype\n"));
1567 		    pgmbad = 1;
1568 		  }
1569 		  else if(access(dp->srvcompprog, X_OK) == -1) {
1570 		    quoted = quote_string(dp->srvcompprog);
1571 
1572 		    g_fprintf(outf, _("ERROR: %s is not executable, server custom "
1573 				    "compression will not work\n"),
1574 			    quoted);
1575 		    amfree(quoted);
1576 		   g_fprintf(outf, _("Check file type\n"));
1577 		    pgmbad = 1;
1578 		  }
1579 		}
1580 
1581 		/* check deprecated splitting parameters */
1582 		if (dumptype_seen(dp->config, DUMPTYPE_TAPE_SPLITSIZE)
1583 		    || dumptype_seen(dp->config, DUMPTYPE_SPLIT_DISKBUFFER)
1584 		    || dumptype_seen(dp->config, DUMPTYPE_FALLBACK_SPLITSIZE)) {
1585 		    tape_size = tapetype_get_length(tp);
1586 		    if (dp->tape_splitsize > tape_size) {
1587 			g_fprintf(outf,
1588 				  _("ERROR: %s %s: tape-splitsize > tape size\n"),
1589 				  hostp->hostname, dp->name);
1590 			pgmbad = 1;
1591 		    }
1592 		    if (dp->tape_splitsize && dp->fallback_splitsize * 1024 > physmem_total()) {
1593 			g_fprintf(outf,
1594 				  _("ERROR: %s %s: fallback-splitsize > total available memory\n"),
1595 				  hostp->hostname, dp->name);
1596 			pgmbad = 1;
1597 		    }
1598 		    if (dp->tape_splitsize && dp->fallback_splitsize > tape_size) {
1599 			g_fprintf(outf,
1600 				  _("ERROR: %s %s: fallback-splitsize > tape size\n"),
1601 				  hostp->hostname, dp->name);
1602 			pgmbad = 1;
1603 		    }
1604 
1605 		    /* also check for part sizes that are too small */
1606 		    if (dp->tape_splitsize && dp->tape_splitsize * 1000 < tape_size) {
1607 			g_fprintf(outf,
1608 				  _("WARNING: %s %s: tape-splitsize of %ju %sB < 0.1%% of tape length.\n"),
1609 				  hostp->hostname, dp->name,
1610 				  (uintmax_t)dp->tape_splitsize/(uintmax_t)unitdivisor,
1611 				  displayunit);
1612 			if (!printed_small_part_size_warning) {
1613 			    printed_small_part_size_warning = TRUE;
1614 			    g_fprintf(outf, "%s", small_part_size_warning);
1615 			}
1616 		    }
1617 
1618 		    /* fallback splitsize will be used if split_diskbuffer is empty or NULL */
1619 		    if (dp->tape_splitsize != 0 && dp->fallback_splitsize != 0 &&
1620 			    (dp->split_diskbuffer == NULL ||
1621 			     dp->split_diskbuffer[0] == '\0') &&
1622 			    dp->fallback_splitsize * 1000 < tape_size) {
1623 			g_fprintf(outf,
1624 			      _("WARNING: %s %s: fallback-splitsize of %ju %sB < 0.1%% of tape length.\n"),
1625 			      hostp->hostname, dp->name,
1626 			      (uintmax_t)dp->fallback_splitsize/(uintmax_t)unitdivisor,
1627 			      displayunit);
1628 			if (!printed_small_part_size_warning) {
1629 			    printed_small_part_size_warning = TRUE;
1630 			    g_fprintf(outf, "%s", small_part_size_warning);
1631 			}
1632 		    }
1633 		}
1634 
1635 		if (dp->data_path == DATA_PATH_DIRECTTCP) {
1636 		    if (dp->compress != COMP_NONE) {
1637 			g_fprintf(outf,
1638 				  _("ERROR: %s %s: Can't compress directtcp data-path\n"),
1639 				  hostp->hostname, dp->name);
1640 			pgmbad = 1;
1641 		    }
1642 		    if (dp->encrypt != ENCRYPT_NONE) {
1643 			g_fprintf(outf,
1644 				  _("ERROR: %s %s: Can't encrypt directtcp data-path\n"),
1645 				  hostp->hostname, dp->name);
1646 			pgmbad = 1;
1647 		    }
1648 		    if (dp->to_holdingdisk == HOLD_REQUIRED) {
1649 			g_fprintf(outf,
1650 				  _("ERROR: %s %s: Holding disk can't be use for directtcp data-path\n"),
1651 				  hostp->hostname, dp->name);
1652 			pgmbad = 1;
1653 		    }
1654 		}
1655 		if (dp->data_path == DATA_PATH_DIRECTTCP && !dev_directtcp_data_path) {
1656 		    g_fprintf(outf,
1657 			      _("ERROR: %s %s: data-path is DIRECTTCP but device do not support it\n"),
1658 			      hostp->hostname, dp->name);
1659 		    pgmbad = 1;
1660 		}
1661 		if (dp->data_path == DATA_PATH_AMANDA && !dev_amanda_data_path) {
1662 		    g_fprintf(outf,
1663 			      _("ERROR: %s %s: data-path is AMANDA but device do not support it\n"),
1664 			      hostp->hostname, dp->name);
1665 		    pgmbad = 1;
1666 		}
1667 
1668 		for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
1669 		     pp_scriptlist = pp_scriptlist->next) {
1670 		    pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
1671 		    g_assert(pp_script != NULL);
1672 		    if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1673 			pp_script_get_execute_on(pp_script) & EXECUTE_ON_PRE_HOST_BACKUP) {
1674 			g_fprintf(outf,
1675 				  _("ERROR: %s %s: Can't run pre-host-backup script on client\n"),
1676 				  hostp->hostname, dp->name);
1677 		    } else if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
1678 			pp_script_get_execute_on(pp_script) & EXECUTE_ON_POST_HOST_BACKUP) {
1679 			g_fprintf(outf,
1680 				  _("ERROR: %s %s: Can't run post-host-backup script on client\n"),
1681 				  hostp->hostname, dp->name);
1682 		    }
1683 		}
1684 
1685 		amfree(disk);
1686 		remove_disk(&origq, dp);
1687 	    }
1688 	    amfree(host);
1689 	    amfree(hostindexdir);
1690 	    hostindexdir_checked = 0;
1691 	}
1692 	amfree(diskdir);
1693 	amfree(hostinfodir);
1694 	amfree(conf_infofile);
1695 	amfree(conf_indexdir);
1696     }
1697 
1698     amfree(datestamp);
1699 
1700     g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
1701 
1702     fflush(outf);
1703     g_debug("userbad: %d", userbad);
1704     g_debug("confbad: %d", confbad);
1705     g_debug("tapebad: %d", tapebad);
1706     g_debug("disklow: %d", disklow);
1707     g_debug("logbad: %d", logbad);
1708     g_debug("infobad: %d", infobad);
1709     g_debug("indexbad: %d", indexbad);
1710     g_debug("pgmbad: %d", pgmbad);
1711 
1712     exit(userbad \
1713 	 || confbad \
1714 	 || tapebad \
1715 	 || disklow \
1716 	 || logbad \
1717 	 || infobad \
1718 	 || indexbad \
1719 	 || pgmbad);
1720     /*NOTREACHED*/
1721     return 0;
1722 }
1723 
1724 /* --------------------------------------------------- */
1725 
1726 int remote_errors;
1727 FILE *outf;
1728 
1729 static void handle_result(void *, pkt_t *, security_handle_t *);
1730 void start_host(am_host_t *hostp);
1731 
1732 #define HOST_READY				((void *)0)	/* must be 0 */
1733 #define HOST_ACTIVE				((void *)1)
1734 #define HOST_DONE				((void *)2)
1735 
1736 #define DISK_READY				((void *)0)	/* must be 0 */
1737 #define DISK_ACTIVE				((void *)1)
1738 #define DISK_DONE				((void *)2)
1739 
1740 void
start_host(am_host_t * hostp)1741 start_host(
1742     am_host_t *hostp)
1743 {
1744     disk_t *dp;
1745     char *req = NULL;
1746     size_t req_len = 0;
1747     int disk_count;
1748     const security_driver_t *secdrv;
1749     char number[NUM_STR_SIZE];
1750     estimate_t estimate;
1751 
1752     if(hostp->up != HOST_READY) {
1753 	return;
1754     }
1755 
1756     /*
1757      * The first time through here we send a "noop" request.  This will
1758      * return the feature list from the client if it supports that.
1759      * If it does not, handle_result() will set the feature list to an
1760      * empty structure.  In either case, we do the disks on the second
1761      * (and subsequent) pass(es).
1762      */
1763     disk_count = 0;
1764     if(hostp->features != NULL) { /* selfcheck service */
1765 	int has_features = am_has_feature(hostp->features,
1766 					  fe_req_options_features);
1767 	int has_hostname = am_has_feature(hostp->features,
1768 					  fe_req_options_hostname);
1769 	int has_maxdumps = am_has_feature(hostp->features,
1770 					  fe_req_options_maxdumps);
1771 	int has_config   = am_has_feature(hostp->features,
1772 					  fe_req_options_config);
1773 
1774 	if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
1775 	   !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1776 	    g_fprintf(outf,
1777 		    _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
1778 		    hostp->hostname);
1779 	    g_fprintf(outf, _("Client might be of a very old version\n"));
1780 	}
1781 	if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
1782 	    g_fprintf(outf,
1783 		    _("ERROR: Client %s does not support selfcheck REP packet.\n"),
1784 		    hostp->hostname);
1785 	    g_fprintf(outf, _("Client might be of a very old version\n"));
1786 	}
1787 	if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
1788 	   !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
1789 	   !am_has_feature(hostp->features, fe_sendsize_req_device)) {
1790 	    g_fprintf(outf,
1791 		    _("ERROR: Client %s does not support sendsize REQ packet.\n"),
1792 		    hostp->hostname);
1793 	    g_fprintf(outf, _("Client might be of a very old version\n"));
1794 	}
1795 	if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
1796 	    g_fprintf(outf,
1797 		    _("ERROR: Client %s does not support sendsize REP packet.\n"),
1798 		    hostp->hostname);
1799 	    g_fprintf(outf, _("Client might be of a very old version\n"));
1800 	}
1801 	if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
1802 	   !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1803 	    g_fprintf(outf,
1804 		   _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
1805 		   hostp->hostname);
1806 	    g_fprintf(outf, _("Client might be of a very old version\n"));
1807 	}
1808 	if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
1809 	    g_fprintf(outf,
1810 		   _("ERROR: Client %s does not support sendbackup REP packet.\n"),
1811 		   hostp->hostname);
1812 	    g_fprintf(outf, _("Client might be of a very old version\n"));
1813 	}
1814 
1815 	g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1816 	req = vstralloc("SERVICE ", "selfcheck", "\n",
1817 			"OPTIONS ",
1818 			has_features ? "features=" : "",
1819 			has_features ? our_feature_string : "",
1820 			has_features ? ";" : "",
1821 			has_maxdumps ? "maxdumps=" : "",
1822 			has_maxdumps ? number : "",
1823 			has_maxdumps ? ";" : "",
1824 			has_hostname ? "hostname=" : "",
1825 			has_hostname ? hostp->hostname : "",
1826 			has_hostname ? ";" : "",
1827 			has_config   ? "config=" : "",
1828 			has_config   ? get_config_name() : "",
1829 			has_config   ? ";" : "",
1830 			"\n",
1831 			NULL);
1832 
1833 	req_len = strlen(req);
1834 	req_len += 128;                         /* room for SECURITY ... */
1835 	req_len += 256;                         /* room for non-disk answers */
1836 	for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1837 	    char *l;
1838 	    char *es;
1839 	    size_t l_len;
1840 	    char *o = NULL;
1841 	    char *calcsize;
1842 	    char *qname, *b64disk;
1843 	    char *qdevice, *b64device = NULL;
1844 	    GPtrArray *errarray;
1845 	    guint      i;
1846 
1847 	    if(dp->up != DISK_READY || dp->todo != 1) {
1848 		continue;
1849 	    }
1850 	    qname = quote_string(dp->name);
1851 
1852 	    errarray = validate_optionstr(dp);
1853 	    if (errarray->len > 0) {
1854 		for (i=0; i < errarray->len; i++) {
1855 		    g_fprintf(outf, _("ERROR: %s:%s %s\n"),
1856 			      hostp->hostname, qname,
1857 			      (char *)g_ptr_array_index(errarray, i));
1858 		}
1859 		g_ptr_array_free(errarray, TRUE);
1860 		amfree(qname);
1861 		remote_errors++;
1862 		continue;
1863 	    } else  if (am_has_feature(hostp->features, fe_req_xml)) {
1864 		o = xml_optionstr(dp, 0);
1865 	    } else {
1866 		o = optionstr(dp);
1867 	    }
1868 
1869 	    b64disk = amxml_format_tag("disk", dp->name);
1870 	    qdevice = quote_string(dp->device);
1871 	    if (dp->device)
1872 		b64device = amxml_format_tag("diskdevice", dp->device);
1873 	    if ((dp->name && qname[0] == '"') ||
1874 		(dp->device && qdevice[0] == '"')) {
1875 		if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
1876 		    g_fprintf(outf,
1877 			    _("WARNING: %s:%s:%s host does not support quoted text\n"),
1878 			    hostp->hostname, qname, qdevice);
1879 		    g_fprintf(outf, _("You must upgrade amanda on the client to "
1880 				    "specify a quoted text/device in the disklist, "
1881 				    "or don't use quoted text for the device.\n"));
1882 		}
1883 	    }
1884 
1885 	    if(dp->device) {
1886 		if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
1887 		    g_fprintf(outf,
1888 		     _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
1889 		     hostp->hostname, qname, dp->device);
1890 		    g_fprintf(outf, _("You must upgrade amanda on the client to "
1891 				    "specify a diskdevice in the disklist "
1892 				    "or don't specify a diskdevice in the disklist.\n"));
1893 		}
1894 		if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
1895 		    g_fprintf(outf,
1896 		     _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
1897 		     hostp->hostname, qname, dp->device);
1898 		    g_fprintf(outf, _("You must upgrade amanda on the client to "
1899 				    "specify a diskdevice in the disklist"
1900 				    " or don't specify a diskdevice in the disklist.\n"));
1901 		}
1902 		if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
1903 		    g_fprintf(outf,
1904 		     _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
1905 		     hostp->hostname, qname, dp->device);
1906 		    g_fprintf(outf, _("You must upgrade amanda on the client to "
1907 				    "specify a diskdevice in the disklist"
1908 				    " or don't specify a diskdevice in the disklist.\n"));
1909 		}
1910 
1911 		if (dp->data_path != DATA_PATH_AMANDA &&
1912 		    !am_has_feature(hostp->features, fe_xml_data_path)) {
1913 		    g_fprintf(outf,
1914 			      _("ERROR: Client %s does not support %s data-path\n"),
1915 			      hostp->hostname,  data_path_to_string(dp->data_path));
1916 		} else if (dp->data_path == DATA_PATH_DIRECTTCP &&
1917 		    !am_has_feature(hostp->features, fe_xml_directtcp_list)) {
1918 		    g_fprintf(outf,
1919 			      _("ERROR: Client %s does not support directtcp data-path\n"),
1920 			      hostp->hostname);
1921 		}
1922 	    }
1923 	    if (dp->program &&
1924 	        (strcmp(dp->program,"DUMP") == 0 ||
1925 	         strcmp(dp->program,"GNUTAR") == 0)) {
1926 		if(strcmp(dp->program, "DUMP") == 0 &&
1927 		   !am_has_feature(hostp->features, fe_program_dump)) {
1928 		    g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
1929 			    hostp->hostname, qname);
1930 		    g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
1931 				    "or you can use another program.\n"));
1932 		}
1933 		if(strcmp(dp->program, "GNUTAR") == 0 &&
1934 		   !am_has_feature(hostp->features, fe_program_gnutar)) {
1935 		    g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
1936 			    hostp->hostname, qname);
1937 		    g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
1938 				    "or you can use another program.\n"));
1939 		}
1940 		estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
1941 		if(estimate == ES_CALCSIZE &&
1942 		   !am_has_feature(hostp->features, fe_calcsize_estimate)) {
1943 		    g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
1944 				    "estimate, using CLIENT.\n"),
1945 			    hostp->hostname, qname);
1946 		    g_fprintf(outf, _("You must upgrade amanda on the client to use "
1947 				    "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
1948 		    estimate = ES_CLIENT;
1949 		}
1950 		if(estimate == ES_CALCSIZE &&
1951 		   am_has_feature(hostp->features, fe_selfcheck_calcsize))
1952 		    calcsize = "CALCSIZE ";
1953 		else
1954 		    calcsize = "";
1955 
1956 		if(dp->compress == COMP_CUST &&
1957 		   !am_has_feature(hostp->features, fe_options_compress_cust)) {
1958 		  g_fprintf(outf,
1959 			  _("ERROR: Client %s does not support custom compression.\n"),
1960 			  hostp->hostname);
1961 		    g_fprintf(outf, _("You must upgrade amanda on the client to "
1962 				    "use custom compression\n"));
1963 		    g_fprintf(outf, _("Otherwise you can use the default client "
1964 				    "compression program.\n"));
1965 		}
1966 		if(dp->encrypt == ENCRYPT_CUST ) {
1967 		  if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
1968 		    g_fprintf(outf,
1969 			    _("ERROR: Client %s does not support data encryption.\n"),
1970 			    hostp->hostname);
1971 		    g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
1972 		    remote_errors++;
1973 		  } else if ( dp->compress == COMP_SERVER_FAST ||
1974 			      dp->compress == COMP_SERVER_BEST ||
1975 			      dp->compress == COMP_SERVER_CUST ) {
1976 		    g_fprintf(outf,
1977 			    _("ERROR: %s: Client encryption with server compression "
1978 			      "is not supported. See amanda.conf(5) for detail.\n"),
1979 			    hostp->hostname);
1980 		    remote_errors++;
1981 		  }
1982 		}
1983 		if (am_has_feature(hostp->features, fe_req_xml)) {
1984 		    l = vstralloc("<dle>\n"
1985 				  "  <program>",
1986 				  dp->program,
1987 				  "</program>\n", NULL);
1988 		    es = xml_estimate(dp->estimatelist, hostp->features);
1989 		    vstrextend(&l, es, "\n", NULL);
1990 		    amfree(es);
1991 		    vstrextend(&l, "  ", b64disk, "\n", NULL);
1992 		    if (dp->device)
1993 			vstrextend(&l, "  ", b64device, "\n", NULL);
1994 		    vstrextend(&l, o, "</dle>\n", NULL);
1995 		} else {
1996 		    if (dp->device) {
1997 			l = vstralloc(calcsize,
1998 				      dp->program, " ",
1999 				      qname, " ",
2000 				      qdevice,
2001 				      " 0 OPTIONS |",
2002 				      o,
2003 				      "\n",
2004 				      NULL);
2005 		    } else {
2006 			l = vstralloc(calcsize,
2007 				      dp->program, " ",
2008 				      qname,
2009 				      " 0 OPTIONS |",
2010 				      o,
2011 				      "\n",
2012 				      NULL);
2013 		    }
2014 		}
2015 	    } else {
2016 		if (!am_has_feature(hostp->features, fe_program_application_api) ||
2017 		    !am_has_feature(hostp->features, fe_req_xml)) {
2018 		    g_fprintf(outf, _("ERROR: %s:%s does not support APPLICATION-API.\n"),
2019 			    hostp->hostname, qname);
2020 		    g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
2021 				    " It is case sensitive\n"));
2022 		    remote_errors++;
2023 		    l = stralloc("");
2024 		} else {
2025 		    l = vstralloc("<dle>\n"
2026 				  "  <program>APPLICATION</program>\n", NULL);
2027 		    if (dp->application) {
2028 			application_t *application;
2029 			char          *xml_app;
2030 
2031 			application = lookup_application(dp->application);
2032 			if (!application) {
2033 			    g_fprintf(outf,
2034 			      _("ERROR: application '%s' not found.\n"), dp->application);
2035 			} else {
2036 			    char *client_name = application_get_client_name(application);
2037 			    if (client_name && strlen(client_name) > 0 &&
2038 				!am_has_feature(hostp->features, fe_application_client_name)) {
2039 				g_fprintf(outf,
2040 			      _("WARNING: %s:%s does not support client-name in application.\n"),
2041 			      hostp->hostname, qname);
2042 			    }
2043 			    xml_app = xml_application(dp, application, hostp->features);
2044 			    vstrextend(&l, xml_app, NULL);
2045 			    amfree(xml_app);
2046 			}
2047 		    }
2048 		    if (dp->pp_scriptlist) {
2049 			if (!am_has_feature(hostp->features, fe_pp_script)) {
2050 			    g_fprintf(outf,
2051 			      _("ERROR: %s:%s does not support SCRIPT-API.\n"),
2052 			      hostp->hostname, qname);
2053 			} else {
2054 			    identlist_t pp_scriptlist;
2055 			    for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
2056 				pp_scriptlist = pp_scriptlist->next) {
2057 				pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
2058 				char *client_name = pp_script_get_client_name(pp_script);;
2059 				if (client_name && strlen(client_name) > 0 &&
2060 				    !am_has_feature(hostp->features, fe_script_client_name)) {
2061 				    g_fprintf(outf,
2062 					_("WARNING: %s:%s does not support client-name in script.\n"),
2063 					hostp->hostname, dp->name);
2064 				}
2065 			    }
2066 			}
2067 		    }
2068 		    es = xml_estimate(dp->estimatelist, hostp->features);
2069 		    vstrextend(&l, es, "\n", NULL);
2070 		    amfree(es);
2071 		    vstrextend(&l, "  ", b64disk, "\n", NULL);
2072 		    if (dp->device)
2073 			vstrextend(&l, "  ", b64device, "\n", NULL);
2074 		    vstrextend(&l, o, "</dle>\n", NULL);
2075 		}
2076 	    }
2077 	    amfree(qname);
2078 	    amfree(qdevice);
2079 	    amfree(b64disk);
2080 	    amfree(b64device);
2081 	    l_len = strlen(l);
2082 	    amfree(o);
2083 
2084 	    strappend(req, l);
2085 	    req_len += l_len;
2086 	    amfree(l);
2087 	    dp->up = DISK_ACTIVE;
2088 	    disk_count++;
2089 	}
2090     }
2091     else { /* noop service */
2092 	req = vstralloc("SERVICE ", "noop", "\n",
2093 			"OPTIONS ",
2094 			"features=", our_feature_string, ";",
2095 			"\n",
2096 			NULL);
2097 	for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2098 	    if(dp->up != DISK_READY || dp->todo != 1) {
2099 		continue;
2100 	    }
2101 	    disk_count++;
2102 	}
2103     }
2104 
2105     if(disk_count == 0) {
2106 	amfree(req);
2107 	hostp->up = HOST_DONE;
2108 	return;
2109     }
2110 
2111     secdrv = security_getdriver(hostp->disks->auth);
2112     if (secdrv == NULL) {
2113 	fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
2114 	      hostp->disks->auth, hostp->hostname);
2115     } else {
2116 	protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf,
2117 			 req, conf_ctimeout, handle_result, hostp);
2118     }
2119 
2120     amfree(req);
2121 
2122     hostp->up = HOST_ACTIVE;
2123 }
2124 
2125 pid_t
start_client_checks(int fd)2126 start_client_checks(
2127     int		fd)
2128 {
2129     am_host_t *hostp;
2130     disk_t *dp, *dp1;
2131     int hostcount;
2132     pid_t pid;
2133     int userbad = 0;
2134 
2135     switch(pid = fork()) {
2136     case -1:
2137 	error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
2138 	/*NOTREACHED*/
2139 
2140     case 0:
2141 	break;
2142 
2143     default:
2144 	return pid;
2145     }
2146 
2147     dup2(fd, 1);
2148     dup2(fd, 2);
2149 
2150     set_pname("amcheck-clients");
2151 
2152     startclock();
2153 
2154     if((outf = fdopen(fd, "w")) == NULL) {
2155 	error(_("fdopen %d: %s"), fd, strerror(errno));
2156 	/*NOTREACHED*/
2157     }
2158     errf = outf;
2159 
2160     g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
2161     g_fprintf(outf,   "--------------------------------\n");
2162 
2163     hostcount = remote_errors = 0;
2164 
2165     if (!who_check_host_setting) {
2166 	remote_errors = check_host_setting(outf);
2167     }
2168 
2169     run_server_global_scripts(EXECUTE_ON_PRE_AMCHECK, get_config_name());
2170     protocol_init();
2171 
2172     for(dp = origq.head; dp != NULL; dp = dp->next) {
2173 	hostp = dp->host;
2174 	if(hostp->up == HOST_READY && dp->todo == 1) {
2175 	    run_server_host_scripts(EXECUTE_ON_PRE_HOST_AMCHECK,
2176 				    get_config_name(), hostp);
2177 	    for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
2178 		run_server_dle_scripts(EXECUTE_ON_PRE_DLE_AMCHECK,
2179 				   get_config_name(), dp1, -1);
2180 	    }
2181 	    start_host(hostp);
2182 	    hostcount++;
2183 	    protocol_check();
2184 	}
2185     }
2186 
2187     protocol_run();
2188     run_server_global_scripts(EXECUTE_ON_POST_AMCHECK, get_config_name());
2189 
2190     g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."),
2191 			 _("Client check: %d hosts checked in %s seconds."),
2192 			 hostcount),
2193 	    hostcount, walltime_str(curclock()));
2194     g_fprintf(outf, plural(_("  %d problem found.\n"),
2195 			 _("  %d problems found.\n"), remote_errors),
2196 	    remote_errors);
2197     fflush(outf);
2198 
2199     exit(userbad || remote_errors > 0);
2200     /*NOTREACHED*/
2201     return 0;
2202 }
2203 
2204 static void
handle_result(void * datap,pkt_t * pkt,security_handle_t * sech)2205 handle_result(
2206     void *		datap,
2207     pkt_t *		pkt,
2208     security_handle_t *	sech)
2209 {
2210     am_host_t *hostp;
2211     disk_t *dp;
2212     char *line;
2213     char *s;
2214     char *t;
2215     int ch;
2216     int tch;
2217     gboolean printed_hostname = FALSE;
2218 
2219     hostp = (am_host_t *)datap;
2220     hostp->up = HOST_READY;
2221 
2222     if (pkt == NULL) {
2223 	g_fprintf(outf,
2224 	    _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
2225 	    security_geterror(sech));
2226 	remote_errors++;
2227 	hostp->up = HOST_DONE;
2228 	return;
2229     }
2230 
2231 #if 0
2232     g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
2233 	    hostp->hostname, pkt->body);
2234 #endif
2235 
2236     s = pkt->body;
2237     ch = *s++;
2238     while(ch) {
2239 	line = s - 1;
2240 	skip_quoted_line(s, ch);
2241 	if (s[-2] == '\n') {
2242 	    s[-2] = '\0';
2243 	}
2244 
2245 	if(strncmp_const(line, "OPTIONS ") == 0) {
2246 
2247 	    t = strstr(line, "features=");
2248 	    if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
2249 		char *u = strchr(t, ';');
2250 		if (u)
2251 		   *u = '\0';
2252 		t += SIZEOF("features=")-1;
2253 		am_release_feature_set(hostp->features);
2254 		if((hostp->features = am_string_to_feature(t)) == NULL) {
2255 		    g_fprintf(outf, _("ERROR: %s: bad features value: '%s'\n"),
2256 			    hostp->hostname, t);
2257 		    g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
2258 		    remote_errors++;
2259 		    hostp->up = HOST_DONE;
2260 		}
2261 		if (u)
2262 		   *u = ';';
2263 	    }
2264 
2265 	    continue;
2266 	}
2267 
2268 	if (client_verbose && !printed_hostname) {
2269 	    g_fprintf(outf, "HOST %s\n", hostp->hostname);
2270 	    printed_hostname = TRUE;
2271 	}
2272 
2273 	if(strncmp_const(line, "OK ") == 0) {
2274 	    if (client_verbose) {
2275 		g_fprintf(outf, "%s\n", line);
2276 	    }
2277 	    continue;
2278 	}
2279 
2280 	t = line;
2281 	if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
2282 	    skip_whitespace(t, tch);
2283 	    /*
2284 	     * If the "error" is that the "noop" service is unknown, it
2285 	     * just means the client is "old" (does not support the service).
2286 	     * We can ignore this.
2287 	     */
2288 	    if(!((hostp->features == NULL) && (pkt->type == P_NAK)
2289 	       && ((strcmp(t - 1, "unknown service: noop") == 0)
2290 		   || (strcmp(t - 1, "noop: invalid service") == 0)))) {
2291 		g_fprintf(outf, _("ERROR: %s%s: %s\n"),
2292 			(pkt->type == P_NAK) ? "NAK " : "",
2293 			hostp->hostname,
2294 			t - 1);
2295 		remote_errors++;
2296 		hostp->up = HOST_DONE;
2297 	    }
2298 	    continue;
2299 	}
2300 
2301 	g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
2302 		hostp->hostname, line);
2303 	remote_errors++;
2304 	hostp->up = HOST_DONE;
2305     }
2306     if(hostp->up == HOST_READY && hostp->features == NULL) {
2307 	/*
2308 	 * The client does not support the features list, so give it an
2309 	 * empty one.
2310 	 */
2311 	dbprintf(_("no feature set from host %s\n"), hostp->hostname);
2312 	hostp->features = am_set_default_feature_set();
2313     }
2314     for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2315 	if(dp->up == DISK_ACTIVE) {
2316 	    dp->up = DISK_DONE;
2317 	}
2318     }
2319     start_host(hostp);
2320     if(hostp->up == HOST_DONE) {
2321 	security_close_connection(sech, hostp->hostname);
2322 	for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2323 	    run_server_dle_scripts(EXECUTE_ON_POST_DLE_AMCHECK,
2324 			       get_config_name(), dp, -1);
2325 	}
2326 	run_server_host_scripts(EXECUTE_ON_POST_HOST_AMCHECK,
2327 				get_config_name(), hostp);
2328     }
2329     /* try to clean up any defunct processes, since Amanda doesn't wait() for
2330        them explicitly */
2331     while(waitpid(-1, NULL, WNOHANG)> 0);
2332 }
2333 
2334 static int
check_host_setting(FILE * outf)2335 check_host_setting(
2336     FILE *outf)
2337 {
2338     am_host_t *p;
2339     disk_t *dp;
2340     int count = 0;
2341 
2342     for (p = get_hostlist(); p != NULL; p = p->next) {
2343 	for(dp = p->disks; dp != NULL; dp = dp->hostnext) {
2344 	    if (strcmp(dp->auth, p->disks->auth) != 0) {
2345 		g_fprintf(outf, "ERROR: Multiple DLE's for host '%s' use different auth methods\n",
2346 			  p->hostname);
2347 		g_fprintf(outf, "       Please ensure that all DLE's for the host use the same auth method, including skipped ones\n");
2348 		count++;
2349 		break;
2350 	    }
2351 	}
2352 
2353 	for(dp = p->disks; dp != NULL; dp = dp->hostnext) {
2354 	    if (dp->maxdumps != p->disks->maxdumps) {
2355 		g_fprintf(outf, "ERROR: Multiple DLE's for host '%s' use different maxdumps values\n",
2356 			  p->hostname);
2357 		g_fprintf(outf, "       Please ensure that all DLE's for the host use the same maxdumps value, including skipped ones\n");
2358 		count++;
2359 		break;
2360 	    }
2361 	}
2362     }
2363     return count;
2364 }
2365