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