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