Imported Upstream version 3.2.0
[debian/amanda] / server-src / amcheck.c
index b7c2c172dafc000fc3386e3482c21070e09613cb..2c843a9b46b0955efc8e9ebe28c328ec965d1c04 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: amcheck.c,v 1.50.2.19.2.7.2.20.2.10 2005/04/06 12:32:31 martinea Exp $
+ * $Id: amcheck.c,v 1.149 2006/08/24 01:57:16 paddy_s Exp $
  *
  * checks for common problems in server and clients
  */
 #include "amanda.h"
+#include "util.h"
 #include "conffile.h"
-#include "statfs.h"
+#include "columnar.h"
+#include "fsusage.h"
 #include "diskfile.h"
 #include "tapefile.h"
-#include "tapeio.h"
-#include "changer.h"
+#include "packet.h"
+#include "security.h"
 #include "protocol.h"
 #include "clock.h"
-#include "version.h"
 #include "amindex.h"
-#include "token.h"
-#include "util.h"
+#include "server_util.h"
 #include "pipespawn.h"
 #include "amfeatures.h"
-
-/*
- * If we don't have the new-style wait access functions, use our own,
- * compatible with old-style BSD systems at least.  Note that we don't
- * care about the case w_stopval == WSTOPPED since we don't ask to see
- * stopped processes, so should never get them from wait.
- */
-#ifndef WEXITSTATUS
-#   define WEXITSTATUS(r)       (((union wait *) &(r))->w_retcode)
-#   define WTERMSIG(r)          (((union wait *) &(r))->w_termsig)
-
-#   undef WIFSIGNALED
-#   define WIFSIGNALED(r)        (((union wait *) &(r))->w_termsig != 0)
-#endif
+#include "device.h"
+#include "property.h"
+#include "timestamp.h"
+#include "amxml.h"
+#include "physmem.h"
 
 #define BUFFER_SIZE    32768
 
-static int conf_ctimeout;
+static time_t conf_ctimeout;
 static int overwrite;
-dgram_t *msg = NULL;
 
-static disklist_t *origqp;
+static disklist_t origq;
 
 static uid_t uid_dumpuser;
 
 /* local functions */
 
-void usage P((void));
-int start_client_checks P((int fd));
-int start_server_check P((int fd, int do_localchk, int do_tapechk));
-int main P((int argc, char **argv));
-int scan_init P((int rc, int ns, int bk));
-int taperscan_slot P((int rc, char *slotstr, char *device));
-char *taper_scan P((void));
-int test_server_pgm P((FILE *outf, char *dir, char *pgm,
-                      int suid, uid_t dumpuid));
-
-void usage()
+void usage(void);
+pid_t start_client_checks(int fd);
+pid_t start_server_check(int fd, int do_localchk, int do_tapechk);
+int main(int argc, char **argv);
+int check_tapefile(FILE *outf, char *tapefile);
+int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
+
+void
+usage(void)
 {
-    error("Usage: amcheck%s [-M <username>] [-mawsclt] <conf> [host [disk]* ]*", versionsuffix());
+    g_printf(_("Usage: amcheck [-am] [-w] [-sclt] [-M <address>] [-o configoption]* <conf> [host [disk]* ]*\n"));
+    exit(1);
+    /*NOTREACHED*/
 }
 
-static unsigned long malloc_hist_1, malloc_size_1;
-static unsigned long malloc_hist_2, malloc_size_2;
-
 static am_feature_t *our_features = NULL;
 static char *our_feature_string = NULL;
 static char *displayunit;
 static long int unitdivisor;
 
-int main(argc, argv)
-int argc;
-char **argv;
+int
+main(
+    int                argc,
+    char **    argv)
 {
     char buffer[BUFFER_SIZE];
     char *version_string;
     char *mainfname = NULL;
     char pid_str[NUM_STR_SIZE];
-    int do_clientchk, clientchk_pid, client_probs;
-    int do_localchk, do_tapechk, serverchk_pid, server_probs;
-    int chk_flag;
-    int opt, size, result_port, tempfd, mainfd;
+    int do_clientchk, client_probs;
+    int do_localchk, do_tapechk, server_probs;
+    pid_t clientchk_pid, serverchk_pid;
+    int opt, tempfd, mainfd;
+    size_t size;
     amwait_t retstat;
     pid_t pid;
     extern int optind;
-    int l, n, s;
-    int fd;
     char *mailto = NULL;
     extern char *optarg;
     int mailout;
     int alwaysmail;
     char *tempfname = NULL;
-    char *conffile;
     char *conf_diskfile;
     char *dumpuser;
     struct passwd *pw;
     uid_t uid_me;
+    char *errstr;
+    config_overrides_t *cfg_ovr;
+    char *mailer;
 
-    for(fd = 3; fd < FD_SETSIZE; fd++) {
-       /*
-        * Make sure nobody spoofs us with a lot of extra open files
-        * that would cause an open we do to get a very high file
-        * descriptor, which in turn might be used as an index into
-        * an array (e.g. an fd_set).
-        */
-       close(fd);
-    }
-
+    /*
+     * Configure program for internationalization:
+     *   1) Only set the message locale for now.
+     *   2) Set textdomain for all amanda related programs to "amanda"
+     *      We don't want to be forced to support dozens of message catalogs.
+     */  
+    setlocale(LC_MESSAGES, "C");
+    textdomain("amanda"); 
+
+    safe_fd(-1, 0);
     safe_cd();
 
     set_pname("amcheck");
-    dbopen();
-
-    malloc_size_1 = malloc_inuse(&malloc_hist_1);
+    /* drop root privileges */
+    if (!set_root_privs(0)) {
+       error(_("amcheck must be run setuid root"));
+    }
 
-    ap_snprintf(pid_str, sizeof(pid_str), "%ld", (long)getpid());
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
 
-    erroutput_type = ERR_INTERACTIVE;
+    dbopen(DBG_SUBDIR_SERVER);
 
-    our_features = am_init_feature_set();
-    our_feature_string = am_feature_to_string(our_features);
+    memset(buffer, 0, sizeof(buffer));
 
-    /* set up dgram port first thing */
+    g_snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid());
 
-    msg = dgram_alloc();
+    add_amanda_log_handler(amanda_log_stderr);
 
-    if(dgram_bind(msg, &result_port) == -1)
-       error("could not bind result datagram port: %s", strerror(errno));
+    our_features = am_init_feature_set();
+    our_feature_string = am_feature_to_string(our_features);
 
-    if(geteuid() == 0) {
-       /* set both real and effective uid's to real uid, likewise for gid */
-       setgid(getgid());
-       setuid(getuid());
-    }
-    uid_me = getuid();
+    uid_me = geteuid();
 
     alwaysmail = mailout = overwrite = 0;
     do_localchk = do_tapechk = do_clientchk = 0;
-    chk_flag = 0;
     server_probs = client_probs = 0;
     tempfd = mainfd = -1;
 
     /* process arguments */
 
-    while((opt = getopt(argc, argv, "M:mawsclt")) != EOF) {
+    cfg_ovr = new_config_overrides(argc/2);
+    while((opt = getopt(argc, argv, "M:mawsclto:")) != EOF) {
        switch(opt) {
-       case 'M':       mailto=stralloc(optarg);
+       case 'M':       if (mailto) {
+                           g_printf(_("Multiple -M options\n"));
+                           exit(1);
+                       }
+                       mailto=stralloc(optarg);
+                       if(!validate_mailto(mailto)){
+                          g_printf(_("Invalid characters in mail address\n"));
+                          exit(1);
+                       }
+                       /*FALLTHROUGH*/
        case 'm':       
-#ifdef MAILER
                        mailout = 1;
-#else
-                       printf("You can't use -%c because configure didn't find a mailer.\n",
-                               opt);
-                       exit(1);
-#endif
                        break;
        case 'a':       
-#ifdef MAILER
                        mailout = 1;
                        alwaysmail = 1;
-#else
-                       printf("You can't use -%c because configure didn't find a mailer.\n",
-                               opt);
-                       exit(1);
-#endif
                        break;
-       case 's':       do_localchk = 1; do_tapechk = 1;
-                       chk_flag = 1;
+       case 's':       do_localchk = do_tapechk = 1;
                        break;
        case 'c':       do_clientchk = 1;
-                       chk_flag = 1;
                        break;
        case 'l':       do_localchk = 1;
-                       chk_flag = 1;
                        break;
-       case 'w':       do_tapechk = 1; overwrite = 1;
-                       chk_flag = 1;
+       case 'w':       overwrite = 1;
+                       break;
+       case 'o':       add_config_override_opt(cfg_ovr, optarg);
                        break;
        case 't':       do_tapechk = 1;
-                       chk_flag = 1;
                        break;
        case '?':
        default:
@@ -210,49 +189,105 @@ char **argv;
        }
     }
     argc -= optind, argv += optind;
-    if(! chk_flag) {
+    if(argc < 1) usage();
+
+
+    if ((do_localchk | do_clientchk | do_tapechk) == 0) {
+       /* Check everything if individual checks were not asked for */
        do_localchk = do_clientchk = do_tapechk = 1;
     }
 
-    if(argc < 1) usage();
+    if(overwrite)
+       do_tapechk = 1;
+
+    set_config_overrides(cfg_ovr);
+    config_init(CONFIG_INIT_EXPLICIT_NAME, argv[0]);
+    dbrename(get_config_name(), DBG_SUBDIR_SERVER);
+
+    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
+    read_diskfile(conf_diskfile, &origq);
+    disable_skip_disk(&origq);
+    amfree(conf_diskfile);
 
-    config_name = stralloc(*argv);
+    if (config_errors(NULL) >= CFGERR_WARNINGS) {
+       config_print_errors();
+       if (config_errors(NULL) >= CFGERR_ERRORS) {
+           g_critical(_("errors processing config file"));
+       }
+    }
 
-    config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
-    conffile = stralloc2(config_dir, CONFFILE_NAME);
-    if(read_conffile(conffile)) {
-       error("errors processing config file \"%s\"", conffile);
+    mailer = getconf_str(CNF_MAILER);
+    if ((!mailer || *mailer == '\0') && mailout == 1) {
+       if (alwaysmail == 1) {
+           g_printf(_("You can't use -a because a mailer is not defined\n"));
+       } else {
+           g_printf(_("You can't use -m because a mailer is not defined\n"));
+       }
+       exit(1);
     }
-    amfree(conffile);
-    conf_ctimeout = getconf_int(CNF_CTIMEOUT);
-    conf_diskfile = getconf_str(CNF_DISKFILE);
-    if (*conf_diskfile == '/') {
-       conf_diskfile = stralloc(conf_diskfile);
-    } else {
-       conf_diskfile = stralloc2(config_dir, conf_diskfile);
+    if(mailout && !mailto && 
+       (getconf_seen(CNF_MAILTO)==0 || strlen(getconf_str(CNF_MAILTO)) == 0)) {
+       g_printf(_("\nWARNING:No mail address configured in  amanda.conf.\n"));
+       g_printf(_("To receive dump results by email configure the "
+                "\"mailto\" parameter in amanda.conf\n"));
+        if(alwaysmail)        
+               g_printf(_("When using -a option please specify -Maddress also\n\n")); 
+       else
+               g_printf(_("Use -Maddress instead of -m\n\n")); 
+       exit(1);
     }
-    if((origqp = read_diskfile(conf_diskfile)) == NULL) {
-       error("could not load disklist %s", conf_diskfile);
+    if(mailout && !mailto)
+    { 
+       if(getconf_seen(CNF_MAILTO) && 
+          strlen(getconf_str(CNF_MAILTO)) > 0) {
+          if(!validate_mailto(getconf_str(CNF_MAILTO))){
+               g_printf(_("\nMail address in amanda.conf has invalid characters")); 
+               g_printf(_("\nNo email will be sent\n")); 
+                mailout = 0;
+          }
+       }
+       else {
+         g_printf(_("\nNo mail address configured in  amanda.conf\n"));
+          if(alwaysmail)        
+               g_printf(_("When using -a option please specify -Maddress also\n\n")); 
+         else
+               g_printf(_("Use -Maddress instead of -m\n\n")); 
+         exit(1);
+      }
+    }
+
+    conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
+
+    errstr = match_disklist(&origq, argc-1, argv+1);
+    if (errstr) {
+       g_printf(_("%s"),errstr);
+       amfree(errstr);
     }
-    match_disklist(origqp, argc-1, argv+1);
-    amfree(conf_diskfile);
 
     /*
-     * Make sure we are running as the dump user.
+     * Make sure we are running as the dump user.  Don't use
+     * check_running_as(..) here, because we want to produce more
+     * verbose error messages.
      */
     dumpuser = getconf_str(CNF_DUMPUSER);
     if ((pw = getpwnam(dumpuser)) == NULL) {
-       error("cannot look up dump user \"%s\"", dumpuser);
+       error(_("amanda.conf has dump user configured to \"%s\", but that user does not exist."), dumpuser);
+       /*NOTREACHED*/
     }
     uid_dumpuser = pw->pw_uid;
     if ((pw = getpwuid(uid_me)) == NULL) {
-       error("cannot look up my own uid (%ld)", (long)uid_me);
+       error(_("cannot get username for running user, uid %ld is not in your user database."),
+           (long)uid_me);
+       /*NOTREACHED*/
     }
+#ifdef CHECK_USERID
     if (uid_me != uid_dumpuser) {
-       error("running as user \"%s\" instead of \"%s\"",
-             pw->pw_name,
-             dumpuser);
+       error(_("running as user \"%s\" instead of \"%s\".\n"
+               "Change user to \"%s\" or change dump user to \"%s\" in amanda.conf"),
+             pw->pw_name, dumpuser, dumpuser, pw->pw_name);
+        /*NOTREACHED*/
     }
+#endif
 
     displayunit = getconf_str(CNF_DISPLAYUNIT);
     unitdivisor = getconf_unit_divisor();
@@ -268,8 +303,10 @@ char **argv;
     if(do_clientchk && (do_localchk || do_tapechk)) {
        /* we need the temp file */
        tempfname = vstralloc(AMANDA_TMPDIR, "/amcheck.temp.", pid_str, NULL);
-       if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1)
-           error("could not open %s: %s", tempfname, strerror(errno));
+       if((tempfd = open(tempfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
+           error(_("could not open temporary amcheck output file %s: %s. Check permissions"), tempfname, strerror(errno));
+           /*NOTREACHED*/
+       }
        unlink(tempfname);                      /* so it goes away on close */
        amfree(tempfname);
     }
@@ -277,8 +314,10 @@ char **argv;
     if(mailout) {
        /* the main fd is a file too */
        mainfname = vstralloc(AMANDA_TMPDIR, "/amcheck.main.", pid_str, NULL);
-       if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1)
-           error("could not open %s: %s", mainfname, strerror(errno));
+       if((mainfd = open(mainfname, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1) {
+           error(_("could not open amcheck server output file %s: %s. Check permissions"), mainfname, strerror(errno));
+           /*NOTREACHED*/
+       }
        unlink(mainfname);                      /* so it goes away on close */
        amfree(mainfname);
     }
@@ -288,11 +327,10 @@ char **argv;
 
     /* start server side checks */
 
-    if(do_localchk || do_tapechk) {
+    if(do_localchk || do_tapechk)
        serverchk_pid = start_server_check(mainfd, do_localchk, do_tapechk);
-    } else {
+    else
        serverchk_pid = 0;
-    }
 
     /* start client side checks */
 
@@ -315,65 +353,49 @@ char **argv;
            server_probs = WIFSIGNALED(retstat) || WEXITSTATUS(retstat);
            serverchk_pid = 0;
        } else {
-           char number[NUM_STR_SIZE];
            char *wait_msg = NULL;
 
-           ap_snprintf(number, sizeof(number), "%ld", (long)pid);
-           wait_msg = vstralloc("parent: reaped bogus pid ", number, "\n",
-                                NULL);
-           for(l = 0, n = strlen(wait_msg); l < n; l += s) {
-               if((s = write(mainfd, wait_msg + l, n - l)) < 0) {
-                   error("write main file: %s", strerror(errno));
-               }
+           wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid);
+           if (full_write(mainfd, wait_msg, strlen(wait_msg)) < strlen(wait_msg)) {
+               error(_("write main file: %s"), strerror(errno));
+               /*NOTREACHED*/
            }
            amfree(wait_msg);
        }
     }
-    amfree(msg);
 
     /* copy temp output to main output and write tagline */
 
     if(do_clientchk && (do_localchk || do_tapechk)) {
-       if(lseek(tempfd, 0, 0) == -1)
-           error("seek temp file: %s", strerror(errno));
+       if(lseek(tempfd, (off_t)0, 0) == (off_t)-1) {
+           error(_("seek temp file: %s"), strerror(errno));
+           /*NOTREACHED*/
+       }
 
-       while((size=read(tempfd, buffer, sizeof(buffer))) > 0) {
-           for(l = 0; l < size; l += s) {
-               if((s = write(mainfd, buffer + l, size - l)) < 0) {
-                   error("write main file: %s", strerror(errno));
-               }
+       while((size = full_read(tempfd, buffer, SIZEOF(buffer))) > 0) {
+           if (full_write(mainfd, buffer, size) < size) {
+               error(_("write main file: %s"), strerror(errno));
+               /*NOTREACHED*/
            }
        }
-       if(size < 0)
-           error("read temp file: %s", strerror(errno));
+       if(errno != 0) {
+           error(_("read temp file: %s"), strerror(errno));
+           /*NOTREACHED*/
+       }
        aclose(tempfd);
     }
 
-    version_string = vstralloc("\n",
-                              "(brought to you by Amanda ", version(), ")\n",
-                              NULL);
-    for(l = 0, n = strlen(version_string); l < n; l += s) {
-       if((s = write(mainfd, version_string + l, n - l)) < 0) {
-           error("write main file: %s", strerror(errno));
-       }
+    version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), VERSION);
+    if (full_write(mainfd, version_string, strlen(version_string)) < strlen(version_string)) {
+       error(_("write main file: %s"), strerror(errno));
+       /*NOTREACHED*/
     }
     amfree(version_string);
-    amfree(config_dir);
-    amfree(config_name);
     amfree(our_feature_string);
     am_release_feature_set(our_features);
     our_features = NULL;
 
-    malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
-    if(malloc_size_1 != malloc_size_2) {
-       malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
-    }
-
     /* send mail if requested, but only if there were problems */
-#ifdef MAILER
-
-#define        MAILTO_LIMIT    10
 
     if((server_probs || client_probs || alwaysmail) && mailout) {
        int mailfd;
@@ -381,59 +403,61 @@ char **argv;
        int errfd;
        FILE *ferr;
        char *subject;
-       char **a;
+       char **a, **b;
+       GPtrArray *pipeargs;
        amwait_t retstat;
-       pid_t mailpid;
-       pid_t wpid;
-       int r;
-       int w;
+       size_t r;
+       size_t w;
        char *err = NULL;
        char *extra_info = NULL;
        char *line = NULL;
-       int ret;
        int rc;
-       int sig;
-       char number[NUM_STR_SIZE];
 
        fflush(stdout);
-       if(lseek(mainfd, (off_t)0, SEEK_SET) == -1) {
-           error("lseek main file: %s", strerror(errno));
+       if(lseek(mainfd, (off_t)0, SEEK_SET) == (off_t)-1) {
+           error(_("lseek main file: %s"), strerror(errno));
+           /*NOTREACHED*/
        }
        if(alwaysmail && !(server_probs || client_probs)) {
-           subject = stralloc2(getconf_str(CNF_ORG),
-                           " AMCHECK REPORT: NO PROBLEMS FOUND");
+           subject = vstrallocf(_("%s AMCHECK REPORT: NO PROBLEMS FOUND"),
+                       getconf_str(CNF_ORG));
        } else {
-           subject = stralloc2(getconf_str(CNF_ORG),
-                           " AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE");
+           subject = vstrallocf(
+                       _("%s AMANDA PROBLEM: FIX BEFORE RUN, IF POSSIBLE"),
+                       getconf_str(CNF_ORG));
        }
-       /*
-        * Variable arg lists are hard to deal with when we do not know
-        * ourself how many args are involved.  Split the address list
-        * and hope there are not more than 9 entries.
-        *
-        * Remember that split() returns the original input string in
-        * argv[0], so we have to skip over that.
-        */
-       a = (char **) alloc((MAILTO_LIMIT + 1) * sizeof(char *));
-       memset(a, 0, (MAILTO_LIMIT + 1) * sizeof(char *));
        if(mailto) {
-           a[1] = mailto;
+           a = (char **) g_new0(char *, 2);
+           a[1] = stralloc(mailto);
            a[2] = NULL;
        } else {
-           n = split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " ");
-           a[n + 1] = NULL;
+           /* (note that validate_mailto doesn't allow any quotes, so this
+            * is really just splitting regular old strings) */
+           a = split_quoted_strings(getconf_str(CNF_MAILTO));
        }
        if((nullfd = open("/dev/null", O_RDWR)) < 0) {
            error("nullfd: /dev/null: %s", strerror(errno));
+           /*NOTREACHED*/
        }
-       mailpid = pipespawn(MAILER, STDIN_PIPE | STDERR_PIPE,
-                           &mailfd, &nullfd, &errfd,
-                           MAILER,
-                           "-s", subject,
-                                 a[1], a[2], a[3], a[4],
-                           a[5], a[6], a[7], a[8], a[9],
-                           NULL);
+
+       /* assemble the command line for the mailer */
+       pipeargs = g_ptr_array_sized_new(4);
+       g_ptr_array_add(pipeargs, mailer);
+       g_ptr_array_add(pipeargs, "-s");
+       g_ptr_array_add(pipeargs, subject);
+       for (b = a; *b; b++)
+           g_ptr_array_add(pipeargs, *b);
+       g_ptr_array_add(pipeargs, NULL);
+
+       pipespawnv(mailer, STDIN_PIPE | STDERR_PIPE, 0,
+                  &mailfd, &nullfd, &errfd,
+                  (char **)pipeargs->pdata);
+
+       g_ptr_array_free(pipeargs, FALSE);
        amfree(subject);
+       amfree(mailto);
+       g_strfreev(a);
+
        /*
         * There is the potential for a deadlock here since we are writing
         * to the process and then reading stderr, but in the normal case,
@@ -441,44 +465,42 @@ char **argv;
         * cases, the pipe will break and we will exit out of the loop.
         */
        signal(SIGPIPE, SIG_IGN);
-       while((r = fullread(mainfd, buffer, sizeof(buffer))) > 0) {
-           if((w = fullwrite(mailfd, buffer, r)) != r) {
-               if(w < 0 && errno == EPIPE) {
-                   strappend(extra_info, "EPIPE writing to mail process\n");
+       while((r = full_read(mainfd, buffer, SIZEOF(buffer))) > 0) {
+           if((w = full_write(mailfd, buffer, r)) != r) {
+               if(errno == EPIPE) {
+                   strappend(extra_info, _("EPIPE writing to mail process\n"));
                    break;
-               } else if(w < 0) {
-                   error("mailfd write: %s", strerror(errno));
+               } else if(errno != 0) {
+                   error(_("mailfd write: %s"), strerror(errno));
+                   /*NOTREACHED*/
                } else {
-                   error("mailfd write: wrote %d instead of %d", w, r);
+                   error(_("mailfd write: wrote %zd instead of %zd"), w, r);
+                   /*NOTREACHED*/
                }
            }
        }
        aclose(mailfd);
        ferr = fdopen(errfd, "r");
+       if (!ferr) {
+           error(_("Can't fdopen: %s"), strerror(errno));
+           /*NOTREACHED*/
+       }
        for(; (line = agets(ferr)) != NULL; free(line)) {
+           if (line[0] == '\0')
+               continue;
            strappend(extra_info, line);
            strappend(extra_info, "\n");
        }
        afclose(ferr);
        errfd = -1;
        rc = 0;
-       while ((wpid = wait(&retstat)) != -1) {
-           if (WIFSIGNALED(retstat)) {
-                   ret = 0;
-                   rc = sig = WTERMSIG(retstat);
-           } else {
-                   sig = 0;
-                   rc = ret = WEXITSTATUS(retstat);
-           }
-           if (rc != 0) {
-                   if (ret == 0) {
-                       strappend(err, "got signal ");
-                       ret = sig;
-                   } else {
-                       strappend(err, "returned ");
-                   }
-                   ap_snprintf(number, sizeof(number), "%d", ret);
-                   strappend(err, number);
+       while (wait(&retstat) != -1) {
+           if (!WIFEXITED(retstat) || WEXITSTATUS(retstat) != 0) {
+               char *mailer_error = str_exit_status("mailer", retstat);
+               strappend(err, mailer_error);
+               amfree(mailer_error);
+
+               rc = 1;
            }
        }
        if (rc != 0) {
@@ -486,213 +508,200 @@ char **argv;
                fputs(extra_info, stderr);
                amfree(extra_info);
            }
-           error("error running mailer %s: %s", MAILER, err);
+           error(_("error running mailer %s: %s"), mailer, err?err:"(unknown)");
+           /*NOTREACHED*/
        }
     }
-#endif
+
     dbclose();
     return (server_probs || client_probs);
 }
 
 /* --------------------------------------------------- */
 
-int nslots, backwards, found, got_match, tapedays;
-char *datestamp;
-char *first_match_label = NULL, *first_match = NULL, *found_device = NULL;
-char *label;
-char *searchlabel, *labelstr;
-tape_t *tp;
-FILE *errf;
-
-int scan_init(rc, ns, bk)
-int rc, ns, bk;
-{
-    if(rc)
-       error("could not get changer info: %s", changer_resultstr);
-
-    nslots = ns;
-    backwards = bk;
-
-    return 0;
-}
-
-int taperscan_slot(rc, slotstr, device)
-int rc;
-char *slotstr;
-char *device;
-{
-    char *errstr;
-
-    if(rc == 2) {
-       fprintf(errf, "%s: fatal slot %s: %s\n",
-               get_pname(), slotstr, changer_resultstr);
-       return 1;
-    }
-    else if(rc == 1) {
-       fprintf(errf, "%s: slot %s: %s\n",
-               get_pname(), slotstr, changer_resultstr);
-       return 0;
-    }
-    else {
-       if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL) {
-           fprintf(errf, "%s: slot %s: %s\n", get_pname(), slotstr, errstr);
-       } else {
-           /* got an amanda tape */
-           fprintf(errf, "%s: slot %s: date %-8s label %s",
-                   get_pname(), slotstr, datestamp, label);
-           if(searchlabel != NULL
-              && (strcmp(label, FAKE_LABEL) == 0
-                  || strcmp(label, searchlabel) == 0)) {
-               /* it's the one we are looking for, stop here */
-               fprintf(errf, " (exact label match)\n");
-               found_device = newstralloc(found_device, device);
-               found = 1;
-               return 1;
-           }
-           else if(!match(labelstr, label))
-               fprintf(errf, " (no match)\n");
-           else {
-               /* not an exact label match, but a labelstr match */
-               /* check against tape list */
-               tp = lookup_tapelabel(label);
-               if(tp == NULL)
-                   fprintf(errf, " (Not in tapelist)\n");
-               else if(!reusable_tape(tp))
-                   fprintf(errf, " (active tape)\n");
-               else if(got_match == 0 && tp->datestamp == 0) {
-                   got_match = 1;
-                   first_match = newstralloc(first_match, slotstr);
-                   first_match_label = newstralloc(first_match_label, label);
-                   fprintf(errf, " (new tape)\n");
-                   found = 3;
-                   found_device = newstralloc(found_device, device);
-                   return 1;
-               }
-               else if(got_match)
-                   fprintf(errf, " (labelstr match)\n");
-               else {
-                   got_match = 1;
-                   first_match = newstralloc(first_match, slotstr);
-                   first_match_label = newstralloc(first_match_label, label);
-                   fprintf(errf, " (first labelstr match)\n");
-                   if(!backwards || !searchlabel) {
-                       found = 2;
-                       found_device = newstralloc(found_device, device);
-                       return 1;
-                   }
-               }
-           }
-       }
-    }
-    return 0;
-}
+static char *datestamp;
+static FILE *errf = NULL;
 
-char *taper_scan()
+int check_tapefile(
+    FILE *outf,
+    char *tapefile)
 {
-    char *outslot = NULL;
-
-    if((tp = lookup_last_reusable_tape(0)) == NULL)
-       searchlabel = NULL;
-    else
-       searchlabel = tp->label;
-
-    found = 0;
-    got_match = 0;
-
-    changer_find(scan_init, taperscan_slot, searchlabel);
-
-    if(found == 2 || found == 3)
-       searchlabel = first_match_label;
-    else if(!found && got_match) {
-       searchlabel = first_match_label;
-       amfree(found_device);
-       if(changer_loadslot(first_match, &outslot, &found_device) == 0) {
-           found = 1;
-       }
-    } else if(!found) {
-       if(searchlabel) {
-           changer_resultstr = newvstralloc(changer_resultstr,
-                                            "label ", searchlabel,
-                                            " or new tape not found in rack",
-                                            NULL);
-       } else {
-           changer_resultstr = newstralloc(changer_resultstr,
-                                           "new tape not found in rack");
+    struct stat statbuf;
+    char *quoted;
+    int tapebad = 0;
+
+    if (stat(tapefile, &statbuf) == 0) {
+       if (!S_ISREG(statbuf.st_mode)) {
+           quoted = quote_string(tapefile);
+           g_fprintf(outf, _("ERROR: tapelist %s: should be a regular file.\n"),
+                   quoted);
+           tapebad = 1;
+           amfree(quoted);
+       } else if (access(tapefile, F_OK) != 0) {
+           quoted = quote_string(tapefile);
+           g_fprintf(outf, _("ERROR: can't access tapelist %s\n"), quoted);
+           tapebad = 1;
+           amfree(quoted);
+       } else if (access(tapefile, W_OK) != 0) {
+           quoted = quote_string(tapefile);
+           g_fprintf(outf, _("ERROR: tapelist %s: not writable\n"), quoted);
+           tapebad = 1;
+           amfree(quoted);
        }
     }
-    amfree(outslot);
-
-    return found ? found_device : NULL;
+    return tapebad;
 }
 
-int test_server_pgm(outf, dir, pgm, suid, dumpuid)
-FILE *outf;
-char *dir;
-char *pgm;
-int suid;
-uid_t dumpuid;
+int
+test_server_pgm(
+    FILE *     outf,
+    char *     dir,
+    char *     pgm,
+    int                suid,
+    uid_t      dumpuid)
 {
     struct stat statbuf;
     int pgmbad = 0;
+    char *quoted;
 
-    pgm = vstralloc(dir, "/", pgm, versionsuffix(), NULL);
+    pgm = vstralloc(dir, "/", pgm, NULL);
+    quoted = quote_string(pgm);
     if(stat(pgm, &statbuf) == -1) {
-       fprintf(outf, "ERROR: program %s: does not exist\n",
-               pgm);
+       g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
+               quoted);
        pgmbad = 1;
     } else if (!S_ISREG(statbuf.st_mode)) {
-       fprintf(outf, "ERROR: program %s: not a file\n",
-               pgm);
+       g_fprintf(outf, _("ERROR: program %s: not a file\n"),
+               quoted);
        pgmbad = 1;
     } else if (access(pgm, X_OK) == -1) {
-       fprintf(outf, "ERROR: program %s: not executable\n",
-               pgm);
+       g_fprintf(outf, _("ERROR: program %s: not executable\n"),
+               quoted);
        pgmbad = 1;
+#ifndef SINGLE_USERID
     } else if (suid \
               && dumpuid != 0
               && (statbuf.st_uid != 0 || (statbuf.st_mode & 04000) == 0)) {
-       fprintf(outf, "WARNING: program %s: not setuid-root\n",
-               pgm);
+       g_fprintf(outf, _("ERROR: program %s: not setuid-root\n"),
+               quoted);
+       pgmbad = 1;
+#else
+    /* Quiet unused parameter warnings */
+    (void)suid;
+    (void)dumpuid;
+#endif /* SINGLE_USERID */
     }
+    amfree(quoted);
     amfree(pgm);
     return pgmbad;
 }
 
-int start_server_check(fd, do_localchk, do_tapechk)
-    int fd;
+/* check that the tape is a valid amanda tape
+   Returns TRUE if all tests passed; FALSE otherwise. */
+static gboolean test_tape_status(FILE * outf) {
+    int outfd;
+    int nullfd = -1;
+    pid_t devpid;
+    char *amcheck_device = NULL;
+    gchar **args;
+    amwait_t wait_status;
+    gboolean success;
+
+    if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
+       return FALSE;
+    }
+
+    fflush(outf);
+    outfd = fileno(outf);
+
+    amcheck_device = vstralloc(amlibexecdir, "/", "amcheck-device", NULL);
+    args = get_config_options(overwrite? 3 : 2);
+    args[0] = amcheck_device; /* steal the reference */
+    args[1] = g_strdup(get_config_name());
+    if (overwrite)
+       args[2] = g_strdup("-w");
+
+    /* run libexecdir/amcheck-device.pl, capturing STDERR and STDOUT to outf */
+    devpid = pipespawnv(amcheck_device, 0, 0,
+           &nullfd, &outfd, &outfd,
+           (char **)args);
+
+    /* and immediately wait for it to die */
+    waitpid(devpid, &wait_status, 0);
+
+    if (WIFSIGNALED(wait_status)) {
+       g_fprintf(outf, _("amcheck-device terminated with signal %d"),
+                 WTERMSIG(wait_status));
+       success = FALSE;
+    } else if (WIFEXITED(wait_status)) {
+       success = (WEXITSTATUS(wait_status) == 0);
+    } else {
+       success = FALSE;
+    }
+
+    g_strfreev(args);
+    close(nullfd);
+
+    return success;
+}
+
+pid_t
+start_server_check(
+    int                fd,
+    int                do_localchk,
+    int                do_tapechk)
 {
-    char *errstr, *tapename;
-    generic_fs_stats_t fs;
-    tape_t *tp;
-    FILE *outf;
-    holdingdisk_t *hdp;
-    int pid;
+    struct fs_usage fsusage;
+    FILE *outf = NULL;
+    pid_t pid G_GNUC_UNUSED;
     int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
     int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
     int testtape = do_tapechk;
+    tapetype_t *tp = NULL;
+    char *quoted;
+    int res;
+    intmax_t kb_avail, kb_needed;
+    off_t tape_size;
+    gboolean printed_small_part_size_warning = FALSE;
+    char *small_part_size_warning =
+       _(" This may create > 1000 parts, severely degrading backup/restore performance.\n"
+       " See http://wiki.zmanda.com/index.php/Splitsize_too_small for more information.\n");
 
     switch(pid = fork()) {
-    case -1: error("could not fork server check: %s", strerror(errno));
-    case 0: break;
+    case -1:
+       error(_("could not spawn a process for checking the server: %s"), strerror(errno));
+        g_assert_not_reached();
+        
+    case 0:
+       break;
+        
     default:
        return pid;
     }
-
+    
     dup2(fd, 1);
     dup2(fd, 2);
-
+    
     set_pname("amcheck-server");
-
-    amfree(msg);
-
+    
     startclock();
 
-    if((outf = fdopen(fd, "w")) == NULL)
-       error("fdopen %d: %s", fd, strerror(errno));
+    /* server does not need root privileges, and the access() calls below use the real userid,
+     * so totally drop privileges at this point (making the userid equal to the dumpuser) */
+    set_root_privs(-1);
+
+    if((outf = fdopen(fd, "w")) == NULL) {
+       error(_("fdopen %d: %s"), fd, strerror(errno));
+       /*NOTREACHED*/
+    }
     errf = outf;
 
-    fprintf(outf, "Amanda Tape Server Host Check\n");
-    fprintf(outf, "-----------------------------\n");
+    g_fprintf(outf, _("Amanda Tape Server Host Check\n"));
+    g_fprintf(outf, "-----------------------------\n");
+
+    if (do_localchk || testtape) {
+        tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
+    }
 
     /*
      * Check various server side config file settings.
@@ -700,86 +709,115 @@ int start_server_check(fd, do_localchk, do_tapechk)
     if(do_localchk) {
        char *ColumnSpec;
        char *errstr = NULL;
-       tapetype_t *tp;
        char *lbl_templ;
 
        ColumnSpec = getconf_str(CNF_COLUMNSPEC);
-       if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
-           fprintf(outf, "ERROR: %s\n", errstr);
+       if(SetColumnDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
+           g_fprintf(outf, _("ERROR: %s\n"), errstr);
            amfree(errstr);
            confbad = 1;
        }
-       tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
-       lbl_templ = tp->lbl_templ;
+       lbl_templ = tapetype_get_lbl_templ(tp);
        if(strcmp(lbl_templ, "") != 0) {
-           if(strchr(lbl_templ, '/') == NULL) {
-               lbl_templ = stralloc2(config_dir, lbl_templ);
-           } else {
-               lbl_templ = stralloc(lbl_templ);
-           }
+           lbl_templ = config_dir_relative(lbl_templ);
            if(access(lbl_templ, R_OK) == -1) {
-               fprintf(outf,
-                       "ERROR: cannot access lbl_templ file %s: %s\n",
+               g_fprintf(outf,
+                       _("ERROR: cannot read label template (lbl-templ) file %s: %s. Check permissions\n"),
                        lbl_templ,
                        strerror(errno));
                confbad = 1;
            }
-#if !defined(LPRCMD)
-           fprintf(outf, "ERROR: lbl_templ set but no LPRCMD defined, you should reconfigure amanda\n       and make sure it find a lpr or lp command.\n");
+#if !defined(HAVE_LPR_CMD)
+           g_fprintf(outf, _("ERROR: lbl-templ set but no LPR command defined. You should reconfigure amanda\n       and make sure it finds a lpr or lp command.\n"));
            confbad = 1;
 #endif
        }
+
+       if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
+                                getconf_int(CNF_FLUSH_THRESHOLD_DUMPED)) {
+           g_fprintf(outf, _("WARNING: flush-threshold-dumped (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"),
+                     getconf_int(CNF_FLUSH_THRESHOLD_DUMPED),
+                     getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
+       }
+
+       if (getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED) <
+                                getconf_int(CNF_TAPERFLUSH)) {
+           g_fprintf(outf, _("WARNING: taperflush (%d) must be less than or equal to flush-threshold-scheduled (%d).\n"),
+                     getconf_int(CNF_TAPERFLUSH),
+                     getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED));
+       }
+
+       if (getconf_int(CNF_TAPERFLUSH) > 0 &&
+           !getconf_boolean(CNF_AUTOFLUSH)) {
+           g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' if taperflush (%d) is greater that 0.\n"),
+                     getconf_int(CNF_TAPERFLUSH));
+       }
+
+       /* Double-check that 'localhost' resolves properly */
+       if ((res = resolve_hostname("localhost", 0, NULL, NULL) != 0)) {
+           g_fprintf(outf, _("ERROR: Cannot resolve `localhost': %s\n"), gai_strerror(res));
+           confbad = 1;
+       }
+
+       if (!getconf_seen(CNF_TAPETYPE)) {
+           g_fprintf(outf,
+                     _("ERROR: no tapetype specified; you must give a value for "
+                       "the 'tapetype' parameter\n"));
+           confbad = 1;
+       }
     }
 
     /*
      * Look up the programs used on the server side.
      */
     if(do_localchk) {
-       if(access(libexecdir, X_OK) == -1) {
-           fprintf(outf, "ERROR: program dir %s: not accessible\n",
-                   libexecdir);
+       /* 
+        * entreprise version will do planner/dumper suid check
+        */
+       if(access(amlibexecdir, X_OK) == -1) {
+           quoted = quote_string(amlibexecdir);
+           g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n."),
+                   quoted);
+           g_fprintf(outf, _("Check permissions\n"));
            pgmbad = 1;
+           amfree(quoted);
        } else {
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, libexecdir, "planner",
-                                       1, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, libexecdir, "dumper",
-                                       1, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, libexecdir, "driver",
-                                       0, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, libexecdir, "taper",
-                                       0, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, libexecdir, "amtrmidx",
-                                       0, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, libexecdir, "amlogroll",
-                                       0, uid_dumpuser);
+           if(test_server_pgm(outf, amlibexecdir, "planner", 1, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, amlibexecdir, "dumper", 1, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, amlibexecdir, "driver", 0, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, amlibexecdir, "taper", 0, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, amlibexecdir, "amtrmidx", 0, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, amlibexecdir, "amlogroll", 0, uid_dumpuser))
+               pgmbad = 1;
        }
        if(access(sbindir, X_OK) == -1) {
-           fprintf(outf, "ERROR: program dir %s: not accessible\n",
+           quoted = quote_string(sbindir);
+           g_fprintf(outf, _("ERROR: Directory %s containing Amanda tools is not accessible\n"),
                    sbindir);
+           g_fprintf(outf, _("Check permissions\n"));
            pgmbad = 1;
+           amfree(quoted);
        } else {
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, sbindir, "amgetconf",
-                                       0, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, sbindir, "amcheck",
-                                       1, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, sbindir, "amdump",
-                                       0, uid_dumpuser);
-           pgmbad = pgmbad \
-                    || test_server_pgm(outf, sbindir, "amreport",
-                                       0, uid_dumpuser);
+           if(test_server_pgm(outf, sbindir, "amgetconf", 0, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, sbindir, "amcheck", 1, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, sbindir, "amdump", 0, uid_dumpuser))
+               pgmbad = 1;
+           if(test_server_pgm(outf, sbindir, "amreport", 0, uid_dumpuser))
+               pgmbad = 1;
        }
        if(access(COMPRESS_PATH, X_OK) == -1) {
-           fprintf(outf, "WARNING: %s is not executable, server-compression and indexing will not work\n",
-                   COMPRESS_PATH);
+           quoted = quote_string(COMPRESS_PATH);
+           g_fprintf(outf, _("WARNING: %s is not executable, server-compression "
+                           "and indexing will not work. \n"),quoted);
+           g_fprintf(outf, _("Check permissions\n"));
+           amfree(quoted);
        }
     }
 
@@ -791,19 +829,18 @@ int start_server_check(fd, do_localchk, do_tapechk)
      */
 
     if(do_localchk || do_tapechk) {
-       char *conf_tapelist;
        char *tapefile;
+       char *newtapefile;
        char *tape_dir;
        char *lastslash;
        char *holdfile;
+        char * tapename;
        struct stat statbuf;
-       
-       conf_tapelist=getconf_str(CNF_TAPELIST);
-       if (*conf_tapelist == '/') {
-           tapefile = stralloc(conf_tapelist);
-       } else {
-           tapefile = stralloc2(config_dir, conf_tapelist);
-       }
+       guint64 part_size, part_cache_max_size, tape_size;
+       part_cache_type_t part_cache_type;
+       char *part_cache_dir;
+
+       tapefile = config_dir_relative(getconf_str(CNF_TAPELIST));
        /*
         * XXX There Really Ought to be some error-checking here... dhw
         */
@@ -815,87 +852,272 @@ int start_server_check(fd, do_localchk, do_tapechk)
         */
        }
        if(access(tape_dir, W_OK) == -1) {
-           fprintf(outf, "ERROR: tapelist dir %s: not writable.\n", tape_dir);
+           quoted = quote_string(tape_dir);
+           g_fprintf(outf, _("ERROR: tapelist dir %s: not writable.\nCheck permissions\n"), 
+                   quoted);
            tapebad = 1;
+           amfree(quoted);
        }
        else if(stat(tapefile, &statbuf) == -1) {
-           fprintf(outf, "ERROR: tapefile %s: %s, you must create an empty file.\n",
-                   tapefile, strerror(errno));
-           tapebad = 1;
-       }
-       else if(access(tapefile, F_OK) != 0) {
-           fprintf(outf, "ERROR: can't access tape list %s\n", tapefile);
-           tapebad = 1;
-       } else if(access(tapefile, F_OK) == 0 && access(tapefile, W_OK) != 0) {
-           fprintf(outf, "ERROR: tape list %s: not writable\n", tapefile);
-           tapebad = 1;
-       } else if(read_tapelist(tapefile)) {
-           fprintf(outf, "ERROR: tape list %s: parse error\n", tapefile);
-           tapebad = 1;
+           if (errno != ENOENT) {
+               quoted = quote_string(tape_dir);
+               g_fprintf(outf, _("ERROR: tapelist %s (%s), "
+                       "you must create an empty file.\n"),
+                       quoted, strerror(errno));
+               tapebad = 1;
+               amfree(quoted);
+           } else {
+               g_fprintf(outf, _("NOTE: tapelist will be created on the next run.\n"));
+           }
+       } else {
+           tapebad |= check_tapefile(outf, tapefile);
+           if (tapebad == 0 && read_tapelist(tapefile)) {
+               quoted = quote_string(tapefile);
+               g_fprintf(outf, _("ERROR: tapelist %s: parse error\n"), quoted);
+               tapebad = 1;
+               amfree(quoted);
+           }
+           newtapefile = stralloc2(tapefile, ".new");
+           tapebad |= check_tapefile(outf, newtapefile);
+           amfree(newtapefile);
+           newtapefile = stralloc2(tapefile, ".amlabel");
+           tapebad |= check_tapefile(outf, newtapefile);
+           amfree(newtapefile);
+           newtapefile = stralloc2(tapefile, ".amlabel.new");
+           tapebad |= check_tapefile(outf, newtapefile);
+           amfree(newtapefile);
+           newtapefile = stralloc2(tapefile, ".yesterday");
+           tapebad |= check_tapefile(outf, newtapefile);
+           amfree(newtapefile);
+           newtapefile = stralloc2(tapefile, ".yesterday.new");
+           tapebad |= check_tapefile(outf, newtapefile);
+           amfree(newtapefile);
        }
-       holdfile = vstralloc(config_dir, "/", "hold", NULL);
+       holdfile = config_dir_relative("hold");
        if(access(holdfile, F_OK) != -1) {
-           fprintf(outf, "WARNING: hold file %s exists\n", holdfile);
+           quoted = quote_string(holdfile);
+           g_fprintf(outf, _("WARNING: hold file %s exists."), holdfile);
+           g_fprintf(outf, _("Amdump will sleep as long as this file exists.\n"));
+           g_fprintf(outf, _("You might want to delete the existing hold file\n"));
+           amfree(quoted);
        }
        amfree(tapefile);
        amfree(tape_dir);
        amfree(holdfile);
        tapename = getconf_str(CNF_TAPEDEV);
-       if (strncmp(tapename, "null:", 5) == 0) {
-           fprintf(outf,
-                   "WARNING: tapedev is %s, dumps will be thrown away\n",
-                   tapename);
-           testtape = 0;
-           do_tapechk = 0;
+       if (tapename == NULL) {
+           if (getconf_str(CNF_TPCHANGER) == NULL) {
+               g_fprintf(outf, _("WARNING:Parameter \"tapedev\" or \"tpchanger\" not specified in amanda.conf.\n"));
+               testtape = 0;
+               do_tapechk = 0;
+           }
+       }
+
+       /* check tapetype-based splitting parameters */
+       part_size = tapetype_get_part_size(tp);
+       part_cache_type = tapetype_get_part_cache_type(tp);
+       part_cache_dir = tapetype_get_part_cache_dir(tp);
+       part_cache_max_size = tapetype_get_part_cache_max_size(tp);
+
+       if (!tapetype_seen(tp, TAPETYPE_PART_SIZE)) {
+           if (tapetype_seen(tp, TAPETYPE_PART_CACHE_TYPE)) {
+               g_fprintf(outf, "ERROR: part-cache-type specified, but no part-size\n");
+               tapebad = 1;
+           }
+           if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
+               g_fprintf(outf, "ERROR: part-cache-dir specified, but no part-size\n");
+               tapebad = 1;
+           }
+           if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
+               g_fprintf(outf, "ERROR: part-cache-max-size specified, but no part-size\n");
+               tapebad = 1;
+           }
+       } else {
+           switch (part_cache_type) {
+           case PART_CACHE_TYPE_DISK:
+               if (!tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)
+                           || !part_cache_dir || !*part_cache_dir) {
+                   g_fprintf(outf,
+                       "ERROR: part-cache-type is DISK, but no part-cache-dir specified\n");
+                   tapebad = 1;
+               } else {
+                   if(get_fs_usage(part_cache_dir, NULL, &fsusage) == -1) {
+                       g_fprintf(outf, "ERROR: part-cache-dir '%s': %s\n",
+                               part_cache_dir, strerror(errno));
+                       tapebad = 1;
+                   } else {
+                       kb_avail = fsusage.fsu_bavail_top_bit_set?
+                           0 : fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
+                       kb_needed = part_size;
+                       if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
+                           kb_needed = part_cache_max_size;
+                       }
+                       if (kb_avail < kb_needed) {
+                           g_fprintf(outf,
+                               "ERROR: part-cache-dir has %ju %sB available, but needs %ju %sB\n",
+                               kb_avail/(uintmax_t)unitdivisor, displayunit,
+                               kb_needed/(uintmax_t)unitdivisor, displayunit);
+                           tapebad = 1;
+                       }
+                   }
+               }
+               break;
+
+           case PART_CACHE_TYPE_MEMORY:
+               kb_avail = physmem_total() / 1024;
+               kb_needed = part_size;
+               if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
+                   kb_needed = part_cache_max_size;
+               }
+               if (kb_avail < kb_needed) {
+                   g_fprintf(outf,
+                       "ERROR: system has %ju %sB memory, but part cache needs %ju %sB\n",
+                       kb_avail/(uintmax_t)unitdivisor, displayunit,
+                       kb_needed/(uintmax_t)unitdivisor, displayunit);
+                   tapebad = 1;
+               }
+
+               /* FALL THROUGH */
+
+           case PART_CACHE_TYPE_NONE:
+               if (tapetype_seen(tp, TAPETYPE_PART_CACHE_DIR)) {
+                   g_fprintf(outf,
+                       "ERROR: part-cache-dir specified, but part-cache-type is not DISK\n");
+                   tapebad = 1;
+               }
+               break;
+           }
+       }
+
+       if (tapetype_seen(tp, TAPETYPE_PART_SIZE) && part_size == 0
+               && part_cache_type != PART_CACHE_TYPE_NONE) {
+           g_fprintf(outf,
+                   "ERROR: part_size is zero, but part-cache-type is not 'none'\n");
+           tapebad = 1;
+       }
+
+       if (tapetype_seen(tp, TAPETYPE_PART_CACHE_MAX_SIZE)) {
+           if (part_cache_type == PART_CACHE_TYPE_NONE) {
+               g_fprintf(outf,
+                   "ERROR: part-cache-max-size is specified but no part cache is in use\n");
+               tapebad = 1;
+           }
+
+           if (part_cache_max_size > part_size) {
+               g_fprintf(outf,
+                   "WARNING: part-cache-max-size is greater than part-size\n");
+           }
+       }
+
+       tape_size = tapetype_get_length(tp);
+       if (part_size && part_size * 1000 < tape_size) {
+           g_fprintf(outf,
+                     _("WARNING: part-size of %ju %sB < 0.1%% of tape length.\n"),
+                     (uintmax_t)part_size/(uintmax_t)unitdivisor, displayunit);
+           if (!printed_small_part_size_warning) {
+               printed_small_part_size_warning = TRUE;
+               g_fprintf(outf, "%s", small_part_size_warning);
+           }
+       } else if (part_cache_max_size && part_cache_max_size * 1000 < tape_size) {
+           g_fprintf(outf,
+                     _("WARNING: part-cache-max-size of %ju %sB < 0.1%% of tape length.\n"),
+                     (uintmax_t)part_cache_max_size/(uintmax_t)unitdivisor, displayunit);
+           if (!printed_small_part_size_warning) {
+               printed_small_part_size_warning = TRUE;
+               g_fprintf(outf, "%s", small_part_size_warning);
+           }
        }
     }
 
     /* check available disk space */
 
     if(do_localchk) {
-       for(hdp = holdingdisks; hdp != NULL; hdp = hdp->next) {
-           if(get_fs_stats(hdp->diskdir, &fs) == -1) {
-               fprintf(outf, "ERROR: holding dir %s: %s, you must create a directory.\n",
-                       hdp->diskdir, strerror(errno));
+       identlist_t    il;
+       holdingdisk_t *hdp;
+
+       for (il = getconf_identlist(CNF_HOLDINGDISK);
+               il != NULL;
+               il = il->next) {
+           hdp = lookup_holdingdisk(il->data);
+           quoted = quote_string(holdingdisk_get_diskdir(hdp));
+           if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1) {
+               g_fprintf(outf, _("ERROR: holding dir %s (%s), "
+                       "you must create a directory.\n"),
+                       quoted, strerror(errno));
                disklow = 1;
+               amfree(quoted);
+               continue;
            }
-           else if(access(hdp->diskdir, W_OK) == -1) {
-               fprintf(outf, "ERROR: holding disk %s: not writable: %s.\n",
-                       hdp->diskdir, strerror(errno));
+
+           /* do the division first to avoid potential integer overflow */
+           if (fsusage.fsu_bavail_top_bit_set)
+               kb_avail = 0;
+           else
+               kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
+
+           if(access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
+               g_fprintf(outf, _("ERROR: holding disk %s: not writable: %s.\n"),
+                       quoted, strerror(errno));
+               g_fprintf(outf, _("Check permissions\n"));
                disklow = 1;
            }
-           else if(fs.avail == -1) {
-               fprintf(outf,
-                       "WARNING: holding disk %s: available space unknown (%ld KB requested)\n",
-                       hdp->diskdir, (long)hdp->disksize);
+           else if(access(holdingdisk_get_diskdir(hdp), X_OK) == -1) {
+               g_fprintf(outf, _("ERROR: holding disk %s: not searcheable: %s.\n"),
+                       quoted, strerror(errno));
+               g_fprintf(outf, _("Check permissions of ancestors of %s\n"), quoted);
                disklow = 1;
            }
-           else if(hdp->disksize > 0) {
-               if(fs.avail < hdp->disksize) {
-                   fprintf(outf,
-                           "WARNING: holding disk %s: only %ld %sB free (%ld %sB requested)\n",
-                           hdp->diskdir, (long)fs.avail/unitdivisor, displayunit,
-                           (long)hdp->disksize/unitdivisor, displayunit);
+           else if(holdingdisk_get_disksize(hdp) > (off_t)0) {
+               if(kb_avail == 0) {
+                   g_fprintf(outf,
+                           _("WARNING: holding disk %s: "
+                           "no space available (%lld %sB requested)\n"), quoted,
+                           (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
+                           displayunit);
                    disklow = 1;
                }
-               else
-                   fprintf(outf,
-                           "Holding disk %s: %ld %sB disk space available, that's plenty\n",
-                           hdp->diskdir, fs.avail/unitdivisor, displayunit);
+               else if(kb_avail < holdingdisk_get_disksize(hdp)) {
+                   g_fprintf(outf,
+                           _("WARNING: holding disk %s: "
+                           "only %lld %sB available (%lld %sB requested)\n"), quoted,
+                           (long long)(kb_avail / (off_t)unitdivisor),
+                           displayunit,
+                           (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
+                           displayunit);
+                   disklow = 1;
+               }
+               else {
+                   g_fprintf(outf,
+                           _("Holding disk %s: %lld %sB disk space available,"
+                           " using %lld %sB as requested\n"),
+                           quoted,
+                           (long long)(kb_avail / (off_t)unitdivisor),
+                           displayunit,
+                           (long long)(holdingdisk_get_disksize(hdp)/(off_t)unitdivisor),
+                           displayunit);
+               }
            }
            else {
-               if(fs.avail < -hdp->disksize) {
-                   fprintf(outf,
-                           "WARNING: holding disk %s: only %ld %sB free, using nothing\n",
-                           hdp->diskdir, fs.avail/unitdivisor, displayunit);
+               if(kb_avail < -holdingdisk_get_disksize(hdp)) {
+                   g_fprintf(outf,
+                           _("WARNING: holding disk %s: "
+                           "only %lld %sB free, using nothing\n"),
+                           quoted, (long long)(kb_avail / (off_t)unitdivisor),
+                           displayunit);
+                   g_fprintf(outf, _("WARNING: Not enough free space specified in amanda.conf\n"));
                    disklow = 1;
                }
-               else
-                   fprintf(outf,
-                           "Holding disk %s: %ld %sB disk space available, using %ld %sB\n",
-                           hdp->diskdir, fs.avail/unitdivisor, displayunit,
-                           (fs.avail + hdp->disksize)/unitdivisor, displayunit);
+               else {
+                   g_fprintf(outf,
+                           _("Holding disk %s: %lld %sB disk space available, using %lld %sB\n"),
+                           quoted,
+                           (long long)(kb_avail/(off_t)unitdivisor),
+                           displayunit,
+                           (long long)((kb_avail + holdingdisk_get_disksize(hdp)) / (off_t)unitdivisor),
+                           displayunit);
+               }
            }
+           amfree(quoted);
        }
     }
 
@@ -908,124 +1130,81 @@ int start_server_check(fd, do_localchk, do_tapechk)
        struct stat stat_old;
        struct stat statbuf;
 
-       conf_logdir = getconf_str(CNF_LOGDIR);
-       if (*conf_logdir == '/') {
-           conf_logdir = stralloc(conf_logdir);
-       } else {
-           conf_logdir = stralloc2(config_dir, conf_logdir);
-       }
+       conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
        logfile = vstralloc(conf_logdir, "/log", NULL);
 
+       quoted = quote_string(conf_logdir);
        if(stat(conf_logdir, &statbuf) == -1) {
-           fprintf(outf, "ERROR: logdir %s: %s, you must create a directory.\n",
-                   conf_logdir, strerror(errno));
+           g_fprintf(outf, _("ERROR: logdir %s (%s), you must create directory.\n"),
+                   quoted, strerror(errno));
            disklow = 1;
        }
        else if(access(conf_logdir, W_OK) == -1) {
-           fprintf(outf, "ERROR: log dir %s: not writable\n", conf_logdir);
+           g_fprintf(outf, _("ERROR: log dir %s: not writable\n"), quoted);
            logbad = 1;
        }
+       amfree(quoted);
 
-       if(access(logfile, F_OK) == 0) {
+       if(logbad == 0 && access(logfile, F_OK) == 0) {
            testtape = 0;
-           logbad = 1;
-           if(access(logfile, W_OK) != 0)
-               fprintf(outf, "ERROR: log file %s: not writable\n",
-                       logfile);
+           logbad = 2;
+           if(access(logfile, W_OK) != 0) {
+               quoted = quote_string(logfile);
+               g_fprintf(outf, _("ERROR: log file %s: not writable\n"), quoted);
+               amfree(quoted);
+           }
        }
 
        olddir = vstralloc(conf_logdir, "/oldlog", NULL);
-       if (stat(olddir,&stat_old) == 0) { /* oldlog exist */
+       quoted = quote_string(olddir);
+       if (logbad == 0 && stat(olddir,&stat_old) == 0) { /* oldlog exist */
            if(!(S_ISDIR(stat_old.st_mode))) {
-               fprintf(outf, "ERROR: oldlog directory \"%s\" is not a directory\n", olddir);
+               g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
+                       quoted);
+               g_fprintf(outf, _("Remove the entry and create a new directory\n"));
+               logbad = 1;
            }
-           if(access(olddir, W_OK) == -1) {
-               fprintf(outf, "ERROR: oldlog dir %s: not writable\n", olddir);
+           if(logbad == 0 && access(olddir, W_OK) == -1) {
+               g_fprintf(outf, _("ERROR: oldlog dir %s: not writable\n"), quoted);
+               g_fprintf(outf, _("Check permissions\n"));
+               logbad = 1;
            }
        }
-       else if(lstat(olddir,&stat_old) == 0) {
-           fprintf(outf, "ERROR: oldlog directory \"%s\" is not a directory\n", olddir);
+       else if(logbad == 0 && lstat(olddir,&stat_old) == 0) {
+           g_fprintf(outf, _("ERROR: oldlog directory %s is not a directory\n"),
+                   quoted);
+               g_fprintf(outf, _("Remove the entry and create a new directory\n"));
+           logbad = 1;
        }
+       amfree(quoted);
 
-       if (testtape) {
+       if (logbad == 0 && testtape) {
            logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
            if (access(logfile, F_OK) == 0) {
                testtape = 0;
-               logbad = 1;
+               logbad = 2;
            }
        }
 
+       amfree(olddir);
        amfree(logfile);
        amfree(conf_logdir);
     }
 
     if (testtape) {
-       /* check that the tape is a valid amanda tape */
-
-       tapedays = getconf_int(CNF_TAPECYCLE);
-       labelstr = getconf_str(CNF_LABELSTR);
-       tapename = getconf_str(CNF_TAPEDEV);
-
-       if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
-           fprintf(outf,
-                   "WARNING: if a tape changer is not available, runtapes must be set to 1\n");
-       }
-
-       if(changer_init() && (tapename = taper_scan()) == NULL) {
-           fprintf(outf, "ERROR: %s\n", changer_resultstr);
-           tapebad = 1;
-       } else if(tape_access(tapename,F_OK|R_OK|W_OK) == -1) {
-           fprintf(outf, "ERROR: %s: %s\n", tapename, strerror(errno));
-           tapebad = 1;
-       } else if((errstr = tape_rdlabel(tapename, &datestamp, &label)) != NULL) {
-           fprintf(outf, "ERROR: %s: %s\n", tapename, errstr);
-           tapebad = 1;
-       } else if(strcmp(label, FAKE_LABEL) != 0) {
-           if(!match(labelstr, label)) {
-               fprintf(outf, "ERROR: label %s doesn't match labelstr \"%s\"\n",
-                       label, labelstr);
-               tapebad = 1;
-           }
-           else {
-               tp = lookup_tapelabel(label);
-               if(tp == NULL) {
-                   fprintf(outf, "ERROR: label %s match labelstr but it not listed in the tapelist file.\n", label);
-                   tapebad = 1;
-               }
-               else if(tp != NULL && !reusable_tape(tp)) {
-                   fprintf(outf, "ERROR: cannot overwrite active tape %s\n",
-                           label);
-                   tapebad = 1;
-               }
-           }
-
-       }
-
-       if(tapebad) {
-           tape_t *exptape = lookup_last_reusable_tape(0);
-           fprintf(outf, "       (expecting ");
-           if(exptape != NULL) fprintf(outf, "tape %s or ", exptape->label);
-           fprintf(outf, "a new tape)\n");
-       }
-
-       if(!tapebad && overwrite) {
-           if((errstr = tape_writable(tapename)) != NULL) {
-               fprintf(outf,
-                       "ERROR: tape %s label ok, but is not writable\n",
-                       label);
-               tapebad = 1;
-           }
-           else fprintf(outf, "Tape %s is writable\n", label);
-       }
-       else fprintf(outf, "NOTE: skipping tape-writable test\n");
-
-       if(!tapebad)
-           fprintf(outf, "Tape %s label ok\n", label);
+        tapebad = !test_tape_status(outf);
     } else if (do_tapechk) {
-       fprintf(outf, "WARNING: skipping tape test because amdump or amflush seem to be running\n");
-       fprintf(outf, "WARNING: if they are not, you must run amcleanup\n");
+       g_fprintf(outf, _("WARNING: skipping tape test because amdump or amflush seem to be running\n"));
+       g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
+    } else if (logbad == 2) {
+       g_fprintf(outf, _("NOTE: amdump or amflush seem to be running\n"));
+       g_fprintf(outf, _("NOTE: if they are not, you must run amcleanup\n"));
+
+       /* we skipped the tape checks, but this is just a NOTE and
+        * should not result in a nonzero exit status, so reset logbad to 0 */
+       logbad = 0;
     } else {
-       fprintf(outf, "NOTE: skipping tape checks\n");
+       g_fprintf(outf, _("NOTE: skipping tape checks\n"));
     }
 
     /*
@@ -1048,169 +1227,358 @@ int start_server_check(fd, do_localchk, do_tapechk)
        char *host;
        char *disk;
        int conf_tapecycle, conf_runspercycle;
+       identlist_t pp_scriptlist;
 
        conf_tapecycle = getconf_int(CNF_TAPECYCLE);
        conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
 
        if(conf_tapecycle <= conf_runspercycle) {
-               fprintf(outf, "WARNING: tapecycle (%d) <= runspercycle (%d).\n",
+               g_fprintf(outf, _("WARNING: tapecycle (%d) <= runspercycle (%d).\n"),
                        conf_tapecycle, conf_runspercycle);
        }
 
-       conf_infofile = stralloc(getconf_str(CNF_INFOFILE));
-       if (*conf_infofile != '/') {
-           char *ci = stralloc2(config_dir, conf_infofile);
-           amfree(conf_infofile);
-           conf_infofile = ci;
-       }
-       conf_indexdir = stralloc(getconf_str(CNF_INDEXDIR));
-       if (*conf_indexdir != '/') {
-           char *ci = stralloc2(config_dir, conf_indexdir);
-           amfree(conf_indexdir);
-           conf_indexdir = ci;
-       }
-#if TEXTDB
+       conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
+       conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
+
+       quoted = quote_string(conf_infofile);
        if(stat(conf_infofile, &statbuf) == -1) {
-           fprintf(outf, "NOTE: info dir %s: does not exist\n", conf_infofile);
-           fprintf(outf, "NOTE: it will be created on the next run.\n");
+           if (errno == ENOENT) {
+               g_fprintf(outf, _("NOTE: conf info dir %s does not exist\n"),
+                       quoted);
+               g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
+           } else {
+               g_fprintf(outf, _("ERROR: conf info dir %s (%s)\n"),
+                       quoted, strerror(errno));
+               infobad = 1;
+           }   
            amfree(conf_infofile);
        } else if (!S_ISDIR(statbuf.st_mode)) {
-           fprintf(outf, "ERROR: info dir %s: not a directory\n", conf_infofile);
+           g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"), quoted);
+           g_fprintf(outf, _("Remove the entry and create a new directory\n"));
            amfree(conf_infofile);
            infobad = 1;
        } else if (access(conf_infofile, W_OK) == -1) {
-           fprintf(outf, "ERROR: info dir %s: not writable\n", conf_infofile);
+           g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
+           g_fprintf(outf, _("Check permissions\n"));
            amfree(conf_infofile);
            infobad = 1;
        } else {
+           char *errmsg = NULL;
+           if (check_infofile(conf_infofile, &origq, &errmsg) == -1) {
+               g_fprintf(outf, "ERROR: Can't copy infofile: %s\n", errmsg);
+               infobad = 1;
+               amfree(errmsg);
+           }
            strappend(conf_infofile, "/");
        }
-#endif
-       while(!empty(*origqp)) {
-           hostp = origqp->head->host;
+       amfree(quoted);
+
+       while(!empty(origq)) {
+           hostp = origq.head->host;
            host = sanitise_filename(hostp->hostname);
-#if TEXTDB
            if(conf_infofile) {
                hostinfodir = newstralloc2(hostinfodir, conf_infofile, host);
+               quoted = quote_string(hostinfodir);
                if(stat(hostinfodir, &statbuf) == -1) {
-                   fprintf(outf, "NOTE: info dir %s: does not exist\n",
-                           hostinfodir);
-                   fprintf(outf, "NOTE: it will be created on the next run.\n");
+                   if (errno == ENOENT) {
+                       g_fprintf(outf, _("NOTE: host info dir %s does not exist\n"),
+                               quoted);
+                       g_fprintf(outf,
+                               _("NOTE: it will be created on the next run.\n"));
+                   } else {
+                       g_fprintf(outf, _("ERROR: host info dir %s (%s)\n"),
+                               quoted, strerror(errno));
+                       infobad = 1;
+                   }   
                    amfree(hostinfodir);
                } else if (!S_ISDIR(statbuf.st_mode)) {
-                   fprintf(outf, "ERROR: info dir %s: not a directory\n",
-                           hostinfodir);
+                   g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
+                           quoted);
+                   g_fprintf(outf, _("Remove the entry and create a new directory\n"));
                    amfree(hostinfodir);
                    infobad = 1;
                } else if (access(hostinfodir, W_OK) == -1) {
-                   fprintf(outf, "ERROR: info dir %s: not writable\n",
-                           hostinfodir);
+                   g_fprintf(outf, _("ERROR: info dir %s: not writable\n"), quoted);
+                   g_fprintf(outf, _("Check permissions\n"));
                    amfree(hostinfodir);
                    infobad = 1;
                } else {
                    strappend(hostinfodir, "/");
                }
+               amfree(quoted);
            }
-#endif
            for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
                disk = sanitise_filename(dp->name);
-#if TEXTDB
                if(hostinfodir) {
+                   char *quotedif;
+
                    diskdir = newstralloc2(diskdir, hostinfodir, disk);
                    infofile = vstralloc(diskdir, "/", "info", NULL);
+                   quoted = quote_string(diskdir);
+                   quotedif = quote_string(infofile);
                    if(stat(diskdir, &statbuf) == -1) {
-                       fprintf(outf, "NOTE: info dir %s: does not exist\n",
-                               diskdir);
-                       fprintf(outf, "NOTE: it will be created on the next run.\n");
+                       if (errno == ENOENT) {
+                           g_fprintf(outf, _("NOTE: info dir %s does not exist\n"),
+                               quoted);
+                           g_fprintf(outf,
+                               _("NOTE: it will be created on the next run.\n"));
+                       } else {
+                           g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
+                                   quoted, strerror(errno));
+                           infobad = 1;
+                       }       
                    } else if (!S_ISDIR(statbuf.st_mode)) {
-                       fprintf(outf, "ERROR: info dir %s: not a directory\n",
-                               diskdir);
+                       g_fprintf(outf, _("ERROR: info dir %s: not a directory\n"),
+                               quoted);
+                       g_fprintf(outf, _("Remove the entry and create a new directory\n"));
                        infobad = 1;
                    } else if (access(diskdir, W_OK) == -1) {
-                       fprintf(outf, "ERROR: info dir %s: not writable\n",
-                               diskdir);
+                       g_fprintf(outf, _("ERROR: info dir %s: not writable\n"),
+                               quoted);
+                       g_fprintf(outf,_("Check permissions\n"));
                        infobad = 1;
                    } else if(stat(infofile, &statbuf) == -1) {
-                       fprintf(outf, "WARNING: info file %s: does not exist\n",
-                               infofile);
-                       fprintf(outf, "NOTE: it will be created on the next run.\n");
+                       if (errno == ENOENT) {
+                           g_fprintf(outf, _("NOTE: info file %s does not exist\n"),
+                                   quotedif);
+                           g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
+                       } else {
+                           g_fprintf(outf, _("ERROR: info dir %s (%s)\n"),
+                                   quoted, strerror(errno));
+                           infobad = 1;
+                       }       
                    } else if (!S_ISREG(statbuf.st_mode)) {
-                       fprintf(outf, "ERROR: info file %s: not a file\n",
-                               infofile);
+                       g_fprintf(outf, _("ERROR: info file %s: not a file\n"),
+                               quotedif);
+                       g_fprintf(outf, _("Remove the entry and create a new file\n"));
                        infobad = 1;
                    } else if (access(infofile, R_OK) == -1) {
-                       fprintf(outf, "ERROR: info file %s: not readable\n",
-                               infofile);
+                       g_fprintf(outf, _("ERROR: info file %s: not readable\n"),
+                               quotedif);
                        infobad = 1;
                    }
+                   amfree(quotedif);
+                   amfree(quoted);
                    amfree(infofile);
                }
-#endif
                if(dp->index) {
                    if(! indexdir_checked) {
+                       quoted = quote_string(conf_indexdir);
                        if(stat(conf_indexdir, &statbuf) == -1) {
-                           fprintf(outf, "NOTE: index dir %s: does not exist\n",
-                                   conf_indexdir);
-                           fprintf(outf, "NOTE: it will be created on the next run.\n");
+                           if (errno == ENOENT) {
+                               g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
+                                       quoted);
+                               g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
+                           } else {
+                               g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
+                                       quoted, strerror(errno));
+                               indexbad = 1;
+                           }   
                            amfree(conf_indexdir);
                        } else if (!S_ISDIR(statbuf.st_mode)) {
-                           fprintf(outf, "ERROR: index dir %s: not a directory\n",
-                                   conf_indexdir);
+                           g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
+                                   quoted);
+                           g_fprintf(outf, _("Remove the entry and create a new directory\n"));
                            amfree(conf_indexdir);
                            indexbad = 1;
                        } else if (access(conf_indexdir, W_OK) == -1) {
-                           fprintf(outf, "ERROR: index dir %s: not writable\n",
-                                   conf_indexdir);
+                           g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
+                                   quoted);
                            amfree(conf_indexdir);
                            indexbad = 1;
                        } else {
                            strappend(conf_indexdir, "/");
                        }
                        indexdir_checked = 1;
+                       amfree(quoted);
                    }
                    if(conf_indexdir) {
                        if(! hostindexdir_checked) {
                            hostindexdir = stralloc2(conf_indexdir, host);
+                           quoted = quote_string(hostindexdir);
                            if(stat(hostindexdir, &statbuf) == -1) {
-                               fprintf(outf, "NOTE: index dir %s: does not exist\n",
-                                       hostindexdir);
-                               fprintf(outf, "NOTE: it will be created on the next run.\n");
+                               if (errno == ENOENT) {
+                                   g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
+                                           quoted);
+                                   g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
+                               } else {
+                                   g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
+                                           quoted, strerror(errno));
+                                   indexbad = 1;
+                               }
                                amfree(hostindexdir);
                            } else if (!S_ISDIR(statbuf.st_mode)) {
-                               fprintf(outf, "ERROR: index dir %s: not a directory\n",
-                                       hostindexdir);
+                               g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
+                                       quoted);
+                               g_fprintf(outf, _("Remove the entry and create a new directory\n"));
                                amfree(hostindexdir);
                                indexbad = 1;
                            } else if (access(hostindexdir, W_OK) == -1) {
-                               fprintf(outf, "ERROR: index dir %s: not writable\n",
-                                       hostindexdir);
+                               g_fprintf(outf, _("ERROR: index dir %s: not writable\n"),
+                                       quoted);
                                amfree(hostindexdir);
                                indexbad = 1;
                            } else {
                                strappend(hostindexdir, "/");
                            }
                            hostindexdir_checked = 1;
+                           amfree(quoted);
                        }
                        if(hostindexdir) {
                            diskdir = newstralloc2(diskdir, hostindexdir, disk);
+                           quoted = quote_string(diskdir);
                            if(stat(diskdir, &statbuf) == -1) {
-                               fprintf(outf, "NOTE: index dir %s: does not exist\n",
-                                       diskdir);
-                               fprintf(outf, "NOTE: it will be created on the next run.\n");
+                               if (errno == ENOENT) {
+                                   g_fprintf(outf, _("NOTE: index dir %s does not exist\n"),
+                                           quoted);
+                                   g_fprintf(outf, _("NOTE: it will be created on the next run.\n"));
+                               } else {
+                                   g_fprintf(outf, _("ERROR: index dir %s (%s)\n"),
+                                       quoted, strerror(errno));
+                                   indexbad = 1;
+                               }       
                            } else if (!S_ISDIR(statbuf.st_mode)) {
-                               fprintf(outf, "ERROR: index dir %s: not a directory\n",
-                                       diskdir);
+                               g_fprintf(outf, _("ERROR: index dir %s: not a directory\n"),
+                                       quoted);
+                               g_fprintf(outf, _("Remove the entry and create a new directory\n"));
                                indexbad = 1;
                            } else if (access(diskdir, W_OK) == -1) {
-                               fprintf(outf, "ERROR: index dir %s: is not writable\n",
-                                       diskdir);
+                               g_fprintf(outf, _("ERROR: index dir %s: is not writable\n"),
+                                       quoted);
                                indexbad = 1;
                            }
+                           amfree(quoted);
+                       }
+                   }
+               }
+
+               if ( dp->encrypt == ENCRYPT_SERV_CUST ) {
+                 if ( dp->srv_encrypt[0] == '\0' ) {
+                   g_fprintf(outf, _("ERROR: server encryption program not specified\n"));
+                   g_fprintf(outf, _("Specify \"server-custom-encrypt\" in the dumptype\n"));
+                   pgmbad = 1;
+                 }
+                 else if(access(dp->srv_encrypt, X_OK) == -1) {
+                   g_fprintf(outf, _("ERROR: %s is not executable, server encryption will not work\n"),
+                           dp->srv_encrypt );
+                  g_fprintf(outf, _("Check file type\n"));
+                   pgmbad = 1;
+                 }
+               }
+               if ( dp->compress == COMP_SERVER_CUST ) {
+                 if ( dp->srvcompprog[0] == '\0' ) {
+                   g_fprintf(outf, _("ERROR: server custom compression program "
+                                   "not specified\n"));
+                   g_fprintf(outf, _("Specify \"server-custom-compress\" in "
+                                   "the dumptype\n"));
+                   pgmbad = 1;
+                 }
+                 else if(access(dp->srvcompprog, X_OK) == -1) {
+                   quoted = quote_string(dp->srvcompprog);
+
+                   g_fprintf(outf, _("ERROR: %s is not executable, server custom "
+                                   "compression will not work\n"),
+                           quoted);
+                   amfree(quoted);
+                  g_fprintf(outf, _("Check file type\n"));
+                   pgmbad = 1;
+                 }
+               }
+
+               /* check deprecated splitting parameters */
+               if (dumptype_seen(dp->config, DUMPTYPE_TAPE_SPLITSIZE)
+                   || dumptype_seen(dp->config, DUMPTYPE_SPLIT_DISKBUFFER)
+                   || dumptype_seen(dp->config, DUMPTYPE_FALLBACK_SPLITSIZE)) {
+                   tape_size = tapetype_get_length(tp);
+                   if (dp->tape_splitsize > tape_size) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: tape-splitsize > tape size\n"),
+                                 hostp->hostname, dp->name);
+                       pgmbad = 1;
+                   }
+                   if (dp->tape_splitsize && dp->fallback_splitsize * 1024 > physmem_total()) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: fallback-splitsize > total available memory\n"),
+                                 hostp->hostname, dp->name);
+                       pgmbad = 1;
+                   }
+                   if (dp->tape_splitsize && dp->fallback_splitsize > tape_size) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: fallback-splitsize > tape size\n"),
+                                 hostp->hostname, dp->name);
+                       pgmbad = 1;
+                   }
+
+                   /* also check for part sizes that are too small */
+                   if (dp->tape_splitsize && dp->tape_splitsize * 1000 < tape_size) {
+                       g_fprintf(outf,
+                                 _("WARNING: %s %s: tape-splitsize of %ju %sB < 0.1%% of tape length.\n"),
+                                 hostp->hostname, dp->name,
+                                 (uintmax_t)dp->tape_splitsize/(uintmax_t)unitdivisor,
+                                 displayunit);
+                       if (!printed_small_part_size_warning) {
+                           printed_small_part_size_warning = TRUE;
+                           g_fprintf(outf, "%s", small_part_size_warning);
                        }
                    }
+
+                   /* fallback splitsize will be used if split_diskbuffer is empty or NULL */
+                   if (dp->tape_splitsize != 0 && dp->fallback_splitsize != 0 &&
+                           (dp->split_diskbuffer == NULL ||
+                            dp->split_diskbuffer[0] == '\0') &&
+                           dp->fallback_splitsize * 1000 < tape_size) {
+                       g_fprintf(outf,
+                             _("WARNING: %s %s: fallback-splitsize of %ju %sB < 0.1%% of tape length.\n"),
+                             hostp->hostname, dp->name,
+                             (uintmax_t)dp->fallback_splitsize/(uintmax_t)unitdivisor,
+                             displayunit);
+                       if (!printed_small_part_size_warning) {
+                           printed_small_part_size_warning = TRUE;
+                           g_fprintf(outf, "%s", small_part_size_warning);
+                       }
+                   }
+               }
+
+               if (dp->data_path == DATA_PATH_DIRECTTCP) {
+                   if (dp->compress != COMP_NONE) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: Can't compress directtcp data-path\n"),
+                                 hostp->hostname, dp->name);
+                       pgmbad = 1;
+                   }
+                   if (dp->encrypt != ENCRYPT_NONE) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: Can't encrypt directtcp data-path\n"),
+                                 hostp->hostname, dp->name);
+                       pgmbad = 1;
+                   }
+                   if (dp->to_holdingdisk == HOLD_REQUIRED) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: Holding disk can't be use for directtcp data-path\n"),
+                                 hostp->hostname, dp->name);
+                       pgmbad = 1;
+                   }
+               }
+
+               for (pp_scriptlist = dp->pp_scriptlist; pp_scriptlist != NULL;
+                    pp_scriptlist = pp_scriptlist->next) {
+                   pp_script_t *pp_script = lookup_pp_script((char *)pp_scriptlist->data);
+                   g_assert(pp_script != NULL);
+                   if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
+                       pp_script_get_execute_on(pp_script) & EXECUTE_ON_PRE_HOST_BACKUP) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: Can't run pre-host-backup script on client\n"),
+                                 hostp->hostname, dp->name);
+                   } else if (pp_script_get_execute_where(pp_script) == ES_CLIENT &&
+                       pp_script_get_execute_on(pp_script) & EXECUTE_ON_POST_HOST_BACKUP) {
+                       g_fprintf(outf,
+                                 _("ERROR: %s %s: Can't run post-host-backup script on client\n"),
+                                 hostp->hostname, dp->name);
+                   }
                }
+
                amfree(disk);
-               remove_disk(origqp, dp);
+               remove_disk(&origq, dp);
            }
            amfree(host);
            amfree(hostindexdir);
@@ -1223,19 +1591,18 @@ int start_server_check(fd, do_localchk, do_tapechk)
     }
 
     amfree(datestamp);
-    amfree(label);
-    amfree(config_dir);
-    amfree(config_name);
 
-    fprintf(outf, "Server check took %s seconds\n", walltime_str(curclock()));
+    g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
 
     fflush(outf);
-
-    malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
-    if(malloc_size_1 != malloc_size_2) {
-       malloc_list(fd, malloc_hist_1, malloc_hist_2);
-    }
+    g_debug("userbad: %d", userbad);
+    g_debug("confbad: %d", confbad);
+    g_debug("tapebad: %d", tapebad);
+    g_debug("disklow: %d", disklow);
+    g_debug("logbad: %d", logbad);
+    g_debug("infobad: %d", infobad);
+    g_debug("indexbad: %d", indexbad);
+    g_debug("pgmbad: %d", pgmbad);
 
     exit(userbad \
         || confbad \
@@ -1245,7 +1612,7 @@ int start_server_check(fd, do_localchk, do_tapechk)
         || infobad \
         || indexbad \
         || pgmbad);
-    /* NOTREACHED */
+    /*NOTREACHED*/
     return 0;
 }
 
@@ -1253,13 +1620,9 @@ int start_server_check(fd, do_localchk, do_tapechk)
 
 int remote_errors;
 FILE *outf;
-int amanda_port;
-
-#ifdef KRB4_SECURITY
-int kamanda_port;
-#endif
 
-static void handle_response P((proto_t *p, pkt_t *pkt));
+static void handle_result(void *, pkt_t *, security_handle_t *);
+void start_host(am_host_t *hostp);
 
 #define HOST_READY                             ((void *)0)     /* must be 0 */
 #define HOST_ACTIVE                            ((void *)1)
@@ -1269,24 +1632,20 @@ static void handle_response P((proto_t *p, pkt_t *pkt));
 #define DISK_ACTIVE                            ((void *)1)
 #define DISK_DONE                              ((void *)2)
 
-int start_host(hostp)
-    am_host_t *hostp;
+void
+start_host(
+    am_host_t *hostp)
 {
     disk_t *dp;
     char *req = NULL;
-    int req_len = 0;
-    int rc;
+    size_t req_len = 0;
     int disk_count;
+    const security_driver_t *secdrv;
     char number[NUM_STR_SIZE];
+    estimate_t estimate;
 
     if(hostp->up != HOST_READY) {
-       return 0;
-    }
-
-    if (strncmp (hostp->hostname,"localhost",9) == 0) {
-        fprintf(outf,
-                    "WARNING: Usage of fully qualified hostname recommended for Client %s.\n",
-                    hostp->hostname);
+       return;
     }
 
     /*
@@ -1304,43 +1663,51 @@ int start_host(hostp)
                                          fe_req_options_hostname);
        int has_maxdumps = am_has_feature(hostp->features,
                                          fe_req_options_maxdumps);
+       int has_config   = am_has_feature(hostp->features,
+                                         fe_req_options_config);
 
        if(!am_has_feature(hostp->features, fe_selfcheck_req) &&
           !am_has_feature(hostp->features, fe_selfcheck_req_device)) {
-           fprintf(outf,
-                   "ERROR: Client %s does not support selfcheck REQ packet.\n",
+           g_fprintf(outf,
+                   _("ERROR: Client %s does not support selfcheck REQ packet.\n"),
                    hostp->hostname);
+           g_fprintf(outf, _("Client might be of a very old version\n"));
        }
        if(!am_has_feature(hostp->features, fe_selfcheck_rep)) {
-           fprintf(outf,
-                   "ERROR: Client %s does not support selfcheck REP packet.\n",
+           g_fprintf(outf,
+                   _("ERROR: Client %s does not support selfcheck REP packet.\n"),
                    hostp->hostname);
+           g_fprintf(outf, _("Client might be of a very old version\n"));
        }
        if(!am_has_feature(hostp->features, fe_sendsize_req_options) &&
           !am_has_feature(hostp->features, fe_sendsize_req_no_options) &&
           !am_has_feature(hostp->features, fe_sendsize_req_device)) {
-           fprintf(outf,
-                   "ERROR: Client %s does not support sendsize REQ packet.\n",
+           g_fprintf(outf,
+                   _("ERROR: Client %s does not support sendsize REQ packet.\n"),
                    hostp->hostname);
+           g_fprintf(outf, _("Client might be of a very old version\n"));
        }
        if(!am_has_feature(hostp->features, fe_sendsize_rep)) {
-           fprintf(outf,
-                   "ERROR: Client %s does not support sendsize REP packet.\n",
+           g_fprintf(outf,
+                   _("ERROR: Client %s does not support sendsize REP packet.\n"),
                    hostp->hostname);
+           g_fprintf(outf, _("Client might be of a very old version\n"));
        }
        if(!am_has_feature(hostp->features, fe_sendbackup_req) &&
           !am_has_feature(hostp->features, fe_sendbackup_req_device)) {
-           fprintf(outf,
-                  "ERROR: Client %s does not support sendbackup REQ packet.\n",
+           g_fprintf(outf,
+                  _("ERROR: Client %s does not support sendbackup REQ packet.\n"),
                   hostp->hostname);
+           g_fprintf(outf, _("Client might be of a very old version\n"));
        }
        if(!am_has_feature(hostp->features, fe_sendbackup_rep)) {
-           fprintf(outf,
-                  "ERROR: Client %s does not support sendbackup REP packet.\n",
+           g_fprintf(outf,
+                  _("ERROR: Client %s does not support sendbackup REP packet.\n"),
                   hostp->hostname);
+           g_fprintf(outf, _("Client might be of a very old version\n"));
        }
 
-       ap_snprintf(number, sizeof(number), "%d", hostp->maxdumps);
+       g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
        req = vstralloc("SERVICE ", "selfcheck", "\n",
                        "OPTIONS ",
                        has_features ? "features=" : "",
@@ -1352,89 +1719,249 @@ int start_host(hostp)
                        has_hostname ? "hostname=" : "",
                        has_hostname ? hostp->hostname : "",
                        has_hostname ? ";" : "",
+                       has_config   ? "config=" : "",
+                       has_config   ? get_config_name() : "",
+                       has_config   ? ";" : "",
                        "\n",
                        NULL);
 
        req_len = strlen(req);
-       req_len += 128;                         /* room for SECURITY ... */
-       req_len += 256;                         /* room for non-disk answers */
+       req_len += 128;                         /* room for SECURITY ... */
+       req_len += 256;                         /* room for non-disk answers */
        for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
            char *l;
-           int l_len;
-           char *o;
-           char* calcsize;
-
-           if(dp->todo == 0) continue;
-
-           if(dp->up != DISK_READY) {
+           char *es;
+           size_t l_len;
+           char *o = NULL;
+           char *calcsize;
+           char *qname, *b64disk;
+           char *qdevice, *b64device = NULL;
+           GPtrArray *errarray;
+           guint      i;
+
+           if(dp->up != DISK_READY || dp->todo != 1) {
                continue;
            }
-           o = optionstr(dp, hostp->features, outf);
+           qname = quote_string(dp->name);
+
+           errarray = validate_optionstr(dp);
+           if (errarray->len > 0) {
+               for (i=0; i < errarray->len; i++) {
+                   g_fprintf(outf, _("ERROR: %s:%s %s\n"),
+                             hostp->hostname, qname,
+                             (char *)g_ptr_array_index(errarray, i));
+               }
+               g_ptr_array_free(errarray, TRUE);
+               amfree(qname);
+               remote_errors++;
+               continue;
+           } else  if (am_has_feature(hostp->features, fe_req_xml)) {
+               o = xml_optionstr(dp, 0);
+           } else {
+               o = optionstr(dp);
+           }
+
+           b64disk = amxml_format_tag("disk", dp->name);
+           qdevice = quote_string(dp->device); 
+           if (dp->device)
+               b64device = amxml_format_tag("diskdevice", dp->device);
+           if ((dp->name && qname[0] == '"') || 
+               (dp->device && qdevice[0] == '"')) {
+               if(!am_has_feature(hostp->features, fe_interface_quoted_text)) {
+                   g_fprintf(outf,
+                           _("WARNING: %s:%s:%s host does not support quoted text\n"),
+                           hostp->hostname, qname, qdevice);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to "
+                                   "specify a quoted text/device in the disklist, "
+                                   "or don't use quoted text for the device.\n"));
+               }
+           }
 
            if(dp->device) {
                if(!am_has_feature(hostp->features, fe_selfcheck_req_device)) {
-                   fprintf(outf,
-                    "ERROR: %s:%s (%s): selfcheck does not support device.\n",
-                    hostp->hostname, dp->name, dp->device);
+                   g_fprintf(outf,
+                    _("ERROR: %s:%s (%s): selfcheck does not support device.\n"),
+                    hostp->hostname, qname, dp->device);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to "
+                                   "specify a diskdevice in the disklist "     
+                                   "or don't specify a diskdevice in the disklist.\n"));       
                }
                if(!am_has_feature(hostp->features, fe_sendsize_req_device)) {
-                   fprintf(outf,
-                    "ERROR: %s:%s (%s): sendsize does not support device.\n",
-                    hostp->hostname, dp->name, dp->device);
+                   g_fprintf(outf,
+                    _("ERROR: %s:%s (%s): sendsize does not support device.\n"),
+                    hostp->hostname, qname, dp->device);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to "
+                                   "specify a diskdevice in the disklist"      
+                                   " or don't specify a diskdevice in the disklist.\n"));      
                }
                if(!am_has_feature(hostp->features, fe_sendbackup_req_device)) {
-                   fprintf(outf,
-                    "ERROR: %s:%s (%s): sendbackup does not support device.\n",
-                    hostp->hostname, dp->name, dp->device);
+                   g_fprintf(outf,
+                    _("ERROR: %s:%s (%s): sendbackup does not support device.\n"),
+                    hostp->hostname, qname, dp->device);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to "
+                                   "specify a diskdevice in the disklist"      
+                                   " or don't specify a diskdevice in the disklist.\n"));      
+               }
+
+               if (dp->data_path != DATA_PATH_AMANDA &&
+                   !am_has_feature(hostp->features, fe_xml_data_path)) {
+                   g_fprintf(outf,
+                             _("ERROR: Client %s does not support %s data-path\n"),
+                             hostp->hostname,  data_path_to_string(dp->data_path));
+               } else if (dp->data_path == DATA_PATH_DIRECTTCP &&
+                   !am_has_feature(hostp->features, fe_xml_directtcp_list)) {
+                   g_fprintf(outf,
+                             _("ERROR: Client %s does not support directtcp data-path\n"),
+                             hostp->hostname);
                }
            }
-           if(strcmp(dp->program, "DUMP") == 0 &&
-              !am_has_feature(hostp->features, fe_program_dump)) {
-               fprintf(outf, "ERROR: %s:%s does not support DUMP.\n",
-                       hostp->hostname, dp->name);
-           }
-           if(strcmp(dp->program, "GNUTAR") == 0 &&
-              !am_has_feature(hostp->features, fe_program_gnutar)) {
-               fprintf(outf, "ERROR: %s:%s does not support GNUTAR.\n",
-                       hostp->hostname, dp->name);
-           }
-           if(dp->estimate == ES_CALCSIZE &&
-              !am_has_feature(hostp->features, fe_calcsize_estimate)) {
-               fprintf(outf, "ERROR: %s:%s does not support CALCSIZE for estimate, using CLIENT.\n",
-                       hostp->hostname, dp->name);
-               dp->estimate = ES_CLIENT;
+           if (dp->program &&
+               (strcmp(dp->program,"DUMP") == 0 || 
+                strcmp(dp->program,"GNUTAR") == 0)) {
+               if(strcmp(dp->program, "DUMP") == 0 &&
+                  !am_has_feature(hostp->features, fe_program_dump)) {
+                   g_fprintf(outf, _("ERROR: %s:%s does not support DUMP.\n"),
+                           hostp->hostname, qname);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to use DUMP "
+                                   "or you can use another program.\n"));      
+               }
+               if(strcmp(dp->program, "GNUTAR") == 0 &&
+                  !am_has_feature(hostp->features, fe_program_gnutar)) {
+                   g_fprintf(outf, _("ERROR: %s:%s does not support GNUTAR.\n"),
+                           hostp->hostname, qname);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
+                                   "or you can use another program.\n"));      
+               }
+               estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
+               if(estimate == ES_CALCSIZE &&
+                  !am_has_feature(hostp->features, fe_calcsize_estimate)) {
+                   g_fprintf(outf, _("ERROR: %s:%s does not support CALCSIZE for "
+                                   "estimate, using CLIENT.\n"),
+                           hostp->hostname, qname);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to use "
+                                   "CALCSIZE for estimate or don't use CALCSIZE for estimate.\n"));
+                   estimate = ES_CLIENT;
+               }
+               if(estimate == ES_CALCSIZE &&
+                  am_has_feature(hostp->features, fe_selfcheck_calcsize))
+                   calcsize = "CALCSIZE ";
+               else
+                   calcsize = "";
+
+               if(dp->compress == COMP_CUST &&
+                  !am_has_feature(hostp->features, fe_options_compress_cust)) {
+                 g_fprintf(outf,
+                         _("ERROR: Client %s does not support custom compression.\n"),
+                         hostp->hostname);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to "
+                                   "use custom compression\n"));
+                   g_fprintf(outf, _("Otherwise you can use the default client "
+                                   "compression program.\n"));
+               }
+               if(dp->encrypt == ENCRYPT_CUST ) {
+                 if ( !am_has_feature(hostp->features, fe_options_encrypt_cust)) {
+                   g_fprintf(outf,
+                           _("ERROR: Client %s does not support data encryption.\n"),
+                           hostp->hostname);
+                   g_fprintf(outf, _("You must upgrade amanda on the client to use encryption program.\n"));
+                   remote_errors++;
+                 } else if ( dp->compress == COMP_SERVER_FAST || 
+                             dp->compress == COMP_SERVER_BEST ||
+                             dp->compress == COMP_SERVER_CUST ) {
+                   g_fprintf(outf,
+                           _("ERROR: %s: Client encryption with server compression "
+                             "is not supported. See amanda.conf(5) for detail.\n"), 
+                           hostp->hostname);
+                   remote_errors++;
+                 } 
+               }
+               if (am_has_feature(hostp->features, fe_req_xml)) {
+                   l = vstralloc("<dle>\n"
+                                 "  <program>",
+                                 dp->program,
+                                 "</program>\n", NULL);
+                   es = xml_estimate(dp->estimatelist, hostp->features);
+                   vstrextend(&l, es, "\n", NULL);
+                   amfree(es);
+                   vstrextend(&l, "  ", b64disk, "\n", NULL);
+                   if (dp->device)
+                       vstrextend(&l, "  ", b64device, "\n", NULL);
+                   vstrextend(&l, o, "</dle>\n", NULL);
+               } else {
+                   if (dp->device) {
+                       l = vstralloc(calcsize,
+                                     dp->program, " ",
+                                     qname, " ",
+                                     qdevice,
+                                     " 0 OPTIONS |",
+                                     o,
+                                     "\n",
+                                     NULL);
+                   } else {
+                       l = vstralloc(calcsize,
+                                     dp->program, " ",
+                                     qname,
+                                     " 0 OPTIONS |",
+                                     o,
+                                     "\n",
+                                     NULL);
+                   }
+               }
+           } else {
+               if (!am_has_feature(hostp->features, fe_program_application_api) ||
+                   !am_has_feature(hostp->features, fe_req_xml)) {
+                   g_fprintf(outf, _("ERROR: %s:%s does not support APPLICATION-API.\n"),
+                           hostp->hostname, qname);
+                   g_fprintf(outf, _("Dumptype configuration is not GNUTAR or DUMP."
+                                   " It is case sensitive\n"));
+                   remote_errors++;
+                   l = stralloc("");
+               } else {
+                   l = vstralloc("<dle>\n"
+                                 "  <program>APPLICATION</program>\n", NULL);
+                   if (dp->application) {
+                       application_t *application;
+                       char          *xml_app;
+
+                       application = lookup_application(dp->application);
+                       if (!application) {
+                           g_fprintf(outf,
+                             _("ERROR: application '%s' not found.\n"), dp->application);
+                       } else {
+                           xml_app = xml_application(dp, application, hostp->features);
+                           vstrextend(&l, xml_app, NULL);
+                           amfree(xml_app);
+                       }
+                   }
+                   if (dp->pp_scriptlist) {
+                       if (!am_has_feature(hostp->features, fe_pp_script)) {
+                           g_fprintf(outf,
+                             _("ERROR: %s:%s does not support SCRIPT-API.\n"),
+                             hostp->hostname, qname);
+                       }
+                   }
+                   es = xml_estimate(dp->estimatelist, hostp->features);
+                   vstrextend(&l, es, "\n", NULL);
+                   amfree(es);
+                   vstrextend(&l, "  ", b64disk, "\n", NULL);
+                   if (dp->device)
+                       vstrextend(&l, "  ", b64device, "\n", NULL);
+                   vstrextend(&l, o, "</dle>\n", NULL);
+               }
            }
-
-           if(dp->estimate == ES_CALCSIZE &&
-              am_has_feature(hostp->features, fe_selfcheck_calcsize))
-               calcsize = "CALCSIZE ";
-           else
-               calcsize = "";
-
-           l = vstralloc(calcsize, 
-                         dp->program, " ",
-                         dp->name, " ",
-                         dp->device ? dp->device : "",
-                         " 0 OPTIONS |",
-                         o,
-                         "\n",
-                         NULL);
+           amfree(qname);
+           amfree(qdevice);
+           amfree(b64disk);
+           amfree(b64device);
            l_len = strlen(l);
            amfree(o);
-           /*
-            * Allow 2X for error response in return packet.
-            */
-           if(req_len + l_len > MAX_DGRAM / 2) {
-               amfree(l);
-               break;
-           }
+
            strappend(req, l);
            req_len += l_len;
            amfree(l);
            dp->up = DISK_ACTIVE;
            disk_count++;
        }
-
     }
     else { /* noop service */
        req = vstralloc("SERVICE ", "noop", "\n",
@@ -1443,55 +1970,51 @@ int start_host(hostp)
                        "\n",
                        NULL);
        for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
-           if(dp->todo == 0) continue;
-
-           if(dp->up != DISK_READY) {
+           if(dp->up != DISK_READY || dp->todo != 1) {
                continue;
            }
            disk_count++;
        }
     }
+
     if(disk_count == 0) {
        amfree(req);
        hostp->up = HOST_DONE;
-       return 0;
+       return;
     }
 
-#ifdef KRB4_SECURITY
-    if(hostp->disks->auth == AUTH_KRB4)
-       rc = make_krb_request(hostp->hostname, kamanda_port, req,
-                             hostp, conf_ctimeout, handle_response);
-    else
-#endif
-        rc = make_request(hostp->hostname, amanda_port, req,
-                         hostp, conf_ctimeout, handle_response);
-
-    req = NULL;                                /* do not own this any more */
-
-    if(rc) {
-       /* couldn't resolve hostname */
-       fprintf(outf,
-               "ERROR: %s: could not resolve hostname\n", hostp->hostname);
-       remote_errors++;
-       hostp->up = HOST_DONE;
+    secdrv = security_getdriver(hostp->disks->auth);
+    if (secdrv == NULL) {
+       fprintf(stderr, _("Could not find security driver \"%s\" for host \"%s\". auth for this dle is invalid\n"),
+             hostp->disks->auth, hostp->hostname);
     } else {
-       hostp->up = HOST_ACTIVE;
+       protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, 
+                        req, conf_ctimeout, handle_result, hostp);
     }
-    return 1;
+
+    amfree(req);
+
+    hostp->up = HOST_ACTIVE;
 }
 
-int start_client_checks(fd)
-int fd;
+pid_t
+start_client_checks(
+    int                fd)
 {
     am_host_t *hostp;
-    disk_t *dp;
-    int hostcount, pid;
-    struct servent *amandad;
+    disk_t *dp, *dp1;
+    int hostcount;
+    pid_t pid;
     int userbad = 0;
 
     switch(pid = fork()) {
-    case -1: error("could not fork client check: %s", strerror(errno));
-    case 0: break;
+    case -1:
+       error(_("INTERNAL ERROR:could not fork client check: %s"), strerror(errno));
+       /*NOTREACHED*/
+
+    case 0:
+       break;
+
     default:
        return pid;
     }
@@ -1503,71 +2026,57 @@ int fd;
 
     startclock();
 
-    if((outf = fdopen(fd, "w")) == NULL)
-       error("fdopen %d: %s", fd, strerror(errno));
+    if((outf = fdopen(fd, "w")) == NULL) {
+       error(_("fdopen %d: %s"), fd, strerror(errno));
+       /*NOTREACHED*/
+    }
     errf = outf;
 
-    fprintf(outf, "\nAmanda Backup Client Hosts Check\n");
-    fprintf(outf,   "--------------------------------\n");
+    g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
+    g_fprintf(outf,   "--------------------------------\n");
 
-#ifdef KRB4_SECURITY
-    kerberos_service_init();
-#endif
-
-    proto_init(msg->socket, time(0), 1024);
-
-    /* get remote service port */
-    if((amandad = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL)
-       amanda_port = AMANDA_SERVICE_DEFAULT;
-    else
-       amanda_port = ntohs(amandad->s_port);
-
-#ifdef KRB4_SECURITY
-    if((amandad = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL)
-       kamanda_port = KAMANDA_SERVICE_DEFAULT;
-    else
-       kamanda_port = ntohs(amandad->s_port);
-#endif
+    protocol_init();
 
     hostcount = remote_errors = 0;
 
-    for(dp = origqp->head; dp != NULL; dp = dp->next) {
+    for(dp = origq.head; dp != NULL; dp = dp->next) {
        hostp = dp->host;
-       if(hostp->up == HOST_READY) {
-           if(start_host(hostp) == 1) {
-               hostcount++;
-               check_protocol();
+       if(hostp->up == HOST_READY && dp->todo == 1) {
+           for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+               run_server_scripts(EXECUTE_ON_PRE_HOST_AMCHECK,
+                                  get_config_name(), dp1, -1);
+           }
+           for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+               run_server_scripts(EXECUTE_ON_PRE_DLE_AMCHECK,
+                                  get_config_name(), dp1, -1);
            }
+           start_host(hostp);
+           hostcount++;
+           protocol_check();
        }
     }
 
-    run_protocol();
-    amfree(msg);
+    protocol_run();
 
-    fprintf(outf,
-     "Client check: %d host%s checked in %s seconds, %d problem%s found\n",
-           hostcount, (hostcount == 1) ? "" : "s",
-           walltime_str(curclock()),
-           remote_errors, (remote_errors == 1) ? "" : "s");
+    g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."), 
+                        _("Client check: %d hosts checked in %s seconds."),
+                        hostcount),
+           hostcount, walltime_str(curclock()));
+    g_fprintf(outf, plural(_("  %d problem found.\n"),
+                        _("  %d problems found.\n"), remote_errors),
+           remote_errors);
     fflush(outf);
 
-    amfree(config_dir);
-    amfree(config_name);
-
-    malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
-    if(malloc_size_1 != malloc_size_2) {
-       malloc_list(fd, malloc_hist_1, malloc_hist_2);
-    }
-
     exit(userbad || remote_errors > 0);
-    /* NOTREACHED */
+    /*NOTREACHED*/
     return 0;
 }
 
-static void handle_response(p, pkt)
-proto_t *p;
-pkt_t *pkt;
+static void
+handle_result(
+    void *             datap,
+    pkt_t *            pkt,
+    security_handle_t *        sech)
 {
     am_host_t *hostp;
     disk_t *dp;
@@ -1577,103 +2086,81 @@ pkt_t *pkt;
     int ch;
     int tch;
 
-    hostp = (am_host_t *) p->datap;
+    hostp = (am_host_t *)datap;
     hostp->up = HOST_READY;
 
-    if(p->state == S_FAILED && pkt == NULL) {
-       if(p->prevstate == S_REPWAIT) {
-           fprintf(outf,
-                   "WARNING: %s: selfcheck reply timed out.\n",
-                   hostp->hostname);
-       }
-       else {
-           fprintf(outf,
-                   "WARNING: %s: selfcheck request timed out.  Host down?\n",
-                   hostp->hostname);
-       }
+    if (pkt == NULL) {
+       g_fprintf(outf,
+           _("WARNING: %s: selfcheck request failed: %s\n"), hostp->hostname,
+           security_geterror(sech));
        remote_errors++;
        hostp->up = HOST_DONE;
        return;
     }
 
-#ifdef KRB4_SECURITY
-    if(hostp->disks->auth == AUTH_KRB4 &&
-       !check_mutual_authenticator(host2key(hostp->hostname), pkt, p)) {
-       fprintf(outf, "ERROR: %s [mutual-authentication failed]\n",
-               hostp->hostname);
-       remote_errors++;
-       hostp->up = HOST_DONE;
-       return;
-    }
-#endif
-
 #if 0
-    fprintf(errf, "got %sresponse from %s:\n----\n%s----\n\n",
-           (p->state == S_FAILED) ? "NAK " : "", hostp->hostname, pkt->body);
+    g_fprintf(errf, _("got response from %s:\n----\n%s----\n\n"),
+           hostp->hostname, pkt->body);
 #endif
 
     s = pkt->body;
     ch = *s++;
     while(ch) {
        line = s - 1;
-       skip_line(s, ch);
+       skip_quoted_line(s, ch);
        if (s[-2] == '\n') {
            s[-2] = '\0';
        }
 
-#define sc "OPTIONS "
-       if(strncmp(line, sc, sizeof(sc)-1) == 0) {
-#undef sc
+       if(strncmp_const(line, "OPTIONS ") == 0) {
 
-#define sc "features="
-           t = strstr(line, sc);
-           if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
-               t += sizeof(sc)-1;
-#undef sc
+           t = strstr(line, "features=");
+           if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
+               char *u = strchr(t, ';');
+               if (u)
+                  *u = '\0';
+               t += SIZEOF("features=")-1;
                am_release_feature_set(hostp->features);
                if((hostp->features = am_string_to_feature(t)) == NULL) {
-                   fprintf(outf, "ERROR: %s: bad features value: %s\n",
-                           hostp->hostname, line);
+                   g_fprintf(outf, _("ERROR: %s: bad features value: '%s'\n"),
+                           hostp->hostname, t);
+                   g_fprintf(outf, _("The amfeature in the reply packet is invalid\n"));
+                   remote_errors++;
+                   hostp->up = HOST_DONE;
                }
+               if (u)
+                  *u = ';';
            }
 
            continue;
        }
 
-#define sc "OK "
-       if(strncmp(line, sc, sizeof(sc)-1) == 0) {
-#undef sc
+       if(strncmp_const(line, "OK ") == 0) {
            continue;
        }
 
-#define sc "ERROR "
-       if(strncmp(line, sc, sizeof(sc)-1) == 0) {
-           t = line + sizeof(sc)-1;
-           tch = t[-1];
-#undef sc
-
+       t = line;
+       if(strncmp_const_skip(line, "ERROR ", t, tch) == 0) {
            skip_whitespace(t, tch);
            /*
             * If the "error" is that the "noop" service is unknown, it
             * just means the client is "old" (does not support the service).
             * We can ignore this.
             */
-           if(hostp->features == NULL
-              && p->state == S_FAILED
-              && (strcmp(t - 1, "unknown service: noop") == 0
-                  || strcmp(t - 1, "noop: invalid service") == 0)) {
-           } else {
-               fprintf(outf, "ERROR: %s%s: %s\n",
-                       (p->state == S_FAILED) ? "NAK " : "",
-                       hostp->hostname,
-                       t - 1);
+           if(!((hostp->features == NULL) && (pkt->type == P_NAK)
+              && ((strcmp(t - 1, "unknown service: noop") == 0)
+                  || (strcmp(t - 1, "noop: invalid service") == 0)))) {
+               g_fprintf(outf, _("ERROR: %s%s: %s\n"),
+                       (pkt->type == P_NAK) ? "NAK " : "",
+                       hostp->hostname,
+                       t - 1);
                remote_errors++;
                hostp->up = HOST_DONE;
            }
            continue;
        }
 
-       fprintf(outf, "ERROR: %s: unknown response: %s\n",
+       g_fprintf(outf, _("ERROR: %s: unknown response: %s\n"),
                hostp->hostname, line);
        remote_errors++;
        hostp->up = HOST_DONE;
@@ -1683,8 +2170,7 @@ pkt_t *pkt;
         * The client does not support the features list, so give it an
         * empty one.
         */
-       dbprintf(("%s: no feature set from host %s\n",
-                 debug_prefix_time(NULL), hostp->hostname));
+       dbprintf(_("no feature set from host %s\n"), hostp->hostname);
        hostp->features = am_set_default_feature_set();
     }
     for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
@@ -1693,4 +2179,18 @@ pkt_t *pkt;
        }
     }
     start_host(hostp);
+    if(hostp->up == HOST_DONE) {
+       security_close_connection(sech, hostp->hostname);
+       for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
+           run_server_scripts(EXECUTE_ON_POST_DLE_AMCHECK,
+                              get_config_name(), dp, -1);
+       }
+       for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
+           run_server_scripts(EXECUTE_ON_POST_HOST_AMCHECK,
+                              get_config_name(), dp, -1);
+       }
+    }
+    /* try to clean up any defunct processes, since Amanda doesn't wait() for
+       them explicitly */
+    while(waitpid(-1, NULL, WNOHANG)> 0);
 }