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