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