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