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