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