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