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