Imported Upstream version 3.3.2
[debian/amanda] / server-src / amcheck.c
index fdbd8cfb74f3729e52106196edc1223b03c91fbd..539397659ca8430d311049b52db5c737a4cbdced 100644 (file)
 #include "fsusage.h"
 #include "diskfile.h"
 #include "tapefile.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 "taperscan.h"
 #include "server_util.h"
 #include "pipespawn.h"
 #include "amfeatures.h"
 #include "device.h"
 #include "property.h"
 #include "timestamp.h"
+#include "amxml.h"
+#include "physmem.h"
+#include <getopt.h>
 
 #define BUFFER_SIZE    32768
 
@@ -72,7 +71,8 @@ int test_server_pgm(FILE *outf, char *dir, char *pgm, int suid, uid_t dumpuid);
 void
 usage(void)
 {
-    error(_("Usage: amcheck%s [-am] [-w] [-sclt] [-M <address>] <conf> [host [disk]* ]* [-o configoption]*"), versionsuffix());
+    g_printf(_("Usage: amcheck [--version] [-am] [-w] [-sclt] [-M <address>] [--client-verbose] [-o configoption]* <conf> [host [disk]* ]*\n"));
+    exit(1);
     /*NOTREACHED*/
 }
 
@@ -81,6 +81,13 @@ static char *our_feature_string = NULL;
 static char *displayunit;
 static long int unitdivisor;
 
+static int client_verbose = FALSE;
+static struct option long_options[] = {
+    {"client-verbose", 0, NULL,  1},
+    {"version"       , 0, NULL,  2},
+    {NULL, 0, NULL, 0}
+};
+
 int
 main(
     int                argc,
@@ -93,8 +100,8 @@ main(
     int do_clientchk, client_probs;
     int do_localchk, do_tapechk, server_probs;
     pid_t clientchk_pid, serverchk_pid;
-    int opt, tempfd, mainfd;
-    ssize_t size;
+    int tempfd, mainfd;
+    size_t size;
     amwait_t retstat;
     pid_t pid;
     extern int optind;
@@ -108,7 +115,8 @@ main(
     struct passwd *pw;
     uid_t uid_me;
     char *errstr;
-    config_overwrites_t *cfg_ovr;
+    config_overrides_t *cfg_ovr;
+    char *mailer;
 
     /*
      * Configure program for internationalization:
@@ -137,12 +145,12 @@ main(
 
     g_snprintf(pid_str, SIZEOF(pid_str), "%ld", (long)getpid());
 
-    erroutput_type = ERR_INTERACTIVE;
+    add_amanda_log_handler(amanda_log_stderr);
 
     our_features = am_init_feature_set();
     our_feature_string = am_feature_to_string(our_features);
 
-    uid_me = getuid();
+    uid_me = geteuid();
 
     alwaysmail = mailout = overwrite = 0;
     do_localchk = do_tapechk = do_clientchk = 0;
@@ -151,35 +159,37 @@ main(
 
     /* process arguments */
 
-    cfg_ovr = new_config_overwrites(argc/2);
-    while((opt = getopt(argc, argv, "M:mawsclto:")) != EOF) {
-       switch(opt) {
-       case 'M':       mailto=stralloc(optarg);
+    cfg_ovr = new_config_overrides(argc/2);
+    while (1) {
+       int option_index = 0;
+       int c;
+       c = getopt_long (argc, argv, "M:mawsclto:", long_options, &option_index);
+       if (c == -1) {
+           break;
+       }
+
+       switch(c) {
+       case 1:         client_verbose = TRUE;
+                       break;
+       case 2:         printf("amcheck-%s\n", VERSION);
+                       return(0);
+                       break;
+       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
-                       g_printf(_("You can't use -%c because configure didn't "
-                                "find a mailer./usr/bin/mail not found\n"),
-                               opt);
-                       exit(1);
-#endif
                        break;
        case 'a':       
-#ifdef MAILER
                        mailout = 1;
                        alwaysmail = 1;
-#else
-                       g_printf(_("You can't use -%c because configure didn't "
-                                "find a mailer./usr/bin/mail not found\n"),
-                               opt);
-                       exit(1);
-#endif
                        break;
        case 's':       do_localchk = do_tapechk = 1;
                        break;
@@ -189,7 +199,7 @@ main(
                        break;
        case 'w':       overwrite = 1;
                        break;
-       case 'o':       add_config_overwrite_opt(cfg_ovr, optarg);
+       case 'o':       add_config_override_opt(cfg_ovr, optarg);
                        break;
        case 't':       do_tapechk = 1;
                        break;
@@ -210,11 +220,31 @@ main(
     if(overwrite)
        do_tapechk = 1;
 
-    config_init(CONFIG_INIT_EXPLICIT_NAME|CONFIG_INIT_FATAL,
-               argv[0]);
-    apply_config_overwrites(cfg_ovr);
-    dbrename(config_name, DBG_SUBDIR_SERVER);
+    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);
+
+    if (config_errors(NULL) >= CFGERR_WARNINGS) {
+       config_print_errors();
+       if (config_errors(NULL) >= CFGERR_ERRORS) {
+           g_critical(_("errors processing config file"));
+       }
+    }
+
+    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);
+    }
     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"));
@@ -248,17 +278,11 @@ main(
 
     conf_ctimeout = (time_t)getconf_int(CNF_CTIMEOUT);
 
-    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
-    if(read_diskfile(conf_diskfile, &origq) < 0) {
-       error(_("could not load disklist %s. Make sure it exists and has correct permissions"), conf_diskfile);
-       /*NOTREACHED*/
-    }
     errstr = match_disklist(&origq, argc-1, argv+1);
     if (errstr) {
        g_printf(_("%s"),errstr);
        amfree(errstr);
     }
-    amfree(conf_diskfile);
 
     /*
      * Make sure we are running as the dump user.  Don't use
@@ -352,7 +376,7 @@ main(
            char *wait_msg = NULL;
 
            wait_msg = vstrallocf(_("parent: reaped bogus pid %ld\n"), (long)pid);
-           if (fullwrite(mainfd, wait_msg, strlen(wait_msg)) < 0) {
+           if (full_write(mainfd, wait_msg, strlen(wait_msg)) < strlen(wait_msg)) {
                error(_("write main file: %s"), strerror(errno));
                /*NOTREACHED*/
            }
@@ -368,21 +392,21 @@ main(
            /*NOTREACHED*/
        }
 
-       while((size = fullread(tempfd, buffer, SIZEOF(buffer))) > 0) {
-           if (fullwrite(mainfd, buffer, (size_t)size) < 0) {
+       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) {
+       if(errno != 0) {
            error(_("read temp file: %s"), strerror(errno));
            /*NOTREACHED*/
        }
        aclose(tempfd);
     }
 
-    version_string = vstrallocf(_("\n(brought to you by Amanda %s)\n"), version());
-    if (fullwrite(mainfd, version_string, strlen(version_string)) < 0) {
+    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*/
     }
@@ -392,9 +416,6 @@ main(
     our_features = NULL;
 
     /* 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;
@@ -402,10 +423,11 @@ main(
        int errfd;
        FILE *ferr;
        char *subject;
-       char **a;
+       char **a, **b;
+       GPtrArray *pipeargs;
        amwait_t retstat;
-       ssize_t r;
-       ssize_t w;
+       size_t r;
+       size_t w;
        char *err = NULL;
        char *extra_info = NULL;
        char *line = NULL;
@@ -424,35 +446,38 @@ main(
                        _("%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 {
-           r = (ssize_t)split(getconf_str(CNF_MAILTO), a, MAILTO_LIMIT, " ");
-           a[r + 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*/
        }
-       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,
@@ -460,12 +485,12 @@ main(
         * 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, (size_t)r)) != (ssize_t)r) {
-               if(w < 0 && errno == EPIPE) {
+       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) {
+               } else if(errno != 0) {
                    error(_("mailfd write: %s"), strerror(errno));
                    /*NOTREACHED*/
                } else {
@@ -503,11 +528,11 @@ main(
                fputs(extra_info, stderr);
                amfree(extra_info);
            }
-           error(_("error running mailer %s: %s"), MAILER, err?err:"(unknown)");
+           error(_("error running mailer %s: %s"), mailer, err?err:"(unknown)");
            /*NOTREACHED*/
        }
     }
-#endif
+
     dbclose();
     return (server_probs || client_probs);
 }
@@ -559,7 +584,7 @@ test_server_pgm(
     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) {
        g_fprintf(outf, _("ERROR: program %s: does not exist\n"),
@@ -594,120 +619,50 @@ test_server_pgm(
 /* 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 tape_status;
-    Device * device;
-    GValue property_value;
-    char * label = NULL;
-    char * tapename = NULL;
-    ReadLabelStatusFlags label_status;
-
-    bzero(&property_value, sizeof(property_value));
-    
-    tapename = getconf_str(CNF_TAPEDEV);
-    g_return_val_if_fail(tapename != NULL, FALSE);
-
-    device_api_init();
-    
-    if (!getconf_seen(CNF_TPCHANGER) && getconf_int(CNF_RUNTAPES) != 1) {
-        g_fprintf(outf,
-                _("WARNING: if a tape changer is not available, runtapes "
-                  "must be set to 1\n"));
-        g_fprintf(outf, _("Change the value of the \"runtapes\" parameter in " 
-                        "amanda.conf or configure a tape changer\n"));
+    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;
     }
-    
-    tape_status = taper_scan(NULL, &label, &datestamp, &tapename, NULL,
-                             FILE_taperscan_output_callback, outf,
-                             NULL, NULL);
-    if (tape_status < 0) {
-        tape_t *exptape = lookup_last_reusable_tape(0);
-        g_fprintf(outf, _("       (expecting "));
-        if(exptape != NULL) g_fprintf(outf, _("tape %s or "), exptape->label);
-        g_fprintf(outf, _("a new tape)\n"));
-        amfree(label);
-        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;
     }
 
-    device = device_open(tapename);
+    g_strfreev(args);
+    close(nullfd);
 
-    if (device == NULL) {
-        g_fprintf(outf, "ERROR: Could not open tape device.\n");
-        amfree(label);
-        return FALSE;
-    }
-    
-    device_set_startup_properties_from_config(device);
-    label_status = device_read_label(device);
-
-    if (tape_status == 3 && 
-        !(label_status & READ_LABEL_STATUS_VOLUME_UNLABELED)) {
-        if (label_status == READ_LABEL_STATUS_SUCCESS) {
-            g_fprintf(outf, "WARNING: Volume was unlabeled, but now "
-                    "is labeled \"%s\".\n", device->volume_label);
-        }
-    } else if (label_status != READ_LABEL_STATUS_SUCCESS && tape_status != 3) {
-        char * errstr = 
-            g_english_strjoinv_and_free
-                (g_flags_nick_to_strv(label_status &
-                                       (~READ_LABEL_STATUS_VOLUME_UNLABELED),
-                                       READ_LABEL_STATUS_FLAGS_TYPE), "or");
-        g_fprintf(outf, "WARNING: Reading label the second time failed: "
-                "One of %s.\n", errstr);
-        g_free(errstr);
-    } else if (tape_status != 3 &&
-               (device->volume_label == NULL || label == NULL ||
-                strcmp(device->volume_label, label) != 0)) {
-        g_fprintf(outf, "WARNING: Label mismatch on re-read: "
-                "Got %s first, then %s.\n", label, device->volume_label);
-    }
-    
-    /* If we can't get this property, it's not an error. Maybe the device
-     * doesn't support this property, or needs an actual volume to know
-     * for sure. */
-    if (device_property_get(device, PROPERTY_MEDIUM_TYPE, &property_value)) {
-        g_assert(G_VALUE_TYPE(&property_value) == MEDIA_ACCESS_MODE_TYPE);
-        if (g_value_get_enum(&property_value) ==
-            MEDIA_ACCESS_MODE_WRITE_ONLY) {
-            g_fprintf(outf, "WARNING: Media access mode is WRITE_ONLY, "
-                    "dumps will be thrown away.\n");
-        }
-    }
-    
-    if (overwrite) {
-       char *timestamp = get_undef_timestamp();
-        if (!device_start(device, ACCESS_WRITE, label, timestamp)) {
-            if (tape_status == 3) {
-                g_fprintf(outf, "ERROR: Could not label brand new tape.\n");
-            } else {
-                g_fprintf(outf,
-                        "ERROR: tape %s label ok, but is not writable.\n",
-                        label);
-            }
-           amfree(timestamp);
-            amfree(label);
-            g_object_unref(device);
-            return FALSE;
-        } else { /* Write succeeded. */
-            if (tape_status != 3) {
-                g_fprintf(outf, "Tape %s is writable; rewrote label.\n", label);
-            } else {
-                g_fprintf(outf, "Wrote label %s to brand new tape.\n", label);
-            }
-        }
-       amfree(timestamp);
-    } else { /* !overwrite */
-        g_fprintf(outf, "NOTE: skipping tape-writable test\n");
-        if (tape_status == 3) {
-            g_fprintf(outf,
-                    "Found a brand new tape, will label it %s.\n", 
-                    label);
-        } else {
-            g_fprintf(outf, "Tape %s label ok\n", label);
-        }                    
-    }
-    g_object_unref(device);
-    amfree(label);
-    return TRUE;
+    return success;
 }
 
 pid_t
@@ -718,7 +673,6 @@ start_server_check(
 {
     struct fs_usage fsusage;
     FILE *outf = NULL;
-    holdingdisk_t *hdp;
     pid_t pid G_GNUC_UNUSED;
     int confbad = 0, tapebad = 0, disklow = 0, logbad = 0;
     int userbad = 0, infobad = 0, indexbad = 0, pgmbad = 0;
@@ -726,7 +680,12 @@ start_server_check(
     tapetype_t *tp = NULL;
     char *quoted;
     int res;
-    intmax_t kb_avail;
+    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:
@@ -747,6 +706,10 @@ start_server_check(
     
     startclock();
 
+    /* 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*/
@@ -784,29 +747,29 @@ start_server_check(
                        strerror(errno));
                confbad = 1;
            }
-#if !defined(LPRCMD)
-           g_fprintf(outf, _("ERROR:lbl-templ  set but no LPRCMD defined. You should reconfigure amanda\n       and make sure it finds 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"), 
+           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"), 
+           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_no_yes_all(CNF_AUTOFLUSH)) {
+           g_fprintf(outf, _("WARNING: autoflush must be set to 'yes' or 'all' if taperflush (%d) is greater that 0.\n"),
                      getconf_int(CNF_TAPERFLUSH));
        }
 
@@ -815,6 +778,13 @@ start_server_check(
            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;
+       }
     }
 
     /*
@@ -886,7 +856,10 @@ start_server_check(
        char *holdfile;
         char * tapename;
        struct stat statbuf;
-       
+       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
@@ -906,14 +879,17 @@ start_server_check(
            amfree(quoted);
        }
        else if(stat(tapefile, &statbuf) == -1) {
-           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 {
+           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);
@@ -956,12 +932,133 @@ start_server_check(
                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 = getconf_holdingdisks(); hdp != NULL; hdp = holdingdisk_next(hdp)) {
+       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), "
@@ -1068,7 +1165,7 @@ start_server_check(
        }
        amfree(quoted);
 
-       if(access(logfile, F_OK) == 0) {
+       if(logbad == 0 && access(logfile, F_OK) == 0) {
            testtape = 0;
            logbad = 2;
            if(access(logfile, W_OK) != 0) {
@@ -1080,20 +1177,20 @@ start_server_check(
 
        olddir = vstralloc(conf_logdir, "/oldlog", NULL);
        quoted = quote_string(olddir);
-       if (stat(olddir,&stat_old) == 0) { /* oldlog exist */
+       if (logbad == 0 && stat(olddir,&stat_old) == 0) { /* oldlog exist */
            if(!(S_ISDIR(stat_old.st_mode))) {
                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) {
+           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) {
+       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"));
@@ -1101,7 +1198,7 @@ start_server_check(
        }
        amfree(quoted);
 
-       if (testtape) {
+       if (logbad == 0 && testtape) {
            logfile = newvstralloc(logfile, conf_logdir, "/amdump", NULL);
            if (access(logfile, F_OK) == 0) {
                testtape = 0;
@@ -1120,8 +1217,12 @@ start_server_check(
        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, _("WARNING: amdump or amflush seem to be running\n"));
-       g_fprintf(outf, _("WARNING: if they are not, you must run amcleanup\n"));
+       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 {
        g_fprintf(outf, _("NOTE: skipping tape checks\n"));
     }
@@ -1146,6 +1247,7 @@ start_server_check(
        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);
@@ -1373,7 +1475,7 @@ start_server_check(
                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"));
+                   g_fprintf(outf, _("Specify \"server-custom-encrypt\" in the dumptype\n"));
                    pgmbad = 1;
                  }
                  else if(access(dp->srv_encrypt, X_OK) == -1) {
@@ -1387,7 +1489,7 @@ start_server_check(
                  if ( dp->srvcompprog[0] == '\0' ) {
                    g_fprintf(outf, _("ERROR: server custom compression program "
                                    "not specified\n"));
-                   g_fprintf(outf, _("Specify \"server_custom_compress\" in "
+                   g_fprintf(outf, _("Specify \"server-custom-compress\" in "
                                    "the dumptype\n"));
                    pgmbad = 1;
                  }
@@ -1403,6 +1505,98 @@ start_server_check(
                  }
                }
 
+               /* 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(&origq, dp);
            }
@@ -1421,6 +1615,14 @@ start_server_check(
     g_fprintf(outf, _("Server check took %s seconds\n"), walltime_str(curclock()));
 
     fflush(outf);
+    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 \
@@ -1460,17 +1662,12 @@ start_host(
     int disk_count;
     const security_driver_t *secdrv;
     char number[NUM_STR_SIZE];
+    estimate_t estimate;
 
     if(hostp->up != HOST_READY) {
        return;
     }
 
-    if (strcmp(hostp->hostname,"localhost") == 0) {
-       g_fprintf(outf,
-                    _("WARNING: Usage of fully qualified hostname recommended for Client %s.\n"),
-                    hostp->hostname);
-    }
-
     /*
      * The first time through here we send a "noop" request.  This will
      * return the feature list from the client if it supports that.
@@ -1543,7 +1740,7 @@ start_host(
                        has_hostname ? hostp->hostname : "",
                        has_hostname ? ";" : "",
                        has_config   ? "config=" : "",
-                       has_config   ? config_name : "",
+                       has_config   ? get_config_name() : "",
                        has_config   ? ";" : "",
                        "\n",
                        NULL);
@@ -1553,22 +1750,41 @@ start_host(
        req_len += 256;                         /* room for non-disk answers */
        for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
            char *l;
+           char *es;
            size_t l_len;
-           char *o;
+           char *o = NULL;
            char *calcsize;
-           char *qname;
-           char *qdevice;
+           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);
-           if (o == NULL) {
-               remote_errors++;
+           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);
            }
-           qname = quote_string(dp->name); 
+
+           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)) {
@@ -1606,9 +1822,22 @@ start_host(
                                    "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 || 
-              strcmp(dp->program,"GNUTAR") == 0) {
+           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"),
@@ -1623,16 +1852,17 @@ start_host(
                    g_fprintf(outf, _("You must upgrade amanda on the client to use GNUTAR "
                                    "or you can use another program.\n"));      
                }
-               if(dp->estimate == ES_CALCSIZE &&
+               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"));
-                   dp->estimate = ES_CLIENT;
+                   estimate = ES_CLIENT;
                }
-               if(dp->estimate == ES_CALCSIZE &&
+               if(estimate == ES_CALCSIZE &&
                   am_has_feature(hostp->features, fe_selfcheck_calcsize))
                    calcsize = "CALCSIZE ";
                else
@@ -1665,56 +1895,104 @@ start_host(
                    remote_errors++;
                  } 
                }
-               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);
+               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_backup_api)) {
-                   g_fprintf(outf, _("ERROR: %s:%s does not support BACKUP-API.\n"),
+               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"));
-               }
-               if(dp->device) {
-                   l = vstralloc("BACKUP ",
-                                 dp->program, 
-                                 " ",
-                                 qname,
-                                 " ",
-                                 qdevice,
-                                 " 0 OPTIONS |",
-                                 o,
-                                 "\n",
-                                 NULL);
+                   remote_errors++;
+                   l = stralloc("");
                } else {
-                   l = vstralloc("BACKUP ",
-                                 dp->program, 
-                                 " ",
-                                 qname,
-                                 " 0 OPTIONS |",
-                                 o,
-                                 "\n",
-                                 NULL);
+                   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 {
+                           char *client_name = application_get_client_name(application);
+                           if (client_name && strlen(client_name) > 0 &&
+                               !am_has_feature(hostp->features, fe_application_client_name)) {
+                               g_fprintf(outf,
+                             _("WARNING: %s:%s does not support client-name in application.\n"),
+                             hostp->hostname, qname);
+                           }
+                           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);
+                       } else {
+                           identlist_t pp_scriptlist;
+                           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);
+                               char *client_name = pp_script_get_client_name(pp_script);;
+                               if (client_name && strlen(client_name) > 0 &&
+                                   !am_has_feature(hostp->features, fe_script_client_name)) {
+                                   g_fprintf(outf,
+                                       _("WARNING: %s:%s does not support client-name in script.\n"),
+                                       hostp->hostname, dp->name);
+                               }
+                           }
+                       }
+                   }
+                   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);
                }
            }
            amfree(qname);
            amfree(qdevice);
+           amfree(b64disk);
+           amfree(b64device);
            l_len = strlen(l);
            amfree(o);
 
@@ -1745,10 +2023,10 @@ start_host(
        return;
     }
 
-    secdrv = security_getdriver(hostp->disks->security_driver);
+    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->security_driver, hostp->hostname);
+             hostp->disks->auth, hostp->hostname);
     } else {
        protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, 
                         req, conf_ctimeout, handle_result, hostp);
@@ -1764,7 +2042,7 @@ start_client_checks(
     int                fd)
 {
     am_host_t *hostp;
-    disk_t *dp;
+    disk_t *dp, *dp1;
     int hostcount;
     pid_t pid;
     int userbad = 0;
@@ -1797,6 +2075,7 @@ start_client_checks(
     g_fprintf(outf, _("\nAmanda Backup Client Hosts Check\n"));
     g_fprintf(outf,   "--------------------------------\n");
 
+    run_server_global_scripts(EXECUTE_ON_PRE_AMCHECK, get_config_name());
     protocol_init();
 
     hostcount = remote_errors = 0;
@@ -1804,6 +2083,12 @@ start_client_checks(
     for(dp = origq.head; dp != NULL; dp = dp->next) {
        hostp = dp->host;
        if(hostp->up == HOST_READY && dp->todo == 1) {
+           run_server_host_scripts(EXECUTE_ON_PRE_HOST_AMCHECK,
+                                   get_config_name(), hostp);
+           for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+               run_server_dle_scripts(EXECUTE_ON_PRE_DLE_AMCHECK,
+                                  get_config_name(), dp1, -1);
+           }
            start_host(hostp);
            hostcount++;
            protocol_check();
@@ -1811,6 +2096,7 @@ start_client_checks(
     }
 
     protocol_run();
+    run_server_global_scripts(EXECUTE_ON_POST_AMCHECK, get_config_name());
 
     g_fprintf(outf, plural(_("Client check: %d host checked in %s seconds."), 
                         _("Client check: %d hosts checked in %s seconds."),
@@ -1839,6 +2125,7 @@ handle_result(
     char *t;
     int ch;
     int tch;
+    gboolean printed_hostname = FALSE;
 
     hostp = (am_host_t *)datap;
     hostp->up = HOST_READY;
@@ -1869,20 +2156,35 @@ handle_result(
        if(strncmp_const(line, "OPTIONS ") == 0) {
 
            t = strstr(line, "features=");
-           if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
+           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) {
-                   g_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;
        }
 
+       if (client_verbose && !printed_hostname) {
+           g_fprintf(outf, "HOST %s\n", hostp->hostname);
+           printed_hostname = TRUE;
+       }
+
        if(strncmp_const(line, "OK ") == 0) {
+           if (client_verbose) {
+               g_fprintf(outf, "%s\n", line);
+           }
            continue;
        }
 
@@ -1926,8 +2228,15 @@ handle_result(
        }
     }
     start_host(hostp);
-    if(hostp->up == HOST_DONE)
+    if(hostp->up == HOST_DONE) {
        security_close_connection(sech, hostp->hostname);
+       for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
+           run_server_dle_scripts(EXECUTE_ON_POST_DLE_AMCHECK,
+                              get_config_name(), dp, -1);
+       }
+       run_server_host_scripts(EXECUTE_ON_POST_HOST_AMCHECK,
+                               get_config_name(), hostp);
+    }
     /* try to clean up any defunct processes, since Amanda doesn't wait() for
        them explicitly */
     while(waitpid(-1, NULL, WNOHANG)> 0);