Imported Upstream version 3.2.0
[debian/amanda] / recover-src / amrecover.c
index 4e2776a60356bf01fff2416d71ad02363e7adab7..2764abbbbb2dbff4a0e8d201c5471ddd791d5ec9 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: amrecover.c,v 1.29.4.7.4.6.2.7.2.1 2005/09/20 21:31:52 jrjackson Exp $
+ * $Id: amrecover.c,v 1.73 2006/07/25 18:27:57 martinea Exp $
  *
  * an interactive program for recovering backed-up files
  */
 
 #include "amanda.h"
-#include "version.h"
-#ifdef HAVE_NETINET_IN_SYSTM_H
-#include <netinet/in_systm.h>
-#endif
-#include <netinet/in.h>
-#ifdef HAVE_NETINET_IP_H
-#include <netinet/ip.h>
-#endif
 #include "stream.h"
 #include "amfeatures.h"
 #include "amrecover.h"
 #include "getfsent.h"
 #include "dgram.h"
-
-#if defined(KRB4_SECURITY)
-#include "krb4-security.h"
-#endif
 #include "util.h"
+#include "conffile.h"
+#include "protocol.h"
+#include "event.h"
+#include "security.h"
+#include "conffile.h"
+
+#define amrecover_debug(i, ...) do {   \
+       if ((i) <= debug_amrecover) {   \
+           dbprintf(__VA_ARGS__);      \
+       }                               \
+} while (0)
+
+extern int process_line(char *line);
+int get_line(void);
+int grab_reply(int show);
+void sigint_handler(int signum);
+int main(int argc, char **argv);
+
+#define USAGE _("Usage: amrecover [[-C] <config>] [-s <index-server>] [-t <tape-server>] [-d <tape-device>] [-o <clientconfigoption>]*\n")
 
-#ifdef HAVE_LIBREADLINE
-#  ifdef HAVE_READLINE_READLINE_H
-#    include <readline/readline.h>
-#    ifdef HAVE_READLINE_HISTORY_H
-#      include <readline/history.h>
-#    endif
-#  else
-#    ifdef HAVE_READLINE_H
-#      include <readline.h>
-#      ifdef HAVE_HISTORY_H
-#        include <history.h>
-#      endif
-#    else
-#      undef HAVE_LIBREADLINE
-#    endif
-#  endif
-#endif
-
-extern int process_line P((char *line));
-int guess_disk P((char *cwd, size_t cwd_len, char **dn_guess, char **mpt_guess));
-
-#define USAGE "Usage: amrecover [[-C] <config>] [-s <index-server>] [-t <tape-server>] [-d <tape-device>]\n"
-
-char *config = NULL;
 char *server_name = NULL;
 int server_socket;
 char *server_line = NULL;
 char *dump_datestamp = NULL;           /* date we are restoring */
 char *dump_hostname;                   /* which machine we are restoring */
 char *disk_name = NULL;                        /* disk we are restoring */
+dle_t *dump_dle = NULL;
 char *mount_point = NULL;              /* where disk was mounted */
 char *disk_path = NULL;                        /* path relative to mount point */
 char dump_date[STR_SIZE];              /* date on which we are restoring */
@@ -87,79 +71,75 @@ char *tape_server_name = NULL;
 int tape_server_socket;
 char *tape_device_name = NULL;
 am_feature_t *our_features = NULL;
-am_feature_t *their_features = NULL;
-
-
-#ifndef HAVE_LIBREADLINE
-/*
- * simple readline() replacements
- */
-
-char *
-readline(prompt)
-char *prompt;
-{
-    printf("%s",prompt);
-    fflush(stdout); fflush(stderr);
-    return agets(stdin);
-}
-
-#define add_history(x)                /* add_history((x)) */
-
-#endif
-
+char *our_features_string = NULL;
+am_feature_t *indexsrv_features = NULL;
+am_feature_t *tapesrv_features = NULL;
+proplist_t proplist = NULL;
+static char *errstr = NULL;
+char *authopt;
+int amindexd_alive = 0;
+
+static struct {
+    const char *name;
+    security_stream_t *fd;
+} streams[] = {
+#define MESGFD  0
+    { "MESG", NULL },
+};
+#define NSTREAMS        (int)(sizeof(streams) / sizeof(streams[0]))
+
+static void amindexd_response(void *, pkt_t *, security_handle_t *);
+void stop_amindexd(void);
+
+static char* mesg_buffer = NULL;
 /* gets a "line" from server and put in server_line */
 /* server_line is terminated with \0, \r\n is striped */
 /* returns -1 if error */
 
-int get_line ()
+int
+get_line(void)
 {
-    char *line = NULL;
-    char *part = NULL;
-    size_t len;
-
-    while(1) {
-       if((part = areads(server_socket)) == NULL) {
-           int save_errno = errno;
-
-           if(server_line) {
-               fputs(server_line, stderr);     /* show the last line read */
-               fputc('\n', stderr);
-           }
-           if(save_errno != 0) {
-               fprintf(stderr, "%s: Error reading line from server: %s\n",
-                               get_pname(),
-                               strerror(save_errno));
-           } else {
-               fprintf(stderr, "%s: Unexpected end of file, check amindexd*debug on server %s\n",
-                       get_pname(),
-                       server_name);
-           }
-           amfree(line);
-           amfree(server_line);
-           errno = save_errno;
+    ssize_t size;
+    char *newbuf, *s;
+    void *buf;
+
+    if (!mesg_buffer)
+       mesg_buffer = stralloc("");
+    while (!strstr(mesg_buffer,"\r\n")) {
+       buf = NULL;
+       size = security_stream_read_sync(streams[MESGFD].fd, &buf);
+       if(size < 0) {
+           amrecover_debug(1, "amrecover: get_line size < 0 (%zd)\n", size);
            return -1;
        }
-       if(line) {
-           strappend(line, part);
-           amfree(part);
-       } else {
-           line = part;
-           part = NULL;
+       else if(size == 0) {
+           amrecover_debug(1, "amrecover: get_line size == 0 (%zd)\n", size);
+           return -1;
        }
-       if((len = strlen(line)) > 0 && line[len-1] == '\r') {
-           line[len-1] = '\0';
-           server_line = newstralloc(server_line, line);
-           amfree(line);
-           return 0;
+       else if (buf == NULL) {
+           amrecover_debug(1, "amrecover: get_line buf == NULL\n");
+           return -1;
        }
-       /*
-        * Hmmm.  We got a "line" from areads(), which means it saw
-        * a '\n' (or EOF, etc), but there was not a '\r' before it.
-        * Put a '\n' back in the buffer and loop for more.
-        */
-       strappend(line, "\n");
+        amrecover_debug(1, "amrecover: get_line size = %zd\n", size);
+       newbuf = alloc(strlen(mesg_buffer)+size+1);
+       strncpy(newbuf, mesg_buffer, (size_t)(strlen(mesg_buffer) + size));
+       memcpy(newbuf+strlen(mesg_buffer), buf, (size_t)size);
+       newbuf[strlen(mesg_buffer)+size] = '\0';
+       amfree(mesg_buffer);
+       mesg_buffer = newbuf;
+       amfree(buf);
     }
+
+    s = strstr(mesg_buffer,"\r\n");
+    *s = '\0';
+    newbuf = stralloc(s+2);
+    server_line = newstralloc(server_line, mesg_buffer);
+    amfree(mesg_buffer);
+    mesg_buffer = newbuf;
+    amrecover_debug(1, "server_line: %s\n", server_line);
+    amrecover_debug(1, "get: %s\n", mesg_buffer);
+    return 0;
 }
 
 
@@ -168,14 +148,19 @@ int get_line ()
 /* return -1 if error */
 /* return code returned by server always occupies first 3 bytes of global
    variable server_line */
-int grab_reply (show)
-int show;
+/* show == 0: Print the reply if it is an error */
+/* show == 1: Always print the reply            */
+int
+grab_reply(
+    int show)
 {
     do {
        if (get_line() == -1) {
            return -1;
        }
-       if(show) puts(server_line);
+       if (show || server_line[0] == '5') {
+           puts(server_line);
+       }
     } while (server_line[3] == '-');
     if(show) fflush(stdout);
 
@@ -185,7 +170,8 @@ int show;
 
 /* get 1 line of reply */
 /* returns -1 if error, 0 if last (or only) line, 1 if more to follow */
-int get_reply_line ()
+int
+get_reply_line(void)
 {
     if (get_line() == -1)
        return -1;
@@ -194,7 +180,8 @@ int get_reply_line ()
 
 
 /* returns pointer to returned line */
-char *reply_line ()
+char *
+reply_line(void)
 {
     return server_line;
 }
@@ -203,43 +190,44 @@ char *reply_line ()
 
 /* returns 0 if server returned an error code (ie code starting with 5)
    and non-zero otherwise */
-int server_happy ()
+int
+server_happy(void)
 {
     return server_line[0] != '5';
 }
 
 
-int send_command(cmd)
-char *cmd;
+int
+send_command(
+    char *     cmd)
 {
-    size_t l, n;
-    ssize_t s;
-    char *end;
-
     /*
      * NOTE: this routine is called from sigint_handler, so we must be
      * **very** careful about what we do since there is no way to know
      * our state at the time the interrupt happened.  For instance,
-     * do not use any stdio routines here.
+     * do not use any stdio or malloc routines here.
      */
-    for (l = 0, n = strlen(cmd); l < n; l += s)
-       if ((s = write(server_socket, cmd + l, n - l)) < 0) {
-           perror("amrecover: Error writing to server");
-           return -1;
-       }
-    end = "\r\n";
-    for (l = 0, n = strlen(end); l < n; l += s)
-       if ((s = write(server_socket, end + l, n - l)) < 0) {
-           perror("amrecover: Error writing to server");
-           return -1;
-       }
-    return 0;
+    char *buffer;
+
+    buffer = alloc(strlen(cmd)+3);
+    strncpy(buffer, cmd, strlen(cmd));
+    buffer[strlen(cmd)] = '\r';
+    buffer[strlen(cmd)+1] = '\n';
+    buffer[strlen(cmd)+2] = '\0';
+
+    g_debug("sending: %s\n", buffer);
+    if(security_stream_write(streams[MESGFD].fd, buffer, strlen(buffer)) < 0) {
+       return -1;
+    }
+    amfree(buffer);
+    return (0);
 }
 
 
 /* send a command to the server, get reply and print to screen */
-int converse(cmd)
-char *cmd;
+int
+converse(
+    char *     cmd)
 {
     if (send_command(cmd) == -1) return -1;
     if (grab_reply(1) == -1) return -1;
@@ -248,8 +236,9 @@ char *cmd;
 
 
 /* same as converse() but reply not echoed to stdout */
-int exchange(cmd)
-char *cmd;
+int
+exchange(
+    char *     cmd)
 {
     if (send_command(cmd) == -1) return -1;
     if (grab_reply(0) == -1) return -1;
@@ -259,8 +248,9 @@ char *cmd;
 
 /* basic interrupt handler for when user presses ^C */
 /* Bale out, letting server know before doing so */
-void sigint_handler(signum)
-int signum;
+void
+sigint_handler(
+    int        signum)
 {
     /*
      * NOTE: we must be **very** careful about what we do here since there
@@ -269,17 +259,22 @@ int signum;
      * routines.  Also, use _exit() instead of exit() to make sure stdio
      * buffer flushing is not attempted.
      */
+    (void)signum;      /* Quiet unused parameter warning */
+
     if (extract_restore_child_pid != -1)
        (void)kill(extract_restore_child_pid, SIGKILL);
     extract_restore_child_pid = -1;
 
-    (void)send_command("QUIT");
+    if(amindexd_alive) 
+       (void)send_command("QUIT");
+
     _exit(1);
 }
 
 
-void clean_pathname(s)
-char *s;
+void
+clean_pathname(
+    char *     s)
 {
     size_t length;
     length = strlen(s);
@@ -298,365 +293,313 @@ char *s;
 }
 
 
-/* try and guess the disk the user is currently on.
-   Return -1 if error, 0 if disk not local, 1 if disk local,
-   2 if disk local but can't guess name */
-/* do this by looking for the longest mount point which matches the
-   current directory */
-int guess_disk (cwd, cwd_len, dn_guess, mpt_guess)
-     char *cwd, **dn_guess, **mpt_guess;
-     size_t cwd_len;
-{
-    size_t longest_match = 0;
-    size_t current_length;
-    size_t cwd_length;
-    int local_disk = 0;
-    generic_fsent_t fsent;
-    char *fsname = NULL;
-    char *disk_try = NULL;
-
-    *dn_guess = *mpt_guess = NULL;
-
-    if (getcwd(cwd, cwd_len) == NULL)
-       return -1;
-    cwd_length = strlen(cwd);
-    dbprintf(("guess_disk: %d: \"%s\"\n", cwd_length, cwd));
-
-    if (open_fstab() == 0)
-       return -1;
-
-    while (get_fstab_nextentry(&fsent))
-    {
-       current_length = fsent.mntdir ? strlen(fsent.mntdir) : (size_t)0;
-       dbprintf(("guess_disk: %d: %d: \"%s\": \"%s\"\n",
-                 longest_match,
-                 current_length,
-                 fsent.mntdir ? fsent.mntdir : "(mntdir null)",
-                 fsent.fsname ? fsent.fsname : "(fsname null)"));
-       if ((current_length > longest_match)
-           && (current_length <= cwd_length)
-           && (strncmp(fsent.mntdir, cwd, current_length) == 0))
-       {
-           longest_match = current_length;
-           amfree(*mpt_guess);
-           *mpt_guess = stralloc(fsent.mntdir);
-           if(strncmp(fsent.fsname,DEV_PREFIX,(strlen(DEV_PREFIX))))
-           {
-               fsname = newstralloc(fsname, fsent.fsname);
-            }
-           else
-           {
-               fsname = newstralloc(fsname,fsent.fsname+strlen(DEV_PREFIX));
-           }
-           local_disk = is_local_fstype(&fsent);
-           dbprintf(("guess_disk: local_disk = %d, fsname = \"%s\"\n",
-                     local_disk,
-                     fsname));
-       }
-    }
-    close_fstab();
-
-    if (longest_match == 0) {
-       amfree(*mpt_guess);
-       amfree(fsname);
-       return -1;                      /* ? at least / should match */
-    }
-
-    if (!local_disk) {
-       amfree(*mpt_guess);
-       amfree(fsname);
-       return 0;
-    }
-
-    /* have mount point now */
-    /* disk name may be specified by mount point (logical name) or
-       device name, have to determine */
-    printf("Trying disk %s ...\n", *mpt_guess);
-    disk_try = stralloc2("DISK ", *mpt_guess);         /* try logical name */
-    if (exchange(disk_try) == -1)
-       exit(1);
-    amfree(disk_try);
-    if (server_happy())
-    {
-       *dn_guess = stralloc(*mpt_guess);               /* logical is okay */
-       amfree(fsname);
-       return 1;
-    }
-    printf("Trying disk %s ...\n", fsname);
-    disk_try = stralloc2("DISK ", fsname);             /* try device name */
-    if (exchange(disk_try) == -1)
-       exit(1);
-    amfree(disk_try);
-    if (server_happy())
-    {
-       *dn_guess = stralloc(fsname);                   /* dev name is okay */
-       amfree(fsname);
-       return 1;
-    }
-
-    /* neither is okay */
-    amfree(*mpt_guess);
-    amfree(fsname);
-    return 2;
-}
-
-
-void quit ()
+void
+quit(void)
 {
     quit_prog = 1;
     (void)converse("QUIT");
+    stop_amindexd();
 }
 
-char *localhost = NULL;
+#ifdef DEFAULT_TAPE_SERVER
+# define DEFAULT_TAPE_SERVER_FAILOVER (DEFAULT_TAPE_SERVER)
+#else
+# define DEFAULT_TAPE_SERVER_FAILOVER (NULL)
+#endif
 
-int main(argc, argv)
-int argc;
-char **argv;
+int
+main(
+    int                argc,
+    char **    argv)
 {
-    int my_port;
-    struct servent *sp;
     int i;
     time_t timer;
     char *lineread = NULL;
     struct sigaction act, oact;
     extern char *optarg;
     extern int optind;
-    char cwd[STR_SIZE], *dn_guess = NULL, *mpt_guess = NULL;
-    char *service_name;
     char *line = NULL;
+    const security_driver_t *secdrv;
+    char *req = NULL;
+    int response_error;
+    struct tm *tm;
+    config_overrides_t *cfg_ovr;
+    char *starting_hostname = NULL;
+
+    /*
+     * 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);
 
     set_pname("amrecover");
-    dbopen();
 
-#ifndef IGNORE_UID_CHECK
-    if (geteuid() != 0) {
-       erroutput_type |= ERR_SYSLOG;
-       error("amrecover must be run by root");
-    }
-#endif
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
 
-    localhost = alloc(MAX_HOSTNAME_LENGTH+1);
-    if (gethostname(localhost, MAX_HOSTNAME_LENGTH) != 0) {
-       error("cannot determine local host name\n");
-    }
-    localhost[MAX_HOSTNAME_LENGTH] = '\0';
+    dbopen(DBG_SUBDIR_CLIENT);
 
-    config = newstralloc(config, DEFAULT_CONFIG);
-    server_name = newstralloc(server_name, DEFAULT_SERVER);
-#ifdef DEFAULT_TAPE_SERVER
-    tape_server_name = newstralloc(tape_server_name, DEFAULT_TAPE_SERVER);
-#else
-    amfree(tape_server_name);
-#endif
-    if (argc > 1 && argv[1][0] != '-')
-    {
-       /*
-        * If the first argument is not an option flag, then we assume
-        * it is a configuration name to match the syntax of the other
-        * Amanda utilities.
-        */
-       char **new_argv;
+    /* treat amrecover-specific command line options as the equivalent
+     * -o command-line options to set configuration values */
+    cfg_ovr = new_config_overrides(argc/2);
 
-       new_argv = (char **) alloc ((argc + 1 + 1) * sizeof (*new_argv));
-       new_argv[0] = argv[0];
-       new_argv[1] = "-C";
-       for (i = 1; i < argc; i++)
-       {
-           new_argv[i + 1] = argv[i];
-       }
-       new_argv[i + 1] = NULL;
-       argc++;
-       argv = new_argv;
+    /* If the first argument is not an option flag, then we assume
+     * it is a configuration name to match the syntax of the other
+     * Amanda utilities. */
+    if (argc > 1 && argv[1][0] != '-') {
+       add_config_override(cfg_ovr, "conf", argv[1]);
+
+       /* remove that option from the command line */
+       argv[1] = argv[0];
+       argv++; argc--;
     }
-    while ((i = getopt(argc, argv, "C:s:t:d:U")) != EOF)
-    {
-       switch (i)
-       {
+
+    /* now parse regular command-line '-' options */
+    while ((i = getopt(argc, argv, "o:C:s:t:d:Uh:")) != EOF) {
+       switch (i) {
            case 'C':
-               config = newstralloc(config, optarg);
+               add_config_override(cfg_ovr, "conf", optarg);
                break;
 
            case 's':
-               server_name = newstralloc(server_name, optarg);
+               add_config_override(cfg_ovr, "index_server", optarg);
                break;
 
            case 't':
-               tape_server_name = newstralloc(tape_server_name, optarg);
+               add_config_override(cfg_ovr, "tape_server", optarg);
                break;
 
            case 'd':
-               tape_device_name = newstralloc(tape_device_name, optarg);
+               add_config_override(cfg_ovr, "tapedev", optarg);
+               break;
+
+           case 'o':
+               add_config_override_opt(cfg_ovr, optarg);
+               break;
+
+           case 'h':
+               starting_hostname = g_strdup(optarg);
                break;
 
            case 'U':
            case '?':
-               (void)printf(USAGE);
+               (void)g_printf(USAGE);
                return 0;
        }
     }
-    if (optind != argc)
-    {
-       (void)fprintf(stderr, USAGE);
+    if (optind != argc) {
+       (void)g_fprintf(stderr, USAGE);
        exit(1);
     }
 
+    /* load the base client configuration */
+    set_config_overrides(cfg_ovr);
+    config_init(CONFIG_INIT_CLIENT, NULL);
+
+    if (config_errors(NULL) >= CFGERR_WARNINGS) {
+       config_print_errors();
+       if (config_errors(NULL) >= CFGERR_ERRORS) {
+           g_critical(_("errors processing config file"));
+       }
+    }
+
+    /* and now try to load the configuration named in that file */
+    config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
+               getconf_str(CNF_CONF));
+
+    check_running_as(RUNNING_AS_ROOT);
+
+    dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
+
+    our_features = am_init_feature_set();
+    our_features_string = am_feature_to_string(our_features);
+
+    if (!starting_hostname) {
+       starting_hostname = alloc(MAX_HOSTNAME_LENGTH+1);
+       if (gethostname(starting_hostname, MAX_HOSTNAME_LENGTH) != 0) {
+           error(_("cannot determine local host name\n"));
+           /*NOTREACHED*/
+       }
+       starting_hostname[MAX_HOSTNAME_LENGTH] = '\0';
+    }
+
+    server_name = NULL;
+    if (getconf_seen(CNF_INDEX_SERVER) == -2) { /* command line argument */
+       server_name = getconf_str(CNF_INDEX_SERVER);
+    }
+    if (!server_name) {
+       server_name = getenv("AMANDA_SERVER");
+       if (server_name) {
+           g_printf(_("Using index server from environment AMANDA_SERVER (%s)\n"), server_name);
+       }
+    }
+    if (!server_name) {
+       server_name = getconf_str(CNF_INDEX_SERVER);
+    }
+    if (!server_name) {
+       error(_("No index server set"));
+       /*NOTREACHED*/
+    }
+    server_name = stralloc(server_name);
+
+    tape_server_name = NULL;
+    if (getconf_seen(CNF_TAPE_SERVER) == -2) { /* command line argument */
+       tape_server_name = getconf_str(CNF_TAPE_SERVER);
+    }
+    if (!tape_server_name) {
+       tape_server_name = getenv("AMANDA_TAPE_SERVER");
+       if (!tape_server_name) {
+           tape_server_name = getenv("AMANDA_TAPESERVER");
+           if (tape_server_name) {
+               g_printf(_("Using tape server from environment AMANDA_TAPESERVER (%s)\n"), tape_server_name);
+           }
+       } else {
+           g_printf(_("Using tape server from environment AMANDA_TAPE_SERVER (%s)\n"), tape_server_name);
+       }
+    }
+    if (!tape_server_name) {
+       tape_server_name = getconf_str(CNF_TAPE_SERVER);
+    }
+    if (!tape_server_name) {
+       error(_("No tape server set"));
+       /*NOTREACHED*/
+    }
+    tape_server_name = stralloc(tape_server_name);
+
+    amfree(tape_device_name);
+    tape_device_name = getconf_str(CNF_TAPEDEV);
+    if (!tape_device_name ||
+       strlen(tape_device_name) == 0 ||
+       !getconf_seen(CNF_TAPEDEV)) {
+       tape_device_name = NULL;
+    } else {
+       tape_device_name = stralloc(tape_device_name);
+    }
+
+    authopt = stralloc(getconf_str(CNF_AUTH));
+
+
     amfree(disk_name);
     amfree(mount_point);
     amfree(disk_path);
     dump_date[0] = '\0';
 
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
+
     /* set up signal handler */
     act.sa_handler = sigint_handler;
     sigemptyset(&act.sa_mask);
     act.sa_flags = 0;
-    if (sigaction(SIGINT, &act, &oact) != 0)
-    {
-       error("error setting signal handler: %s", strerror(errno));
+    if (sigaction(SIGINT, &act, &oact) != 0) {
+       error(_("error setting signal handler: %s"), strerror(errno));
+       /*NOTREACHED*/
     }
 
-    service_name = stralloc2("amandaidx", SERVICE_SUFFIX);
+    proplist = g_hash_table_new_full(g_str_hash, g_str_equal, &g_free, &free_property_t);
 
-    printf("AMRECOVER Version %s. Contacting server on %s ...\n",
-          version(), server_name);  
-    if ((sp = getservbyname(service_name, "tcp")) == NULL)
-    {
-       error("%s/tcp unknown protocol", service_name);
-    }
-    amfree(service_name);
-    server_socket = stream_client_privileged(server_name,
-                                            ntohs(sp->s_port),
-                                            -1,
-                                            -1,
-                                            &my_port);
-    if (server_socket < 0)
-    {
-       error("cannot connect to %s: %s", server_name, strerror(errno));
-    }
-    if (my_port >= IPPORT_RESERVED)
-    {
-       error("did not get a reserved port: %d", my_port);
+    protocol_init();
+
+    /* We assume that amindexd support fe_amindexd_options_features */
+    /*                             and fe_amindexd_options_auth     */
+    /* We should send a noop to really know                         */
+    req = vstrallocf("SERVICE amindexd\n"
+                   "OPTIONS features=%s;auth=%s;\n",
+                   our_features_string, authopt);
+
+    secdrv = security_getdriver(authopt);
+    if (secdrv == NULL) {
+       error(_("no '%s' security driver available for host '%s'"),
+           authopt, server_name);
+       /*NOTREACHED*/
     }
 
-#if 0
-    /*
-     * We may need root privilege again later for a reserved port to
-     * the tape server, so we will drop down now but might have to
-     * come back later.
-     */
-    setegid(getgid());
-    seteuid(getuid());
-#endif
+    protocol_sendreq(server_name, secdrv, generic_client_get_security_conf,
+                    req, STARTUP_TIMEOUT, amindexd_response, &response_error);
+
+    amfree(req);
+    protocol_run();
+
+    g_printf(_("AMRECOVER Version %s. Contacting server on %s ...\n"),
+          VERSION, server_name);
+
+    if(response_error != 0) {
+       g_fprintf(stderr,"%s\n",errstr);
+       exit(1);
+    }
 
     /* get server's banner */
-    if (grab_reply(1) == -1)
+    if (grab_reply(1) == -1) {
+        aclose(server_socket);
        exit(1);
-    if (!server_happy())
-    {
+    }
+    if (!server_happy()) {
        dbclose();
        aclose(server_socket);
        exit(1);
     }
 
-    /* do the security thing */
-#if defined(KRB4_SECURITY)
-#if 0 /* not yet implemented */
-    if(krb4_auth)
-    {
-       line = get_krb_security();
-    } else
-#endif /* 0 */
-#endif
-    {
-       line = get_bsd_security();
-    }
-    if (converse(line) == -1)
-       exit(1);
-    if (!server_happy())
-       exit(1);
-    memset(line, '\0', strlen(line));
-    amfree(line);
-
-    /* try to get the features form the server */
+    /* try to get the features from the server */
     {
-       char *our_feature_string = NULL;
        char *their_feature_string = NULL;
 
-       our_features = am_init_feature_set();
-       our_feature_string = am_feature_to_string(our_features);
-       line = stralloc2("FEATURES ", our_feature_string);
+       indexsrv_features = NULL;
+
+       line = vstrallocf("FEATURES %s", our_features_string);
        if(exchange(line) == 0) {
            their_feature_string = stralloc(server_line+13);
-           their_features = am_string_to_feature(their_feature_string);
+           indexsrv_features = am_string_to_feature(their_feature_string);
+           if (!indexsrv_features)
+               g_printf(_("Bad feature string from server: %s"), their_feature_string);
        }
-       else {
-           their_features = am_set_default_feature_set();
-        }
-       amfree(our_feature_string);
+       if (!indexsrv_features)
+           indexsrv_features = am_set_default_feature_set();
+
        amfree(their_feature_string);
        amfree(line);
     }
 
     /* set the date of extraction to be today */
     (void)time(&timer);
-    strftime(dump_date, sizeof(dump_date), "%Y-%m-%d", localtime(&timer));
-    printf("Setting restore date to today (%s)\n", dump_date);
-    line = stralloc2("DATE ", dump_date);
-    if (converse(line) == -1)
+    tm = localtime(&timer);
+    if (tm) 
+       strftime(dump_date, sizeof(dump_date), "%Y-%m-%d", tm);
+    else
+       error(_("BAD DATE"));
+
+    g_printf(_("Setting restore date to today (%s)\n"), dump_date);
+    line = vstrallocf("DATE %s", dump_date);
+    if (converse(line) == -1) {
+        aclose(server_socket);
        exit(1);
+    }
     amfree(line);
 
-    line = stralloc2("SCNF ", config);
-    if (converse(line) == -1)
+    line = vstrallocf("SCNF %s", get_config_name());
+    if (converse(line) == -1) {
+        aclose(server_socket);
        exit(1);
+    }
     amfree(line);
 
-    if (server_happy())
-    {
+    if (server_happy()) {
        /* set host we are restoring to this host by default */
        amfree(dump_hostname);
-       set_host(localhost);
+       set_host(starting_hostname);
        if (dump_hostname)
-       {
-            /* get a starting disk and directory based on where
-              we currently are */
-           switch (guess_disk(cwd, sizeof(cwd), &dn_guess, &mpt_guess))
-           {
-               case 1:
-                   /* okay, got a guess. Set disk accordingly */
-                   printf("$CWD '%s' is on disk '%s' mounted at '%s'.\n",
-                          cwd, dn_guess, mpt_guess);
-                   set_disk(dn_guess, mpt_guess);
-                   set_directory(cwd);
-                   if (server_happy() && strcmp(cwd, mpt_guess) != 0)
-                       printf("WARNING: not on root of selected filesystem, check man-page!\n");
-                   amfree(dn_guess);
-                   amfree(mpt_guess);
-                   break;
-
-               case 0:
-                   printf("$CWD '%s' is on a network mounted disk\n",
-                          cwd);
-                   printf("so you must 'sethost' to the server\n");
-                   /* fake an unhappy server */
-                   server_line[0] = '5';
-                   break;
-
-               case 2:
-               case -1:
-               default:
-                   printf("Can't determine disk and mount point from $CWD '%s'\n", cwd);
-                   /* fake an unhappy server */
-                   server_line[0] = '5';
-                   break;
-           }
-       }
+           g_printf(_("Use the setdisk command to choose dump disk to recover\n"));
+       else
+           g_printf(_("Use the sethost command to choose a host to recover\n"));
+
     }
 
     quit_prog = 0;
-    do
-    {
+    do {
        if ((lineread = readline("amrecover> ")) == NULL) {
            clearerr(stdin);
            putchar('\n');
@@ -665,6 +608,7 @@ char **argv;
        if (lineread[0] != '\0') 
        {
            add_history(lineread);
+           dbprintf(_("user command: '%s'\n"), lineread);
            process_line(lineread);     /* act on line's content */
        }
        amfree(lineread);
@@ -675,3 +619,215 @@ char **argv;
     aclose(server_socket);
     return 0;
 }
+
+static void
+amindexd_response(
+    void *datap,
+    pkt_t *pkt,
+    security_handle_t *sech)
+{
+    int ports[NSTREAMS], *response_error = datap, i;
+    char *p;
+    char *tok;
+    char *extra = NULL;
+
+    assert(response_error != NULL);
+    assert(sech != NULL);
+
+    if (pkt == NULL) {
+       errstr = newvstrallocf(errstr, _("[request failed: %s]"),
+                            security_geterror(sech));
+       *response_error = 1;
+       return;
+    }
+
+    if (pkt->type == P_NAK) {
+#if defined(PACKET_DEBUG)
+       dbprintf(_("got nak response:\n----\n%s\n----\n\n"), pkt->body);
+#endif
+
+       tok = strtok(pkt->body, " ");
+       if (tok == NULL || strcmp(tok, "ERROR") != 0)
+           goto bad_nak;
+
+       tok = strtok(NULL, "\n");
+       if (tok != NULL) {
+           errstr = newvstrallocf(errstr, "NAK: %s", tok);
+           *response_error = 1;
+       } else {
+bad_nak:
+           errstr = newvstrallocf(errstr, _("request NAK"));
+           *response_error = 2;
+       }
+       return;
+    }
+
+    if (pkt->type != P_REP) {
+       errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
+                             pkt_type2str(pkt->type), pkt->body);
+       *response_error = 1;
+       return;
+    }
+
+#if defined(PACKET_DEBUG)
+    g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
+#endif
+
+    for(i = 0; i < NSTREAMS; i++) {
+        ports[i] = -1;
+        streams[i].fd = NULL;
+    }
+
+    p = pkt->body;
+    while((tok = strtok(p, " \n")) != NULL) {
+       p = NULL;
+
+       /*
+        * Error response packets have "ERROR" followed by the error message
+        * followed by a newline.
+        */
+       if (strcmp(tok, "ERROR") == 0) {
+           tok = strtok(NULL, "\n");
+           if (tok == NULL) {
+               errstr = newvstrallocf(errstr, _("[bogus error packet]"));
+           } else {
+               errstr = newvstrallocf(errstr, "%s", tok);
+           }
+           *response_error = 2;
+           return;
+       }
+
+
+        /*
+         * Regular packets have CONNECT followed by three streams
+         */
+        if (strcmp(tok, "CONNECT") == 0) {
+
+           /*
+            * Parse the three stream specifiers out of the packet.
+            */
+           for (i = 0; i < NSTREAMS; i++) {
+               tok = strtok(NULL, " ");
+               if (tok == NULL || strcmp(tok, streams[i].name) != 0) {
+                   extra = vstrallocf(
+                          _("CONNECT token is \"%s\": expected \"%s\""),
+                          tok ? tok : _("(null)"), streams[i].name);
+                   goto parse_error;
+               }
+               tok = strtok(NULL, " \n");
+               if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
+                   extra = vstrallocf(
+                          _("CONNECT %s token is \"%s\" expected a port number"),
+                          streams[i].name, tok ? tok : _("(null)"));
+                   goto parse_error;
+               }
+           }
+           continue;
+       }
+
+       /*
+        * OPTIONS [options string] '\n'
+        */
+       if (strcmp(tok, "OPTIONS") == 0) {
+           tok = strtok(NULL, "\n");
+           if (tok == NULL) {
+               extra = vstrallocf(_("OPTIONS token is missing"));
+               goto parse_error;
+           }
+#if 0
+           tok_end = tok + strlen(tok);
+           while((p = strchr(tok, ';')) != NULL) {
+               *p++ = '\0';
+               if(strncmp_const(tok, "features=") == 0) {
+                   tok += SIZEOF("features=") - 1;
+                   am_release_feature_set(their_features);
+                   if((their_features = am_string_to_feature(tok)) == NULL) {
+                       errstr = newvstrallocf(errstr,
+                                     _("OPTIONS: bad features value: %s"),
+                                     tok);
+                       goto parse_error;
+                   }
+               }
+               tok = p;
+           }
+#endif
+           continue;
+       }
+#if 0
+       extra = vstrallocf(_("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""), tok ? tok : _("(null)"));
+       goto parse_error;
+#endif
+    }
+
+    /*
+     * Connect the streams to their remote ports
+     */
+    for (i = 0; i < NSTREAMS; i++) {
+/*@i@*/        if (ports[i] == -1)
+           continue;
+       streams[i].fd = security_stream_client(sech, ports[i]);
+       if (streams[i].fd == NULL) {
+           errstr = newvstrallocf(errstr,
+                       _("[could not connect %s stream: %s]"),
+                       streams[i].name, security_geterror(sech));
+           goto connect_error;
+       }
+    }
+    /*
+     * Authenticate the streams
+     */
+    for (i = 0; i < NSTREAMS; i++) {
+       if (streams[i].fd == NULL)
+           continue;
+       if (security_stream_auth(streams[i].fd) < 0) {
+           errstr = newvstrallocf(errstr,
+               _("[could not authenticate %s stream: %s]"),
+               streams[i].name, security_stream_geterror(streams[i].fd));
+           goto connect_error;
+       }
+    }
+
+    /*
+     * The MESGFD and DATAFD streams are mandatory.  If we didn't get
+     * them, complain.
+     */
+    if (streams[MESGFD].fd == NULL) {
+        errstr = newvstrallocf(errstr, _("[couldn't open MESG streams]"));
+        goto connect_error;
+    }
+
+    /* everything worked */
+    *response_error = 0;
+    amindexd_alive = 1;
+    return;
+
+parse_error:
+    errstr = newvstrallocf(errstr,
+                         _("[parse of reply message failed: %s]"),
+                         extra ? extra : _("(no additional information)"));
+    amfree(extra);
+    *response_error = 2;
+    return;
+
+connect_error:
+    stop_amindexd();
+    *response_error = 1;
+}
+
+/*
+ * This is called when everything needs to shut down so event_loop()
+ * will exit.
+ */
+void
+stop_amindexd(void)
+{
+    int i;
+
+    amindexd_alive = 0;
+    for (i = 0; i < NSTREAMS; i++) {
+        if (streams[i].fd != NULL) {
+            security_stream_close(streams[i].fd);
+            streams[i].fd = NULL;
+        }
+    }
+}