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