Imported Upstream version 2.5.2p1
[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 "conffile.h"
44 #include "amandad.h"
45
46 #ifdef SAMBA_CLIENT
47 #include "findpass.h"
48 #endif
49
50 #define selfcheck_debug(i,x) do {       \
51         if ((i) <= debug_selfcheck) {   \
52             dbprintf(x);                \
53         }                               \
54 } while (0)
55
56 int need_samba=0;
57 int need_rundump=0;
58 int need_dump=0;
59 int need_restore=0;
60 int need_vdump=0;
61 int need_vrestore=0;
62 int need_xfsdump=0;
63 int need_xfsrestore=0;
64 int need_vxdump=0;
65 int need_vxrestore=0;
66 int need_runtar=0;
67 int need_gnutar=0;
68 int need_compress_path=0;
69 int need_calcsize=0;
70 int program_is_backup_api=0;
71
72 static char *amandad_auth = NULL;
73 static am_feature_t *our_features = NULL;
74 static char *our_feature_string = NULL;
75 static g_option_t *g_options = NULL;
76
77 /* local functions */
78 int main(int argc, char **argv);
79
80 static void check_options(char *program, char *calcprog, char *disk, char *amdevice, option_t *options);
81 static void check_disk(char *program, char *calcprog, char *disk, char *amdevice, int level, option_t *options);
82 static void check_overall(void);
83 static void check_access(char *filename, int mode);
84 static int check_file_exist(char *filename);
85 static void check_file(char *filename, int mode);
86 static void check_dir(char *dirname, int mode);
87 static void check_suid(char *filename);
88 static void check_space(char *dir, off_t kbytes);
89
90 int
91 main(
92     int         argc,
93     char **     argv)
94 {
95     int level;
96     char *line = NULL;
97     char *program = NULL;
98     char *calcprog = NULL;
99     char *disk = NULL;
100     char *qdisk = NULL;
101     char *amdevice = NULL;
102     char *qamdevice = NULL;
103     char *optstr = NULL;
104     char *err_extra = NULL;
105     char *s, *fp;
106     char *conffile;
107     option_t *options;
108     int ch;
109 #if defined(USE_DBMALLOC)
110     unsigned long malloc_hist_1, malloc_size_1;
111     unsigned long malloc_hist_2, malloc_size_2;
112 #endif
113
114     /* initialize */
115
116     safe_fd(-1, 0);
117     safe_cd();
118
119     set_pname("selfcheck");
120
121     /* Don't die when child closes pipe */
122     signal(SIGPIPE, SIG_IGN);
123
124 #if defined(USE_DBMALLOC)
125     malloc_size_1 = malloc_inuse(&malloc_hist_1);
126 #endif
127
128     erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
129     dbopen(DBG_SUBDIR_CLIENT);
130     startclock();
131     dbprintf(("%s: version %s\n", get_pname(), version()));
132
133     if(argc > 2 && strcmp(argv[1], "amandad") == 0) {
134         amandad_auth = stralloc(argv[2]);
135     }
136
137     conffile = vstralloc(CONFIG_DIR, "/", "amanda-client.conf", NULL);
138     if (read_clientconf(conffile) > 0) {
139         printf("ERROR [reading conffile: %s]\n", conffile);
140         error("error reading conffile: %s", conffile);
141         /*NOTREACHED*/
142     }
143     amfree(conffile);
144
145     our_features = am_init_feature_set();
146     our_feature_string = am_feature_to_string(our_features);
147
148     /* handle all service requests */
149
150     /*@ignore@*/
151     for(; (line = agets(stdin)) != NULL; free(line)) {
152     /*@end@*/
153         if (line[0] == '\0')
154             continue;
155
156         if(strncmp_const(line, "OPTIONS ") == 0) {
157             g_options = parse_g_options(line+8, 1);
158             if(!g_options->hostname) {
159                 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
160                 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
161                 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
162             }
163
164             printf("OPTIONS ");
165             if(am_has_feature(g_options->features, fe_rep_options_features)) {
166                 printf("features=%s;", our_feature_string);
167             }
168             if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
169                 printf("hostname=%s;", g_options->hostname);
170             }
171             printf("\n");
172             fflush(stdout);
173
174             if (g_options->config) {
175                 conffile = vstralloc(CONFIG_DIR, "/", g_options->config, "/",
176                                      "amanda-client.conf", NULL);
177                 if (read_clientconf(conffile) > 0) {
178                     printf("ERROR [reading conffile: %s]\n", conffile);
179                     error("error reading conffile: %s", conffile);
180                     /*NOTREACHED*/
181                 }
182                 amfree(conffile);
183
184                 dbrename(g_options->config, DBG_SUBDIR_CLIENT);
185             }
186
187             continue;
188         }
189
190         s = line;
191         ch = *s++;
192
193         skip_whitespace(s, ch);                 /* find program name */
194         if (ch == '\0') {
195             goto err;                           /* no program */
196         }
197         program = s - 1;
198         skip_non_whitespace(s, ch);
199         s[-1] = '\0';                           /* terminate the program name */
200
201         program_is_backup_api = 0;
202         if(strcmp(program,"BACKUP")==0) {
203             program_is_backup_api = 1;
204             skip_whitespace(s, ch);             /* find dumper name */
205             if (ch == '\0') {
206                 goto err;                       /* no program */
207             }
208             program = s - 1;
209             skip_non_whitespace(s, ch);
210             s[-1] = '\0';                       /* terminate the program name */
211         }
212
213         if(strncmp_const(program, "CALCSIZE") == 0) {
214             skip_whitespace(s, ch);             /* find program name */
215             if (ch == '\0') {
216                 goto err;                       /* no program */
217             }
218             calcprog = s - 1;
219             skip_non_whitespace(s, ch);
220             s[-1] = '\0';
221         }
222         else {
223             calcprog = NULL;
224         }
225
226         skip_whitespace(s, ch);                 /* find disk name */
227         if (ch == '\0') {
228             goto err;                           /* no disk */
229         }
230         qdisk = s - 1;
231         skip_quoted_string(s, ch);
232         s[-1] = '\0';                           /* terminate the disk name */
233         disk = unquote_string(qdisk);
234
235         skip_whitespace(s, ch);                 /* find the device or level */
236         if (ch == '\0') {
237             goto err;                           /* no device or level */
238         }
239         if(!isdigit((int)s[-1])) {
240             fp = s - 1;
241             skip_quoted_string(s, ch);
242              s[-1] = '\0';                      /* terminate the device */
243             qamdevice = stralloc(fp);
244             amdevice = unquote_string(qamdevice);
245             skip_whitespace(s, ch);             /* find level number */
246         }
247         else {
248             amdevice = stralloc(disk);
249         }
250
251                                                 /* find level number */
252         if (ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
253             goto err;                           /* bad level */
254         }
255         skip_integer(s, ch);
256
257         skip_whitespace(s, ch);
258         if (ch && strncmp_const_skip(s - 1, "OPTIONS ", s, ch) == 0) {
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, options);
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, NULL);
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 == COMP_BEST) || (options->compress == COMP_FAST) 
486                 || (options->compress == COMP_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     option_t    *options)
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     FILE *toolin;
521     char number[NUM_STR_SIZE];
522
523     (void)level;        /* Quiet unused parameter warning */
524
525     dbprintf(("%s: checking disk %s\n", debug_prefix_time(NULL), qdisk));
526
527     if(strcmp(myprogram,"CALCSIZE") == 0) {
528         if(amdevice[0] == '/' && amdevice[1] == '/') {
529             err = vstralloc("Can't use CALCSIZE for samba estimate,",
530                             " use CLIENT: ",
531                             amdevice,
532                             NULL);
533             goto common_exit;
534         }
535         myprogram = calcprog;
536     }
537
538     if (strcmp(myprogram, "GNUTAR")==0) {
539         if(amdevice[0] == '/' && amdevice[1] == '/') {
540 #ifdef SAMBA_CLIENT
541             int nullfd, checkerr;
542             int passwdfd;
543             char *pwtext;
544             size_t pwtext_len;
545             pid_t checkpid;
546             amwait_t retstat;
547             char number[NUM_STR_SIZE];
548             pid_t wpid;
549             int ret, sig, rc;
550             char *line;
551             char *sep;
552             FILE *ferr;
553             char *pw_fd_env;
554             int errdos;
555
556             parsesharename(amdevice, &share, &subdir);
557             if (!share) {
558                 err = stralloc2("cannot parse for share/subdir disk entry ", amdevice);
559                 goto common_exit;
560             }
561             if ((subdir) && (SAMBA_VERSION < 2)) {
562                 err = vstralloc("subdirectory specified for share '",
563                                 amdevice,
564                                 "' but samba not v2 or better",
565                                 NULL);
566                 goto common_exit;
567             }
568             if ((user_and_password = findpass(share, &domain)) == NULL) {
569                 err = stralloc2("cannot find password for ", amdevice);
570                 goto common_exit;
571             }
572             lpass = strlen(user_and_password);
573             if ((pwtext = strchr(user_and_password, '%')) == NULL) {
574                 err = stralloc2("password field not \'user%pass\' for ", amdevice);
575                 goto common_exit;
576             }
577             *pwtext++ = '\0';
578             pwtext_len = (size_t)strlen(pwtext);
579             amfree(device);
580             if ((device = makesharename(share, 0)) == NULL) {
581                 err = stralloc2("cannot make share name of ", share);
582                 goto common_exit;
583             }
584
585             if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
586                 err = stralloc2("Cannot access /dev/null : ", strerror(errno));
587                 goto common_exit;
588             }
589
590             if (pwtext_len > 0) {
591                 pw_fd_env = "PASSWD_FD";
592             } else {
593                 pw_fd_env = "dummy_PASSWD_FD";
594             }
595             checkpid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE,
596                                  &nullfd, &nullfd, &checkerr,
597                                  pw_fd_env, &passwdfd,
598                                  "smbclient",
599                                  device,
600                                  *user_and_password ? "-U" : skip_argument,
601                                  *user_and_password ? user_and_password : skip_argument,
602                                  "-E",
603                                  domain ? "-W" : skip_argument,
604                                  domain ? domain : skip_argument,
605 #if SAMBA_VERSION >= 2
606                                  subdir ? "-D" : skip_argument,
607                                  subdir ? subdir : skip_argument,
608 #endif
609                                  "-c", "quit",
610                                  NULL);
611             amfree(domain);
612             aclose(nullfd);
613             /*@ignore@*/
614             if ((pwtext_len > 0)
615               && fullwrite(passwdfd, pwtext, (size_t)pwtext_len) < 0) {
616                 err = vstralloc("password write failed: ",
617                                 amdevice,
618                                 ": ",
619                                 strerror(errno),
620                                 NULL);
621                 aclose(passwdfd);
622                 goto common_exit;
623             }
624             /*@end@*/
625             memset(user_and_password, '\0', (size_t)lpass);
626             amfree(user_and_password);
627             aclose(passwdfd);
628             ferr = fdopen(checkerr, "r");
629             if (!ferr) {
630                 printf("ERROR [Can't fdopen: %s]\n", strerror(errno));
631                 error("Can't fdopen: %s", strerror(errno));
632                 /*NOTREACHED*/
633             }
634             sep = "";
635             errdos = 0;
636             for(sep = ""; (line = agets(ferr)) != NULL; free(line)) {
637                 if (line[0] == '\0')
638                     continue;
639                 strappend(extra_info, sep);
640                 strappend(extra_info, line);
641                 sep = ": ";
642                 if(strstr(line, "ERRDOS") != NULL) {
643                     errdos = 1;
644                 }
645             }
646             afclose(ferr);
647             checkerr = -1;
648             rc = 0;
649             while ((wpid = wait(&retstat)) != -1) {
650                 if (WIFSIGNALED(retstat)) {
651                     ret = 0;
652                     rc = sig = WTERMSIG(retstat);
653                 } else {
654                     sig = 0;
655                     rc = ret = WEXITSTATUS(retstat);
656                 }
657                 if (rc != 0) {
658                     strappend(err, sep);
659                     if (ret == 0) {
660                         strappend(err, "got signal ");
661                         ret = sig;
662                     } else {
663                         strappend(err, "returned ");
664                     }
665                     snprintf(number, (size_t)sizeof(number), "%d", ret);
666                     strappend(err, number);
667                 }
668             }
669             if (errdos != 0 || rc != 0) {
670                 err = newvstralloc(err,
671                                    "samba access error: ",
672                                    amdevice,
673                                    ": ",
674                                    extra_info ? extra_info : "",
675                                    err,
676                                    NULL);
677                 amfree(extra_info);
678             }
679 #else
680             err = stralloc2("This client is not configured for samba: ", qdisk);
681 #endif
682             goto common_exit;
683         }
684         amode = F_OK;
685         amfree(device);
686         device = amname_to_dirname(amdevice);
687     } else if (strcmp(myprogram, "DUMP") == 0) {
688         if(amdevice[0] == '/' && amdevice[1] == '/') {
689             err = vstralloc("The DUMP program cannot handle samba shares,",
690                             " use GNUTAR: ",
691                             qdisk,
692                             NULL);
693             goto common_exit;
694         }
695 #ifdef VDUMP                                                            /* { */
696 #ifdef DUMP                                                             /* { */
697         if (strcmp(amname_to_fstype(amdevice), "advfs") == 0)
698 #else                                                                   /* }{ */
699         if (1)
700 #endif                                                                  /* } */
701         {
702             amfree(device);
703             device = amname_to_dirname(amdevice);
704             amode = F_OK;
705         } else
706 #endif                                                                  /* } */
707         {
708             amfree(device);
709             device = amname_to_devname(amdevice);
710 #ifdef USE_RUNDUMP
711             amode = F_OK;
712 #else
713             amode = R_OK;
714 #endif
715         }
716     }
717     else { /* program_is_backup_api==1 */
718         pid_t  backup_api_pid;
719         int    property_pipe[2];
720         backup_support_option_t *bsu;
721
722         bsu = backup_support_option(program, g_options, disk, amdevice);
723
724         if (pipe(property_pipe) < 0) {
725             err = vstralloc("pipe failed: ", strerror(errno), NULL);
726             goto common_exit;
727         }
728         fflush(stdout);fflush(stderr);
729         
730         switch (backup_api_pid = fork()) {
731         case -1:
732             err = vstralloc("fork failed: ", strerror(errno), NULL);
733             goto common_exit;
734
735         case 0: /* child */
736             {
737                 char *argvchild[14];
738                 char *cmd = vstralloc(DUMPER_DIR, "/", program, NULL);
739                 int j=0;
740                 argvchild[j++] = program;
741                 argvchild[j++] = "selfcheck";
742                 if (bsu->message_line == 1) {
743                     argvchild[j++] = "--message";
744                     argvchild[j++] = "line";
745                 }
746                 if (g_options->config != NULL && bsu->config == 1) {
747                     argvchild[j++] = "--config";
748                     argvchild[j++] = g_options->config;
749                 }
750                 if (g_options->hostname != NULL && bsu->host == 1) {
751                     argvchild[j++] = "--host";
752                     argvchild[j++] = g_options->hostname;
753                 }
754                 if (disk != NULL && bsu->disk == 1) {
755                     argvchild[j++] = "--disk";
756                     argvchild[j++] = disk;
757                 }
758                 argvchild[j++] = "--device";
759                 argvchild[j++] = amdevice;
760                 if(options && options->createindex && bsu->index_line == 1) {
761                     argvchild[j++] = "--index";
762                     argvchild[j++] = "line";
763                 }
764                 if (!options->no_record && bsu->record == 1) {
765                     argvchild[j++] = "--record";
766                 }
767                 argvchild[j++] = NULL;
768                 dup2(property_pipe[0], 0);
769                 aclose(property_pipe[1]);
770                 execve(cmd,argvchild,safe_env());
771                 printf("ERROR [Can't execute %s: %s]\n", cmd, strerror(errno));
772                 exit(127);
773             }
774         default: /* parent */
775             {
776                 int status;
777                 aclose(property_pipe[0]);
778                 toolin = fdopen(property_pipe[1],"w");
779                 if (!toolin) {
780                     err = vstralloc("Can't fdopen: ", strerror(errno), NULL);
781                     goto common_exit;
782                 }
783                 output_tool_property(toolin, options);
784                 fflush(toolin);
785                 fclose(toolin);
786                 if (waitpid(backup_api_pid, &status, 0) < 0) {
787                     if (!WIFEXITED(status)) {
788                         snprintf(number, SIZEOF(number), "%d",
789                                  (int)WTERMSIG(status));
790                         err = vstralloc("Tool exited with signal ", number,
791                                         NULL);
792                     } else if (WEXITSTATUS(status) != 0) {
793                         snprintf(number, SIZEOF(number), "%d",
794                                  (int)WEXITSTATUS(status));
795                         err = vstralloc("Tool exited with status ", number,
796                                         NULL);
797                     } else {
798                         err = stralloc("waitpid returned negative value");
799                     }
800                     goto common_exit;
801                 }
802             }
803         }
804         amfree(bsu);
805         fflush(stdout);fflush(stderr);
806         amfree(device);
807         amfree(qamdevice);
808         amfree(qdisk);
809         return;
810     }
811
812     qdevice = quote_string(device);
813     dbprintf(("%s: device %s\n", debug_prefix_time(NULL), qdevice));
814
815     /* skip accessability test if this is an AFS entry */
816     if(strncmp_const(device, "afs:") != 0) {
817 #ifdef CHECK_FOR_ACCESS_WITH_OPEN
818         access_result = open(device, O_RDONLY);
819         access_type = "open";
820 #else
821         access_result = access(device, amode);
822         access_type = "access";
823 #endif
824         if(access_result == -1) {
825             err = vstralloc("could not ", access_type, " ", qdevice,
826                         " (", qdisk, "): ", strerror(errno), NULL);
827         }
828 #ifdef CHECK_FOR_ACCESS_WITH_OPEN
829         aclose(access_result);
830 #endif
831     }
832
833 common_exit:
834
835     amfree(share);
836     amfree(subdir);
837     if(user_and_password) {
838         memset(user_and_password, '\0', (size_t)lpass);
839         amfree(user_and_password);
840     }
841     amfree(domain);
842
843     if(err) {
844         printf("ERROR [%s]\n", err);
845         dbprintf(("%s: %s\n", debug_prefix_time(NULL), err));
846         amfree(err);
847     } else {
848         printf("OK %s\n", qdisk);
849         dbprintf(("%s: disk %s OK\n", debug_prefix_time(NULL), qdisk));
850         printf("OK %s\n", qamdevice);
851         dbprintf(("%s: amdevice %s OK\n",
852                   debug_prefix_time(NULL), qamdevice));
853         printf("OK %s\n", qdevice);
854         dbprintf(("%s: device %s OK\n", debug_prefix_time(NULL), qdevice));
855     }
856     if(extra_info) {
857         dbprintf(("%s: extra info: %s\n", debug_prefix_time(NULL), extra_info));
858         amfree(extra_info);
859     }
860     amfree(qdisk);
861     amfree(qdevice);
862     amfree(qamdevice);
863     amfree(device);
864
865     /* XXX perhaps do something with level: read dumpdates and sanity check */
866 }
867
868 static void
869 check_overall(void)
870 {
871     char *cmd;
872     struct stat buf;
873     int testfd;
874     char *gnutar_list_dir;
875     int   need_amandates = 0;
876
877     if( need_runtar )
878     {
879         cmd = vstralloc(libexecdir, "/", "runtar", versionsuffix(), NULL);
880         check_file(cmd,X_OK);
881         check_suid(cmd);
882         amfree(cmd);
883     }
884
885     if( need_rundump )
886     {
887         cmd = vstralloc(libexecdir, "/", "rundump", versionsuffix(), NULL);
888         check_file(cmd,X_OK);
889         check_suid(cmd);
890         amfree(cmd);
891     }
892
893     if( need_dump ) {
894 #ifdef DUMP
895         check_file(DUMP, X_OK);
896 #else
897         printf("ERROR [DUMP program not available]\n");
898 #endif
899     }
900
901     if( need_restore ) {
902 #ifdef RESTORE
903         check_file(RESTORE, X_OK);
904 #else
905         printf("ERROR [RESTORE program not available]\n");
906 #endif
907     }
908
909     if ( need_vdump ) {
910 #ifdef VDUMP
911         check_file(VDUMP, X_OK);
912 #else
913         printf("ERROR [VDUMP program not available]\n");
914 #endif
915     }
916
917     if ( need_vrestore ) {
918 #ifdef VRESTORE
919         check_file(VRESTORE, X_OK);
920 #else
921         printf("ERROR [VRESTORE program not available]\n");
922 #endif
923     }
924
925     if( need_xfsdump ) {
926 #ifdef XFSDUMP
927         check_file(XFSDUMP, F_OK);
928 #else
929         printf("ERROR [XFSDUMP program not available]\n");
930 #endif
931     }
932
933     if( need_xfsrestore ) {
934 #ifdef XFSRESTORE
935         check_file(XFSRESTORE, X_OK);
936 #else
937         printf("ERROR [XFSRESTORE program not available]\n");
938 #endif
939     }
940
941     if( need_vxdump ) {
942 #ifdef VXDUMP
943         check_file(VXDUMP, X_OK);
944 #else
945         printf("ERROR [VXDUMP program not available]\n");
946 #endif
947     }
948
949     if( need_vxrestore ) {
950 #ifdef VXRESTORE
951         check_file(VXRESTORE, X_OK);
952 #else
953         printf("ERROR [VXRESTORE program not available]\n");
954 #endif
955     }
956
957     if( need_gnutar ) {
958 #ifdef GNUTAR
959         check_file(GNUTAR, X_OK);
960 #else
961         printf("ERROR [GNUTAR program not available]\n");
962 #endif
963         need_amandates = 1;
964         gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
965         if (strlen(gnutar_list_dir) == 0)
966             gnutar_list_dir = NULL;
967         if (gnutar_list_dir) 
968             check_dir(gnutar_list_dir, R_OK|W_OK);
969     }
970
971     if (need_amandates) {
972         char *amandates_file;
973         amandates_file = getconf_str(CNF_AMANDATES);
974         check_file(amandates_file, R_OK|W_OK);
975     }
976     if( need_calcsize ) {
977         char *cmd;
978
979         cmd = vstralloc(libexecdir, "/", "calcsize", versionsuffix(), NULL);
980
981         check_file(cmd, X_OK);
982
983         amfree(cmd);
984     }
985
986     if( need_samba ) {
987 #ifdef SAMBA_CLIENT
988         check_file(SAMBA_CLIENT, X_OK);
989 #else
990         printf("ERROR [SMBCLIENT program not available]\n");
991 #endif
992         testfd = open("/etc/amandapass", R_OK);
993         if (testfd >= 0) {
994             if(fstat(testfd, &buf) == 0) {
995                 if ((buf.st_mode & 0x7) != 0) {
996                     printf("ERROR [/etc/amandapass is world readable!]\n");
997                 } else {
998                     printf("OK [/etc/amandapass is readable, but not by all]\n");
999                 }
1000             } else {
1001                 printf("OK [unable to stat /etc/amandapass: %s]\n",
1002                        strerror(errno));
1003             }
1004             aclose(testfd);
1005         } else {
1006             printf("ERROR [unable to open /etc/amandapass: %s]\n",
1007                    strerror(errno));
1008         }
1009     }
1010
1011     if (need_compress_path )
1012         check_file(COMPRESS_PATH, X_OK);
1013
1014     if (need_dump || need_xfsdump ) {
1015         if (check_file_exist("/etc/dumpdates")) {
1016             check_file("/etc/dumpdates",
1017 #ifdef USE_RUNDUMP
1018                        F_OK
1019 #else
1020                        R_OK|W_OK
1021 #endif
1022                       );
1023         } else {
1024 #ifndef USE_RUNDUMP
1025             if (access("/etc", R_OK|W_OK) == -1) {
1026                 printf("ERROR [dump will not be able to create the /etc/dumpdates file: %s]\n", strerror(errno));
1027             }
1028 #endif
1029         }
1030     }
1031
1032     if (need_vdump) {
1033         if (check_file_exist("/etc/vdumpdates")) {
1034             check_file("/etc/vdumpdates", F_OK);
1035         }
1036     }
1037
1038     check_access("/dev/null", R_OK|W_OK);
1039     check_space(AMANDA_TMPDIR, (off_t)64);      /* for amandad i/o */
1040
1041 #ifdef AMANDA_DBGDIR
1042     check_space(AMANDA_DBGDIR, (off_t)64);      /* for amandad i/o */
1043 #endif
1044
1045     check_space("/etc", (off_t)64);             /* for /etc/dumpdates writing */
1046 }
1047
1048 static void
1049 check_space(
1050     char *      dir,
1051     off_t       kbytes)
1052 {
1053     generic_fs_stats_t statp;
1054     char *quoted = quote_string(dir);
1055
1056     if(get_fs_stats(dir, &statp) == -1) {
1057         printf("ERROR [cannot statfs %s: %s]\n", quoted, strerror(errno));
1058     } else if(statp.avail < kbytes) {
1059         printf("ERROR [dir %s needs " OFF_T_FMT "KB, only has "
1060                 OFF_T_FMT "KB available.]\n", quoted,
1061                 (OFF_T_FMT_TYPE)kbytes,
1062                 (OFF_T_FMT_TYPE)statp.avail);
1063     } else {
1064         printf("OK %s has more than " OFF_T_FMT " KB available.\n",
1065                 quoted, (OFF_T_FMT_TYPE)kbytes);
1066     }
1067     amfree(quoted);
1068 }
1069
1070 static void
1071 check_access(
1072     char *      filename,
1073     int         mode)
1074 {
1075     char *noun, *adjective;
1076     char *quoted = quote_string(filename);
1077
1078     if(mode == F_OK)
1079         noun = "find", adjective = "exists";
1080     else if((mode & X_OK) == X_OK)
1081         noun = "execute", adjective = "executable";
1082     else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
1083         noun = "read/write", adjective = "read/writable";
1084     else 
1085         noun = "access", adjective = "accessible";
1086
1087     if(access(filename, mode) == -1)
1088         printf("ERROR [can not %s %s: %s]\n", noun, quoted, strerror(errno));
1089     else
1090         printf("OK %s %s\n", quoted, adjective);
1091     amfree(quoted);
1092 }
1093
1094 static int
1095 check_file_exist(
1096     char *filename)
1097 {
1098     struct stat stat_buf;
1099
1100     if (stat(filename, &stat_buf) != 0) {
1101         if(errno == ENOENT) {
1102             return 0;
1103         }
1104     }
1105     return 1;
1106 }
1107
1108 static void
1109 check_file(
1110     char *      filename,
1111     int         mode)
1112 {
1113     struct stat stat_buf;
1114     char *quoted;
1115
1116     if(!stat(filename, &stat_buf)) {
1117         if(!S_ISREG(stat_buf.st_mode)) {
1118             quoted = quote_string(filename);
1119             printf("ERROR [%s is not a file]\n", quoted);
1120             amfree(quoted);
1121         }
1122     }
1123     check_access(filename, mode);
1124 }
1125
1126 static void
1127 check_dir(
1128     char *      dirname,
1129     int         mode)
1130 {
1131     struct stat stat_buf;
1132     char *quoted;
1133     char *dir;
1134
1135     if(!stat(dirname, &stat_buf)) {
1136         if(!S_ISDIR(stat_buf.st_mode)) {
1137             quoted = quote_string(dirname);
1138             printf("ERROR [%s is not a directory]\n", quoted);
1139             amfree(quoted);
1140         }
1141     }
1142     dir = stralloc2(dirname, "/.");
1143     check_access(dir, mode);
1144     amfree(dir);
1145 }
1146
1147 static void
1148 check_suid(
1149     char *      filename)
1150 {
1151 /* The following is only valid for real Unixs */
1152 #ifndef IGNORE_UID_CHECK
1153     struct stat stat_buf;
1154     char *quoted = quote_string(filename);
1155
1156     if(!stat(filename, &stat_buf)) {
1157         if(stat_buf.st_uid != 0 ) {
1158             printf("ERROR [%s is not owned by root]\n", quoted);
1159         }
1160         if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
1161             printf("ERROR [%s is not SUID root]\n", quoted);
1162         }
1163     }
1164     else {
1165         printf("ERROR [can not stat %s]\n", quoted);
1166     }
1167     amfree(quoted);
1168 #else
1169     (void)filename;     /* Quiet unused parameter warning */
1170 #endif
1171 }