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