Imported Upstream version 2.4.5
[debian/amanda] / client-src / selfcheck.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 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  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /* 
28  * $Id: selfcheck.c,v 1.40.2.3.4.4.2.22.2.2 2004/08/12 12:30:53 martinea Exp $
29  *
30  * do self-check and send back any error messages
31  */
32
33 #include "amanda.h"
34 #include "clock.h"
35 #include "statfs.h"
36 #include "version.h"
37 #include "getfsent.h"
38 #include "amandates.h"
39 #include "util.h"
40 #include "pipespawn.h"
41 #include "amfeatures.h"
42 #include "client_util.h"
43
44 #ifdef SAMBA_CLIENT
45 #include "findpass.h"
46 #endif
47
48 int need_samba=0;
49 int need_rundump=0;
50 int need_dump=0;
51 int need_restore=0;
52 int need_vdump=0;
53 int need_vrestore=0;
54 int need_xfsdump=0;
55 int need_xfsrestore=0;
56 int need_vxdump=0;
57 int need_vxrestore=0;
58 int need_runtar=0;
59 int need_gnutar=0;
60 int need_compress_path=0;
61 int need_calcsize=0;
62
63 static am_feature_t *our_features = NULL;
64 static char *our_feature_string = NULL;
65 static g_option_t *g_options = NULL;
66
67 /* local functions */
68 int main P((int argc, char **argv));
69
70 static void check_options P((char *program, char *calcprog, char *disk, char *device, option_t *options));
71 static void check_disk P((char *program, char *calcprog, char *disk, char *amdevice, int level));
72 static void check_overall P((void));
73 static void check_access P((char *filename, int mode));
74 static void check_file P((char *filename, int mode));
75 static void check_dir P((char *dirname, int mode));
76 static void check_suid P((char *filename));
77 static void check_space P((char *dir, long kbytes));
78
79 int main(argc, argv)
80 int argc;
81 char **argv;
82 {
83     int level;
84     char *line = NULL;
85     char *program = NULL;
86     char *calcprog = NULL;
87     char *disk = NULL;
88     char *device = NULL;
89     char *optstr = NULL;
90     char *err_extra = NULL;
91     char *s, *fp;
92     int ch;
93     int fd;
94     unsigned long malloc_hist_1, malloc_size_1;
95     unsigned long malloc_hist_2, malloc_size_2;
96     option_t *options;
97
98     /* initialize */
99
100     for(fd = 3; fd < FD_SETSIZE; fd++) {
101         /*
102          * Make sure nobody spoofs us with a lot of extra open files
103          * that would cause an open we do to get a very high file
104          * descriptor, which in turn might be used as an index into
105          * an array (e.g. an fd_set).
106          */
107         close(fd);
108     }
109
110     safe_cd();
111
112     set_pname("selfcheck");
113
114     malloc_size_1 = malloc_inuse(&malloc_hist_1);
115
116     erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
117     dbopen();
118     startclock();
119     dbprintf(("%s: version %s\n", argv[0], version()));
120
121     our_features = am_init_feature_set();
122     our_feature_string = am_feature_to_string(our_features);
123
124     /* handle all service requests */
125
126     for(; (line = agets(stdin)) != NULL; free(line)) {
127 #define sc "OPTIONS "
128         if(strncmp(line, sc, sizeof(sc)-1) == 0) {
129 #undef sc
130             g_options = parse_g_options(line+8, 1);
131             if(!g_options->hostname) {
132                 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
133                 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
134                 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
135             }
136
137             printf("OPTIONS ");
138             if(am_has_feature(g_options->features, fe_rep_options_features)) {
139                 printf("features=%s;", our_feature_string);
140             }
141             if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
142                 printf("hostname=%s;", g_options->hostname);
143             }
144             printf("\n");
145             fflush(stdout);
146             continue;
147         }
148
149         s = line;
150         ch = *s++;
151
152         skip_whitespace(s, ch);                 /* find program name */
153         if (ch == '\0') {
154             goto err;                           /* no program */
155         }
156         program = s - 1;
157         skip_non_whitespace(s, ch);
158         s[-1] = '\0';                           /* terminate the program name */
159
160         if(strncmp(program, "CALCSIZE", 8) == 0) {
161             skip_whitespace(s, ch);             /* find program name */
162             if (ch == '\0') {
163                 goto err;                       /* no program */
164             }
165             calcprog = s - 1;
166             skip_non_whitespace(s, ch);
167             s[-1] = '\0';
168         }
169         else {
170             calcprog = NULL;
171         }
172
173         skip_whitespace(s, ch);                 /* find disk name */
174         if (ch == '\0') {
175             goto err;                           /* no disk */
176         }
177         disk = s - 1;
178         skip_non_whitespace(s, ch);
179         s[-1] = '\0';                           /* terminate the disk name */
180
181         skip_whitespace(s, ch);                 /* find the device or level */
182         if (ch == '\0') {
183             goto err;                           /* no device or level */
184         }
185         if(!isdigit((int)s[-1])) {
186             fp = s - 1;
187             skip_non_whitespace(s, ch);
188             s[-1] = '\0';                       /* terminate the device */
189             device = stralloc(fp);
190             skip_whitespace(s, ch);             /* find level number */
191         }
192         else {
193             device = stralloc(disk);
194         }
195
196                                                 /* find level number */
197         if (ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
198             goto err;                           /* bad level */
199         }
200         skip_integer(s, ch);
201
202         skip_whitespace(s, ch);
203 #define sc "OPTIONS "
204         if (ch && strncmp (s - 1, sc, sizeof(sc)-1) == 0) {
205             s += sizeof(sc)-1;
206             ch = s[-1];
207 #undef sc
208             skip_whitespace(s, ch);             /* find the option string */
209             if(ch == '\0') {
210                 goto err;                       /* bad options string */
211             }
212             optstr = s - 1;
213             skip_non_whitespace(s, ch);
214             s[-1] = '\0';                       /* terminate the options */
215             options = parse_options(optstr, disk, device, g_options->features, 1);
216             check_options(program, calcprog, disk, device, options);
217             check_disk(program, calcprog, disk, device, level);
218             free_sl(options->exclude_file);
219             free_sl(options->exclude_list);
220             free_sl(options->include_file);
221             free_sl(options->include_list);
222             amfree(options->str);
223             amfree(options);
224         } else if (ch == '\0') {
225             /* check all since no option */
226             need_samba=1;
227             need_rundump=1;
228             need_dump=1;
229             need_restore=1;
230             need_vdump=1;
231             need_vrestore=1;
232             need_xfsdump=1;
233             need_xfsrestore=1;
234             need_vxdump=1;
235             need_vxrestore=1;
236             need_runtar=1;
237             need_gnutar=1;
238             need_compress_path=1;
239             need_calcsize=1;
240             check_disk(program, calcprog, disk, device, level);
241         } else {
242             goto err;                           /* bad syntax */
243         }
244         amfree(device);
245     }
246
247     check_overall();
248
249     amfree(line);
250     amfree(our_feature_string);
251     am_release_feature_set(our_features);
252     our_features = NULL;
253     am_release_feature_set(g_options->features);
254     g_options->features = NULL;
255     amfree(g_options->str);
256     amfree(g_options->hostname);
257     amfree(g_options);
258
259     malloc_size_2 = malloc_inuse(&malloc_hist_2);
260
261     if(malloc_size_1 != malloc_size_2) {
262 #if defined(USE_DBMALLOC)
263         extern int dbfd;
264
265         malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
266 #endif
267     }
268
269     dbclose();
270     return 0;
271
272  err:
273     printf("ERROR [BOGUS REQUEST PACKET]\n");
274     dbprintf(("%s: REQ packet is bogus%s%s\n",
275               debug_prefix_time(NULL),
276               err_extra ? ": " : "",
277               err_extra ? err_extra : ""));
278     dbclose();
279     return 1;
280 }
281
282
283 static void
284 check_options(program, calcprog, disk, device, options)
285     char *program, *calcprog, *disk, *device;
286     option_t *options;
287 {
288     char *myprogram = program;
289
290     if(strcmp(myprogram,"CALCSIZE") == 0) {
291         int nb_exclude = 0;
292         int nb_include = 0;
293         char *file_exclude = NULL;
294         char *file_include = NULL;
295
296         if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
297         if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
298         if(options->include_file) nb_include += options->include_file->nb_element;
299         if(options->include_list) nb_include += options->include_list->nb_element;
300
301         if(nb_exclude > 0) file_exclude = build_exclude(disk, device, options, 1);
302         if(nb_include > 0) file_include = build_include(disk, device, options, 1);
303
304         amfree(file_exclude);
305         amfree(file_include);
306
307         need_calcsize=1;
308         myprogram = calcprog;
309     }
310
311     if(strcmp(myprogram,"GNUTAR") == 0) {
312         need_gnutar=1;
313         if(disk[0] == '/' && disk[1] == '/') {
314             if(options->exclude_file && options->exclude_file->nb_element > 1) {
315                 printf("ERROR [samba support only one exclude file]\n");
316             }
317             if(options->exclude_list && options->exclude_list->nb_element > 0 &&
318                options->exclude_optional==0) {
319                 printf("ERROR [samba does not support exclude list]\n");
320             }
321             if(options->include_file && options->include_file->nb_element > 0) {
322                 printf("ERROR [samba does not support include file]\n");
323             }
324             if(options->include_list && options->include_list->nb_element > 0 &&
325                options->include_optional==0) {
326                 printf("ERROR [samba does not support include list]\n");
327             }
328             need_samba=1;
329         }
330         else {
331             int nb_exclude = 0;
332             int nb_include = 0;
333             char *file_exclude = NULL;
334             char *file_include = NULL;
335
336             if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
337             if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
338             if(options->include_file) nb_include += options->include_file->nb_element;
339             if(options->include_list) nb_include += options->include_list->nb_element;
340
341             if(nb_exclude > 0) file_exclude = build_exclude(disk, device, options, 1);
342             if(nb_include > 0) file_include = build_include(disk, device, options, 1);
343
344             amfree(file_exclude);
345             amfree(file_include);
346
347             need_runtar=1;
348         }
349     }
350
351     if(strcmp(myprogram,"DUMP") == 0) {
352         if(options->exclude_file && options->exclude_file->nb_element > 0) {
353             printf("ERROR [DUMP does not support exclude file]\n");
354         }
355         if(options->exclude_list && options->exclude_list->nb_element > 0) {
356             printf("ERROR [DUMP does not support exclude list]\n");
357         }
358         if(options->include_file && options->include_file->nb_element > 0) {
359             printf("ERROR [DUMP does not support include file]\n");
360         }
361         if(options->include_list && options->include_list->nb_element > 0) {
362             printf("ERROR [DUMP does not support include list]\n");
363         }
364 #ifdef USE_RUNDUMP
365         need_rundump=1;
366 #endif
367 #ifndef AIX_BACKUP
368 #ifdef VDUMP
369 #ifdef DUMP
370         if (strcmp(amname_to_fstype(disk), "advfs") == 0)
371 #else
372         if (1)
373 #endif
374         {
375             need_vdump=1;
376             need_rundump=1;
377             if (options->createindex)
378                 need_vrestore=1;
379         }
380         else
381 #endif /* VDUMP */
382 #ifdef XFSDUMP
383 #ifdef DUMP
384         if (strcmp(amname_to_fstype(disk), "xfs") == 0)
385 #else
386         if (1)
387 #endif
388         {
389             need_xfsdump=1;
390             need_rundump=1;
391             if (options->createindex)
392                 need_xfsrestore=1;
393         }
394         else
395 #endif /* XFSDUMP */
396 #ifdef VXDUMP
397 #ifdef DUMP
398         if (strcmp(amname_to_fstype(disk), "vxfs") == 0)
399 #else
400         if (1)
401 #endif
402         {
403             need_vxdump=1;
404             if (options->createindex)
405                 need_vxrestore=1;
406         }
407         else
408 #endif /* VXDUMP */
409         {
410             need_dump=1;
411             if (options->createindex)
412                 need_restore=1;
413         }
414 #else
415         /* AIX backup program */
416         need_dump=1;
417         if (options->createindex)
418             need_restore=1;
419 #endif
420     }
421     if(options->compress == COMPR_BEST || options->compress == COMPR_FAST) 
422         need_compress_path=1;
423 }
424
425 static void check_disk(program, calcprog, disk, amdevice, level)
426 char *program, *calcprog, *disk, *amdevice;
427 int level;
428 {
429     char *device = NULL;
430     char *err = NULL;
431     char *user_and_password = NULL, *domain = NULL;
432     char *share = NULL, *subdir = NULL;
433     int lpass = 0;
434     int amode;
435     int access_result;
436     char *access_type;
437     char *extra_info = NULL;
438     char *myprogram = program;
439
440     if(strcmp(myprogram,"CALCSIZE") == 0) {
441         if(amdevice[0] == '/' && amdevice[1] == '/') {
442             err = vstralloc("Can't use CALCSIZE for samba estimate,",
443                             " use CLIENT: ",
444                             amdevice,
445                             NULL);
446             goto common_exit;
447         }
448         myprogram = calcprog;
449     }
450
451     dbprintf(("%s: checking disk %s\n", debug_prefix_time(NULL), disk));
452
453     if (strcmp(myprogram, "GNUTAR") == 0) {
454         if(amdevice[0] == '/' && amdevice[1] == '/') {
455 #ifdef SAMBA_CLIENT
456             int nullfd, checkerr;
457             int passwdfd;
458             char *pwtext;
459             int pwtext_len;
460             int checkpid;
461             amwait_t retstat;
462             char number[NUM_STR_SIZE];
463             int wpid;
464             int ret, sig, rc;
465             char *line;
466             char *sep;
467             FILE *ferr;
468             char *pw_fd_env;
469             int errdos;
470
471             parsesharename(amdevice, &share, &subdir);
472             if (!share) {
473                 err = stralloc2("cannot parse for share/subdir disk entry ", amdevice);
474                 goto common_exit;
475             }
476             if ((subdir) && (SAMBA_VERSION < 2)) {
477                 err = vstralloc("subdirectory specified for share '",
478                                 amdevice,
479                                 "' but samba not v2 or better",
480                                 NULL);
481                 goto common_exit;
482             }
483             if ((user_and_password = findpass(share, &domain)) == NULL) {
484                 err = stralloc2("cannot find password for ", amdevice);
485                 goto common_exit;
486             }
487             lpass = strlen(user_and_password);
488             if ((pwtext = strchr(user_and_password, '%')) == NULL) {
489                 err = stralloc2("password field not \'user%pass\' for ", amdevice);
490                 goto common_exit;
491             }
492             *pwtext++ = '\0';
493             pwtext_len = strlen(pwtext);
494             if ((device = makesharename(share, 0)) == NULL) {
495                 err = stralloc2("cannot make share name of ", share);
496                 goto common_exit;
497             }
498
499             nullfd = open("/dev/null", O_RDWR);
500             if (pwtext_len > 0) {
501                 pw_fd_env = "PASSWD_FD";
502             } else {
503                 pw_fd_env = "dummy_PASSWD_FD";
504             }
505             checkpid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
506                                  &nullfd, &nullfd, &checkerr,
507                                  pw_fd_env, &passwdfd,
508                                  "smbclient",
509                                  device,
510                                  *user_and_password ? "-U" : skip_argument,
511                                  *user_and_password ? user_and_password : skip_argument,
512                                  "-E",
513                                  domain ? "-W" : skip_argument,
514                                  domain ? domain : skip_argument,
515 #if SAMBA_VERSION >= 2
516                                  subdir ? "-D" : skip_argument,
517                                  subdir ? subdir : skip_argument,
518 #endif
519                                  "-c", "quit",
520                                  NULL);
521             if (domain) {
522                 memset(domain, '\0', strlen(domain));
523                 amfree(domain);
524             }
525             aclose(nullfd);
526             if (pwtext_len > 0 && fullwrite(passwdfd, pwtext, pwtext_len) < 0) {
527                 err = vstralloc("password write failed: ",
528                                 amdevice,
529                                 ": ",
530                                 strerror(errno),
531                                 NULL);
532                 aclose(passwdfd);
533                 goto common_exit;
534             }
535             memset(user_and_password, '\0', lpass);
536             amfree(user_and_password);
537             aclose(passwdfd);
538             ferr = fdopen(checkerr, "r");
539             sep = "";
540             errdos = 0;
541             for(sep = ""; (line = agets(ferr)) != NULL; free(line)) {
542                 strappend(extra_info, sep);
543                 strappend(extra_info, line);
544                 sep = ": ";
545                 if(strstr(line, "ERRDOS") != NULL) {
546                     errdos = 1;
547                 }
548             }
549             afclose(ferr);
550             checkerr = -1;
551             rc = 0;
552             while ((wpid = wait(&retstat)) != -1) {
553                 if (WIFSIGNALED(retstat)) {
554                     ret = 0;
555                     rc = sig = WTERMSIG(retstat);
556                 } else {
557                     sig = 0;
558                     rc = ret = WEXITSTATUS(retstat);
559                 }
560                 if (rc != 0) {
561                     strappend(err, sep);
562                     if (ret == 0) {
563                         strappend(err, "got signal ");
564                         ret = sig;
565                     } else {
566                         strappend(err, "returned ");
567                     }
568                     ap_snprintf(number, sizeof(number), "%d", ret);
569                     strappend(err, number);
570                 }
571             }
572             if (errdos != 0 || rc != 0) {
573                 err = newvstralloc(err,
574                                    "samba access error: ",
575                                    amdevice,
576                                    ": ",
577                                    extra_info ? extra_info : "",
578                                    err,
579                                    NULL);
580                 amfree(extra_info);
581             }
582 #else
583             err = stralloc2("This client is not configured for samba: ", amdevice);
584 #endif
585             goto common_exit;
586         }
587         amode = F_OK;
588         device = amname_to_dirname(amdevice);
589     } else {
590         if(amdevice[0] == '/' && amdevice[1] == '/') {
591             err = vstralloc("The DUMP program cannot handle samba shares,",
592                             " use GNUTAR: ",
593                             amdevice,
594                             NULL);
595             goto common_exit;
596         }
597 #ifdef VDUMP                                                            /* { */
598 #ifdef DUMP                                                             /* { */
599         if (strcmp(amname_to_fstype(amdevice), "advfs") == 0)
600 #else                                                                   /* }{ */
601         if (1)
602 #endif                                                                  /* } */
603         {
604             device = amname_to_dirname(amdevice);
605             amode = F_OK;
606         } else
607 #endif                                                                  /* } */
608         {
609             device = amname_to_devname(amdevice);
610 #ifdef USE_RUNDUMP
611             amode = F_OK;
612 #else
613             amode = R_OK;
614 #endif
615         }
616     }
617
618     dbprintf(("%s: device %s\n", debug_prefix_time(NULL), device));
619
620     /* skip accessability test if this is an AFS entry */
621     if(strncmp(device, "afs:", 4) != 0) {
622 #ifdef CHECK_FOR_ACCESS_WITH_OPEN
623         access_result = open(device, O_RDONLY);
624         access_type = "open";
625 #else
626         access_result = access(device, amode);
627         access_type = "access";
628 #endif
629         if(access_result == -1) {
630             err = vstralloc("could not ", access_type, " ", device,
631                         " (", disk, "): ", strerror(errno), NULL);
632         }
633 #ifdef CHECK_FOR_ACCESS_WITH_OPEN
634         aclose(access_result);
635 #endif
636     }
637
638 common_exit:
639
640     amfree(share);
641     amfree(subdir);
642     if(user_and_password) {
643         memset(user_and_password, '\0', lpass);
644         amfree(user_and_password);
645     }
646     if(domain) {
647         memset(domain, '\0', strlen(domain));
648         amfree(domain);
649     }
650
651     if(err) {
652         printf("ERROR [%s]\n", err);
653         dbprintf(("%s: %s\n", debug_prefix_time(NULL), err));
654         amfree(err);
655     } else {
656         printf("OK %s\n", disk);
657         dbprintf(("%s: disk \"%s\" OK\n", debug_prefix_time(NULL), disk));
658         printf("OK %s\n", amdevice);
659         dbprintf(("%s: amdevice \"%s\" OK\n",
660                   debug_prefix_time(NULL), amdevice));
661         printf("OK %s\n", device);
662         dbprintf(("%s: device \"%s\" OK\n", debug_prefix_time(NULL), device));
663     }
664     if(extra_info) {
665         dbprintf(("%s: extra info: %s\n", debug_prefix_time(NULL), extra_info));
666         amfree(extra_info);
667     }
668     amfree(device);
669
670     /* XXX perhaps do something with level: read dumpdates and sanity check */
671 }
672
673 static void check_overall()
674 {
675     char *cmd;
676     struct stat buf;
677     int testfd;
678
679     if( need_runtar )
680     {
681         cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
682         check_file(cmd,X_OK);
683         check_suid(cmd);
684         amfree(cmd);
685     }
686
687     if( need_rundump )
688     {
689         cmd = vstralloc(libexecdir, "/", "rundump", versionsuffix(), NULL);
690         check_file(cmd,X_OK);
691         check_suid(cmd);
692         amfree(cmd);
693     }
694
695     if( need_dump ) {
696 #ifdef DUMP
697         check_file(DUMP, X_OK);
698 #else
699         printf("ERROR [DUMP program not available]\n");
700 #endif
701     }
702
703     if( need_restore ) {
704 #ifdef RESTORE
705         check_file(RESTORE, X_OK);
706 #else
707         printf("ERROR [RESTORE program not available]\n");
708 #endif
709     }
710
711     if ( need_vdump ) {
712 #ifdef VDUMP
713         check_file(VDUMP, X_OK);
714 #else
715         printf("ERROR [VDUMP program not available]\n");
716 #endif
717     }
718
719     if ( need_vrestore ) {
720 #ifdef VRESTORE
721         check_file(VRESTORE, X_OK);
722 #else
723         printf("ERROR [VRESTORE program not available]\n");
724 #endif
725     }
726
727     if( need_xfsdump ) {
728 #ifdef XFSDUMP
729         check_file(XFSDUMP, F_OK);
730 #else
731         printf("ERROR [XFSDUMP program not available]\n");
732 #endif
733     }
734
735     if( need_xfsrestore ) {
736 #ifdef XFSRESTORE
737         check_file(XFSRESTORE, X_OK);
738 #else
739         printf("ERROR [XFSRESTORE program not available]\n");
740 #endif
741     }
742
743     if( need_vxdump ) {
744 #ifdef VXDUMP
745         check_file(VXDUMP, X_OK);
746 #else
747         printf("ERROR [VXDUMP program not available]\n");
748 #endif
749     }
750
751     if( need_vxrestore ) {
752 #ifdef VXRESTORE
753         check_file(VXRESTORE, X_OK);
754 #else
755         printf("ERROR [VXRESTORE program not available]\n");
756 #endif
757     }
758
759     if( need_gnutar ) {
760 #ifdef GNUTAR
761         check_file(GNUTAR, X_OK);
762 #else
763         printf("ERROR [GNUTAR program not available]\n");
764 #endif
765 #ifdef AMANDATES_FILE
766         check_file(AMANDATES_FILE, R_OK|W_OK);
767 #endif
768 #ifdef GNUTAR_LISTED_INCREMENTAL_DIR
769         check_dir(GNUTAR_LISTED_INCREMENTAL_DIR,R_OK|W_OK);
770 #endif
771     }
772
773     if( need_calcsize ) {
774         char *cmd;
775
776         cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
777
778         check_file(cmd, X_OK);
779
780         amfree(cmd);
781     }
782
783     if( need_samba ) {
784 #ifdef SAMBA_CLIENT
785         check_file(SAMBA_CLIENT, X_OK);
786 #else
787         printf("ERROR [SMBCLIENT program not available]\n");
788 #endif
789         testfd = open("/etc/amandapass", R_OK);
790         if (testfd >= 0) {
791             if(fstat(testfd, &buf) == 0) {
792                 if ((buf.st_mode & 0x7) != 0) {
793                     printf("ERROR [/etc/amandapass is world readable!]\n");
794                 } else {
795                     printf("OK [/etc/amandapass is readable, but not by all]\n");
796                 }
797             } else {
798                 printf("OK [unable to stat /etc/amandapass: %s]\n",
799                        strerror(errno));
800             }
801             aclose(testfd);
802         } else {
803             printf("ERROR [unable to open /etc/amandapass: %s]\n",
804                    strerror(errno));
805         }
806     }
807
808     if( need_compress_path ) {
809         check_file(COMPRESS_PATH, X_OK);
810     }
811
812     if( need_dump || need_xfsdump )
813         check_file("/etc/dumpdates",
814 #ifdef USE_RUNDUMP
815                    F_OK
816 #else
817                    R_OK|W_OK
818 #endif
819                    );
820
821     if (need_vdump) {
822         check_file("/etc/vdumpdates", F_OK);
823     }
824
825     check_access("/dev/null", R_OK|W_OK);
826     check_space(AMANDA_TMPDIR, 64);     /* for amandad i/o */
827
828 #ifdef AMANDA_DBGDIR
829     check_space(AMANDA_DBGDIR, 64);     /* for amandad i/o */
830 #endif
831
832     check_space("/etc", 64);            /* for /etc/dumpdates writing */
833 }
834
835 static void check_space(dir, kbytes)
836 char *dir;
837 long kbytes;
838 {
839     generic_fs_stats_t statp;
840
841     if(get_fs_stats(dir, &statp) == -1)
842         printf("ERROR [cannot statfs %s: %s]\n", dir, strerror(errno));
843     else if(statp.avail < kbytes)
844         printf("ERROR [dir %s needs %ldKB, only has %ldKB available.]\n",
845                dir, kbytes, statp.avail);
846     else
847         printf("OK %s has more than %ld KB available.\n", dir, kbytes);
848 }
849
850 static void check_access(filename, mode)
851 char *filename;
852 int mode;
853 {
854     char *noun, *adjective;
855
856     if(mode == F_OK)
857         noun = "find", adjective = "exists";
858     else if((mode & X_OK) == X_OK)
859         noun = "execute", adjective = "executable";
860     else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
861         noun = "read/write", adjective = "read/writable";
862     else 
863         noun = "access", adjective = "accessible";
864
865     if(access(filename, mode) == -1)
866         printf("ERROR [can not %s %s: %s]\n", noun, filename, strerror(errno));
867     else
868         printf("OK %s %s\n", filename, adjective);
869 }
870
871 static void check_file(filename, mode)
872 char *filename;
873 int mode;
874 {
875     struct stat stat_buf;
876     if(!stat(filename, &stat_buf)) {
877         if(!S_ISREG(stat_buf.st_mode)) {
878             printf("ERROR [%s is not a file]\n", filename);
879         }
880     }
881     check_access(filename, mode);
882 }
883
884 static void check_dir(dirname, mode)
885 char *dirname;
886 int mode;
887 {
888     struct stat stat_buf;
889     char *dir;
890
891     if(!stat(dirname, &stat_buf)) {
892         if(!S_ISDIR(stat_buf.st_mode)) {
893             printf("ERROR [%s is not a directory]\n", dirname);
894         }
895     }
896     dir = stralloc2(dirname, "/.");
897     check_access(dir, mode);
898     amfree(dir);
899 }
900
901 static void check_suid(filename)
902 char *filename;
903 {
904 /* The following is only valid for real Unixs */
905 #ifndef IGNORE_UID_CHECK
906     struct stat stat_buf;
907     if(!stat(filename, &stat_buf)) {
908         if(stat_buf.st_uid != 0 ) {
909             printf("ERROR [%s is not owned by root]\n",filename);
910         }
911         if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
912             printf("ERROR [%s is not SUID root]\n",filename);
913         }
914     }
915     else {
916         printf("ERROR [can not stat %s]\n",filename);
917     }
918 #endif
919 }