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