diff --git a/event/sched/GNUmakefile.in b/event/sched/GNUmakefile.in
index 36f4260e564e874d7c8763b18aa600336869713d..f6cf02dc7aa1bb7652b7c8804ea6ccc4708e943d 100644
--- a/event/sched/GNUmakefile.in
+++ b/event/sched/GNUmakefile.in
@@ -27,7 +27,7 @@ DBLIBS    = -L/usr/local/lib/mysql -lmysqlclient -lz
 LIBS     += -levent_r -ltb -lcipher -lz
 
 ULXRINC   = -I/usr/local/include -I/usr/local/include/ulxmlrpcpp
-CXXFLAGS += -pthread -O $(ULXRINC)
+CXXFLAGS += -pthread -O $(ULXRINC) -I$(OBJDIR)
 ULXRLIBS  = -L/usr/local/lib  -lulsshxmlrpcpp -lulxmlrpcpp -lexpat
 
 #
@@ -62,7 +62,7 @@ rpc.o:			rpc.cc rpc.h
 rrpc.o:			rpc.cc rpc.h
 	$(CXX) $(CXXFLAGS) -DSSLRPC $(ULXRINC) -c -o rrpc.o $<
 
-install:
+install: event-sched_rpc
 	-mkdir -p $(INSTALL_DIR)/opsdir/sbin
 	$(INSTALL_PROGRAM) event-sched_rpc $(INSTALL_DIR)/opsdir/sbin/event-sched
 
diff --git a/event/sched/event-sched.c b/event/sched/event-sched.c
index 124dbfdfc8fe701024ab6bd3acd42edb156095d6..6c099de894eebb53da44698ad456628824703829 100644
--- a/event/sched/event-sched.c
+++ b/event/sched/event-sched.c
@@ -154,7 +154,9 @@ main(int argc, char **argv)
 	if (!server)
 		server = "localhost";
 #ifdef  RPC
-	RPC_init(NULL, BOSSNODE, 0);
+	if (RPC_init(NULL, BOSSNODE, 0)) {
+		fatal("could not connect to rpc server");
+	}
 #endif
 	
 	snprintf(buf, sizeof(buf), "elvin://%s%s%s",
@@ -193,6 +195,10 @@ main(int argc, char **argv)
 		fatal("could not get static event list");
 	}
 
+#ifdef  RPC
+	RPC_kill();
+#endif
+
 	/* Dequeue events and process them at the appropriate times: */
 	dequeue(handle);
 
diff --git a/event/sched/rpc.cc b/event/sched/rpc.cc
index 4b8ad549b330910f7b07e22eaeecb9c4ab4d7c9a..1d2344803027592e201851c8dbc9ef85372e2012 100644
--- a/event/sched/rpc.cc
+++ b/event/sched/rpc.cc
@@ -3,9 +3,14 @@
  * Copyright (c) 2004 University of Utah and the Flux Group.
  * All rights reserved.
  */
+
+#include "config.h"
+
 #include "rpc.h"
 
-#define ULXR_INCLUDE_SSL_STUFF
+#include <limits.h>
+#include <sys/types.h>
+#include <pwd.h>
 
 #include <ulxmlrpcpp.h>  // always first header
 #include <iostream>
@@ -27,38 +32,111 @@
 // This is just a stub that calls the realmain in event-sched.c
 //
 extern "C" int	realmain(int argc, char **argv);
-	
+
+/**
+ * We cache the connection to the server until all of the RPCs have completed
+ * so we do not have to reconnect.
+ */
+static struct {
+  ulxr::Connection *conn;	// The cached connection.
+  ulxr::Protocol *proto;	// The cached protocol layer.
+} rpc_data;
+
 int
 main(int argc, char **argv)
 {
-	return realmain(argc, argv);
+  return realmain(argc, argv);
 }
 
-/*
- * Simply save the stuff we need for making the connections later.
- */
-static char *CERTPATH;
-static char *PATH;
-static char *HOST = "localhost";
-static int   PORT = 3069;
-
 int RPC_init(char *certpath, char *host, int port)
 {
-  printf("%s %s %d\n", certpath, host, port);
+  int retval = -1;
   
 #ifdef SSHRPC
-  PATH     = "xmlrpc";
+  {
+    char identity_path[PATH_MAX];
+    struct passwd *pwd;
+
+    /* Construct the path to the identity and */
+    pwd = getpwuid(getuid());
+    snprintf(identity_path,
+	     sizeof(identity_path),
+	     "%s/.ssh/identity",
+	     pwd->pw_dir);
+    /* ... check to make sure it is passphrase-less. */
+    if (!ulxr::SSHConnection::has_passphraseless_login(identity_path)) {
+      /* XXX We should just automatically restore from backup here. */
+      fprintf(stderr, " *** ~/.ssh/identity is not a passphrase-less key\n");
+      fprintf(stderr, "     You will need to regenerate the key manually\n");
+      return 1;
+    }
+    else {
+      static char *XMLRPC_PATHS[] = {
+	"xmlrpc",
+	TBROOT "/sbin/sshxmlrpc_server.py",
+	NULL
+      };
+      
+      int lpc;
+
+      /*
+       * Probe each path looking for one that has the server on the other side.
+       */
+      for (lpc = 0; XMLRPC_PATHS[lpc]; lpc++) {
+	try {
+	  ulxr::RFC822Protocol *proto;
+	  ulxr::CppString server_name;
+	  
+	  rpc_data.conn = new ulxr::SSHConnection(
+		pwd->pw_name, host, XMLRPC_PATHS[lpc],
+		port ? port : ulxr::SSHConnection::DEFAULT_PORT,
+		ulxr::SSHConnection::DEFAULT_SSH_OPTS +
+		ULXR_PCHAR(" -1 -i ") +
+		ULXR_PCHAR(identity_path));
+	  rpc_data.proto = proto = new ulxr::RFC822Protocol(rpc_data.conn);
+	  
+	  server_name = proto->probe();
+	  
+	  if (strcmp(server_name.c_str(), "EmulabServer") == 0) {
+	    break;
+	  }
+	  else {
+	    RPC_kill();
+	  }
+	}
+	catch (ulxr::ConnectionException &ce) {
+	  RPC_kill();
+	}
+      }
+      if (rpc_data.proto == NULL) {
+	/* Bah, could not connect to the server. */
+	return 1;
+      }
+    }
+  }
 #else
-  if (certpath)
-    CERTPATH = strdup(certpath);
-  else
-    CERTPATH = "/usr/testbed/etc/client.pem";
+  /* XXX THIS IS OUT OF DATE */
+  {
+    ulxr::SSLConnection conn(false, host, port);
+    ulxr::HttpProtocol proto(&conn, conn.getHostName());
+    if (certpath == NULL)
+      certpath = "/usr/testbed/etc/client.pem";
+    conn.setCryptographyData("", certpath, certpath);
+  }
 #endif
-  HOST     = strdup(host);
-  PORT     = port;
   return 0;
 }
 
+void RPC_kill(void)
+{
+  if (rpc_data.proto != NULL) {
+    delete rpc_data.proto;
+    rpc_data.proto = NULL;
+    delete rpc_data.conn;
+    rpc_data.conn = NULL;
+  }
+}
+
 /*
  * Contact the server and invoke the method. All of the methods we care
  * about using return a string, which we return to the caller after
@@ -73,25 +151,21 @@ RPC_invoke(char *pid, char *eid, char *method, emulab::EmulabResponse *er)
   try
     {
 #ifdef SSHRPC
-      const char *user = getenv("USER");
-      
-      ulxr::SSHConnection conn(user, HOST, PATH);
-      ulxr::RFC822Protocol proto(&conn);
-      emulab::ServerProxy proxy(&proto);
+      emulab::ServerProxy proxy(rpc_data.proto);
 #else
-      ulxr::SSLConnection conn(false, HOST, PORT); 
-      ulxr::HttpProtocol proto(&conn, conn.getHostName());
-      conn.setCryptographyData("", CERTPATH, CERTPATH);
-      emulab::ServerProxy proxy(&proto, false, "/RPC2");
+      /* XXX THIS IS OUT OF DATE */
+      emulab::ServerProxy proxy(rpc_data.proto, false, "/RPC2");
 #endif
 
       *er = proxy.invoke(method,
-			emulab::SPA_String, "proj", pid,
-			emulab::SPA_String, "exp", eid,
-			emulab::SPA_TAG_DONE);
-
+			 emulab::SPA_String, "proj", pid,
+			 emulab::SPA_String, "exp", eid,
+			 emulab::SPA_TAG_DONE);
+      
       if (! er->isSuccess()){
-	  ULXR_CERR << "SSL_waitforactive failed: "
+	  ULXR_CERR << "RPC_invoke failed: "
+		    << method
+		    << " "
 		    << er->getOutput()
 		    << std::endl;
 	  return -1;
diff --git a/event/sched/rpc.h b/event/sched/rpc.h
index fd885a5a40d3ff322bbdac6606acfd2326155b51..d219dc871ba746bdfe67a789e7756c4ea492c1a6 100644
--- a/event/sched/rpc.h
+++ b/event/sched/rpc.h
@@ -23,6 +23,7 @@ typedef struct address_tuple * address_tuple_t;
 #endif
 
 extern CD int RPC_init(char *certpath, char *host, int port);
+extern CD void RPC_kill(void);
 extern CD int RPC_waitforactive(char *pid, char *eid);
 extern CD int RPC_agentlist(char *pid, char *eid);
 extern CD int RPC_grouplist(char *pid, char *eid);
diff --git a/xmlrpc/script_wrapper.py.in b/xmlrpc/script_wrapper.py.in
index 93ee2f3175161b19af61089f5d2b9cce3d22bc4c..5122c9abe8c7e2fff6c324f79cc5eb7e7849d958 100755
--- a/xmlrpc/script_wrapper.py.in
+++ b/xmlrpc/script_wrapper.py.in
@@ -60,6 +60,11 @@ admin           = 0
 devel           = 0
 needhelp        = 0
 
+# The set of paths to try when connecting to the server.
+XMLRPC_PATH     = [ "xmlrpc", SERVER_PATH + "/sbin/sshxmlrpc_server.py", ]
+# The ssh options that should be added to the default.
+SSH_OPTS        = { "-1" : "-1" }
+
 API = {
     "node_admin"        : { "func" : "adminmode",
                             "help" : "Boot selected nodes into FreeBSD MFS" },
@@ -133,6 +138,52 @@ def wrapperoptions():
     print "    --debug     Turn on semi-useful debugging"
     return
 
+##
+# Construct an SSHTransport object that is connected to an EmulabServer object
+# on the peer.  Multiple paths are attempted until one succeeds.
+#
+# @param user_agent The user-agent identifier.
+# @param ssh_identity The ssh identity file to use when connecting.
+# @return A pair containing the ssh transport and the path used, in that order.
+#
+def make_transport(user_agent=None, ssh_identity=None):
+    if path:
+        retval = (SSHTransport(user_agent=user_agent, ssh_opts=SSH_OPTS), path)
+        pass
+    else:
+        for xrpath in XMLRPC_PATH:
+            try:
+                retval = (SSHTransport(user_agent=user_agent,
+                                       ssh_identity=ssh_identity,
+                                       ssh_opts=SSH_OPTS),
+                          xrpath)
+                hdrs = retval[0].probe(xmlrpc_server,
+                                       "/" + xrpath,
+                                       verbose=debug)
+                if hdrs["probe-response"] == "EmulabServer":
+                    if debug:
+                        print "make_transport: found path " + xrpath
+                        pass
+                    break
+                else:
+                    retval = None
+                    pass
+                pass
+            except BadResponse, e:
+                if debug:
+                    print ("make_transport: bad response for "
+                           + xrpath + "; " + str(e))
+                    pass
+                pass
+            pass
+        pass
+
+    if not retval:
+        print "error - Unable to connect to RPC server"
+        pass
+
+    return retval
+
 #
 # Process a single command line
 #
@@ -143,15 +194,16 @@ def do_method(module, method, params):
     if impotent:
         return 0;
 
+    transport, fullpath = make_transport(user_agent="sshxmlrpc_wrapper-v0.2")
+
     # Get a handle on the server,
     server = SSHServerProxy("ssh://" + login_id + "@" + xmlrpc_server +
-                            "/xmlrpc/" + module,
-                            path=path,
-                            user_agent="sshxmlrpc_wrapper-v0.1")
-
+                            "/" + fullpath,
+                            transport=transport,
+                            user_agent="sshxmlrpc_wrapper-v0.2")
     
     # Get a pointer to the function we want to invoke.
-    meth      = getattr(server, method)
+    meth      = getattr(server, module + "." + method)
     meth_args = [ PACKAGE_VERSION, params ]
 
     #
@@ -1558,6 +1610,15 @@ if admin:
 handler      = None;
 command_argv = None;
 
+if not conf_passphraseless_login():
+    sys.stderr.write(sys.argv[0]
+                     + ": error - No agent or passphrase-less key found\n")
+    sys.stderr.write(sys.argv[0]
+                     + ": You will need to regenerate your passphrase-less "
+                     + "key manually\n")
+    sys.exit(-1)
+    pass
+
 if API.has_key(os.path.basename(sys.argv[0])):
     handler      = API[os.path.basename(sys.argv[0])]["func"];
     command_argv = sys.argv[len(wrapper_argv) + 1:];
diff --git a/xmlrpc/sshxmlrpc.py b/xmlrpc/sshxmlrpc.py
index f226d901c8656c0db234bffc9e7d9ad859eccb6a..1198aa82d261437f4598a24e4f1854fa6851175d 100644
--- a/xmlrpc/sshxmlrpc.py
+++ b/xmlrpc/sshxmlrpc.py
@@ -1,3 +1,5 @@
+#! /usr/bin/env python
+
 #
 # EMULAB-COPYRIGHT
 # Copyright (c) 2004 University of Utah and the Flux Group.
@@ -41,22 +43,167 @@
 # OF THIS SOFTWARE.
 # --------------------------------------------------------------------
 #
-import os
 import sys
 import types
 import urllib
 import popen2
 import rfc822
 import xmlrpclib
-if os.name != "nt":
-    import syslog
+import os, os.path
+
+VERSION = 0.5
+
+##
+## BEGIN Debugging setup
+##
+
+SSHXMLRPC_DEBUG = os.environ.get("SSHXMLRPC_DEBUG", "")
+
+# SSHXMLRPC_DEBUG = "all"
+
+if "all" in SSHXMLRPC_DEBUG:
+    SSHXMLRPC_DEBUG = "config,connect,io,ssh"
+    pass
+
+def __sxrdebug__(key, *args):
+    if key in SSHXMLRPC_DEBUG:
+        sys.stderr.write("sshxmlrpc.py: " + "".join(args) + "\n")
+        pass
+    return
+
+##
+## END Debugging setup
+##
+
+
+
+__sxrdebug__("config", "OS ", os.name)
 
-# XXX This should come from configure.
 if os.name != "nt":
+    import syslog
     LOG_TESTBED = syslog.LOG_LOCAL5;
+    pass
 
 import traceback
 
+
+
+##
+## BEGIN Self-configuration
+##
+
+##
+# Search the user's PATH for the given command.
+#
+# @param command The command name to search for.
+# @return The full path to the command or None if the command was not found.
+#
+def conf_which(command):
+    if os.path.exists(command) and os.path.isfile(command):
+        return command
+    for path in os.environ.get('PATH', os.defpath).split(os.pathsep):
+        fullpath = os.path.join(path, command)
+        if os.path.exists(fullpath) and os.path.isfile(fullpath):
+            return fullpath
+        pass
+    return None
+
+##
+# Search for several commands in the user's PATH and return the first match.
+#
+# @param possible_commands The list of commands to search for.
+# @return The first command that matched, or None if no match was found.
+#
+def conf_detect(possible_commands):
+    for cmd in possible_commands:
+        if len(cmd) > 0:
+            cmd_no_flags = cmd.split()[0]
+            retval = conf_which(cmd_no_flags)
+            if retval is not None:
+                return cmd
+            pass
+        pass
+    return None
+
+# The default identity for the user.
+DEFAULT_SSH_IDENTITY = os.path.join(os.path.expanduser("~"),
+                                    ".ssh",
+                                    "identity")
+
+__sxrdebug__("config", "DEFAULT_SSH_IDENTITY - ", str(DEFAULT_SSH_IDENTITY))
+
+##
+# Check if the user can perform a passphrase-less login.
+#
+# @param identity The identity to check.
+# @return True if the user can perform a passphraseless login.
+#
+def conf_passphraseless_login(identity=None):
+    if os.environ.get("SSH_AUTH_SOCK", "") == "":
+        # No agent, check for a passphrase-less key and then
+        if SSHKEYGEN_COMMAND is not None:
+            if not identity or (identity == ""):
+                identity = DEFAULT_SSH_IDENTITY
+                pass
+            rc = os.system(SSHKEYGEN_COMMAND
+                           + " -p -P \"\" -N \"\" -f "
+                           + identity
+                           + " > /dev/null 2>&1")
+            if rc != 0:
+                retval = False
+                pass
+            else:
+                retval = True
+                pass
+            pass
+        # ... complain.
+        elif os.name != "nt":
+            retval = False
+            pass
+        pass
+    else:
+        retval = True
+        pass
+    
+    return retval
+
+# Find a suitable "ssh" command and
+SSH_COMMAND = conf_detect([
+    os.environ.get("SSHXMLRPC_SSH", ""),
+    "ssh -T -x -C -o 'CompressionLevel 5' %(-F)s %(-l)s %(-i)s %(-v)s %(-1)s %(-2)s",
+    "plink -x -C %(-l)s %(-i)s %(-v)s %(-1)s %(-2)s",
+    ])
+
+__sxrdebug__("config", "SSH_COMMAND - ", str(SSH_COMMAND))
+
+# ... error out if we don't.
+if SSH_COMMAND is None:
+    sys.stderr.write("sshxmlrpc.py: Unable to locate a suitable SSH command\n")
+    if os.environ.has_key("SSHXMLRPC_SSH"):
+        sys.stderr.write("sshxmlrpc.py: '"
+                         + os.environ["SSHXMLRPC_SSH"]
+                         + "' was not found.\n")
+        pass
+    else:
+        sys.stderr.write("sshxmlrpc.py: Set the SSHXMLRPC_SSH environment "
+                         "variable to a suitable binary (e.g. ssh/plink)\n")
+        pass
+    raise ImportError, "suitable ssh not found in path: %(PATH)s" % os.environ
+
+# Find ssh-keygen so we can do some tests when a connection is made.
+SSHKEYGEN_COMMAND = conf_detect([
+    os.environ.get("SSHXMLRPC_SSHKEYGEN", ""),
+    "ssh-keygen",
+    ])
+
+__sxrdebug__("config", "SSHKEYGEN_COMMAND - ", str(SSHKEYGEN_COMMAND))
+
+##
+## END Self-configuration
+##
+
+
+
 ##
 # Base class for exceptions in this module.
 #
@@ -113,55 +260,55 @@ class SSHConnection:
     # @param ssh_config The ssh config file to use when initiating a new
     # connection.
     # 
-    def __init__(self, host, handler, streams=None, ssh_config=None):
+    def __init__(self, host, handler, streams=None, ssh_config=None,
+                 ssh_identity=None, ssh_opts={}):
         # Store information about the peer and
         self.handler = handler
         self.host = host
+        self.last_lines = []
 
         # ... initialize the read and write file objects.
-        self.myChild = None
         if streams:
             self.rfile = streams[0]
             self.wfile = streams[1]
+            self.errfile = None
+            self.closed = False
             pass
         else:
-            self.user, ssh_host = urllib.splituser(self.host)
-            # print self.user + " " + self.host + " " + handler
+            if not conf_passphraseless_login(ssh_identity):
+                sys.stderr.write("sshxmlrpc.py: warning - No agent or "
+                                 "passphrase-less key found, "
+                                 "continuing anyways...\n")
+                pass
             
-            # Use ssh unless we're on Windows with no ssh-agent running.
-            nt = os.name == "nt"
-            use_ssh = not nt or os.environ.has_key("SSH_AGENT_PID")
+            self.user, ssh_host = urllib.splituser(self.host)
 
-            flags = ""
+            all_opts = { "-l" : "", "-i" : "", "-F" : "", "-v" : "",
+                         "-1" : "", "-2" : "" }
+            all_opts.update(ssh_opts)
             if self.user:
-                flags = flags + " -l " + self.user
+                all_opts["-l"] = "-l " + self.user
                 pass
-            if use_ssh and ssh_config:
-                flags = flags + " -F " + ssh_config
+            if ssh_identity:
+                all_opts["-i"] = "-i " + ssh_identity
                 pass
-            args = flags + " " + ssh_host + " " + handler
-
-            if use_ssh:
-                cmd = "ssh -T -x -C -o 'CompressionLevel 5' " + args
+            if ssh_config:
+                all_opts["-F"] = "-F " + ssh_config
                 pass
-            else:
-                # Use the PyTTY plink, equivalent to the ssh command.
-                cmd = "plink -x -C " + args
+            if "ssh" in SSHXMLRPC_DEBUG:
+                all_opts["-v"] = "-vvv"
                 pass
-
-            if not nt:
-                # Popen3 objects, and the wait method, are Unix-only.
-                self.myChild = popen2.Popen3(cmd, 1)
-                self.rfile   = self.myChild.fromchild
-                self.wfile   = self.myChild.tochild
-                self.errfile = self.myChild.childerr
-                pass
-            else:
-                # Open the pipe in Binary mode so it doesn't mess with CR-LFs.
-                self.rfile, self.wfile, self.errfile = popen2.popen3(cmd, mode='b')
-                pass
-            # print "wfile", self.wfile, "rfile", self.rfile
+            
+            args = (SSH_COMMAND % all_opts) + " " + ssh_host + " " + handler
+            
+            __sxrdebug__("connect", "open - ", args)
+            
+            # Open the pipe in Binary mode so it doesn't mess with CR-LFs.
+            self.rfile, self.wfile, self.errfile = popen2.popen3(
+                args, mode='b')
+            self.closed = False
             pass
+        
         return
 
     ##
@@ -169,26 +316,44 @@ class SSHConnection:
     # @return The amount of data read.
     #
     def read(self, len=1024):
-        return self.rfile.read(len)
+        retval = self.rfile.read(len)
+
+        __sxrdebug__("io", "read - ", retval)
+        return retval
 
     ##
     # @return A line of data or None if there is no more input.
     #
     def readline(self):
-        return self.rfile.readline()
+        retval = self.rfile.readline()
+        if len(retval) > 0:
+            self.last_lines.append(retval)
+            if len(self.last_lines) > 5:
+                self.last_lines.pop(0)
+                pass
+            pass
+        else:
+            self.closed = True
+            pass
+        
+        __sxrdebug__("io", "readline - ", retval)
+        return retval
 
     ##
     # @param stuff The data to send to the other side.
     # @return The amount of data written.
     #
     def write(self, stuff):
-        # print "write", stuff
+        __sxrdebug__("io", "write - ", stuff)
+        
         return self.wfile.write(stuff)
 
     ##
     # Flush any write buffers.
     #
     def flush(self):
+        __sxrdebug__("io", "flush")
+        
         self.wfile.flush()
         return
 
@@ -196,12 +361,10 @@ class SSHConnection:
     # Close the connection.
     #
     def close(self):
+        __sxrdebug__("connect", "close - ", self.host)
+        
         self.wfile.close()
         self.rfile.close()
-        if self.myChild:
-            self.myChild.wait()
-            self.myChild = None
-            pass
         return
 
     ##
@@ -222,6 +385,46 @@ class SSHConnection:
         self.flush()
         return
 
+    ##
+    # Dump the standard error from the peer to the given file pointer with the
+    # given prefix.
+    #
+    # @param fp The file pointer where the output should be written or None if
+    # you just want to drain the pipe.
+    # @param prefix Prefix to prepend to every line. (optional)
+    #
+    def dump_stderr(self, fp, prefix=""):
+        if self.errfile:
+            while True:
+                line = self.errfile.readline()
+                if not line:
+                    break
+                if fp:
+                    fp.write(prefix + line)
+                    pass
+                pass
+            pass
+        return
+
+    ##
+    # Dump the last five lines of input read from the peer.  Helpful for
+    # debugging connections and what not.
+    #
+    # @param fp The file pointer where the output should be written.
+    # @param prefix Prefix to prepend to every line. (optional)
+    #
+    def dump_last_lines(self, fp, prefix=""):
+        if len(self.last_lines) < 5:
+            for lpc in range(len(self.last_lines), 5):
+                if not self.readline():
+                    break
+                pass
+            pass
+        for line in self.last_lines:
+            fp.write(prefix + line)
+            pass
+        return
+
     def __repr__(self):
         return "<SSHConnection %s%s>" % (self.host, self.handler)
 
@@ -238,52 +441,63 @@ class SSHTransport:
     # @param ssh_config The ssh config file to use when making new connections.
     # @param user_agent Symbolic name for the program acting on behalf of the
     #   user.
+    # @param ssh_opts List of additional options to pass to SSH_COMMAND.
     #
-    def __init__(self, ssh_config=None, user_agent=None):
+    def __init__(self, ssh_config=None, user_agent=None, ssh_identity=None,
+                 ssh_opts={}):
         self.connections = {}
         self.ssh_config = ssh_config
-        self.user_agent = user_agent
+        if user_agent:
+            self.user_agent = user_agent
+            pass
+        else:
+            self.user_agent = sys.argv[0]
+            pass
+        self.ssh_identity = ssh_identity
+        self.ssh_opts = ssh_opts
         return
 
-    def __del__(self):
-        for key, val in self.connections.items():
-            val.close()
+    ##
+    # Probe the peer and return their response headers.  Useful for making sure
+    # the other side is what we expect it to be.
+    #
+    # @param host The host to contact.
+    # @param handler The XML-RPC handler.
+    # @param hdrs A dictionary of additional headers to send to the peer, these
+    # will be included in their response.
+    # @return The response headers from the peer.
+    # @throws BadResponse if there was a problem interpreting the other side's
+    # response.
+    #
+    def probe(self, host, handler, hdrs={}, verbose=False):
+        handler = self.munge_handler(handler)
+        
+        connection = self.get_connection((host, handler))
+        connection.putheader("probe", self.user_agent)
+        for (key, value) in hdrs.items():
+            connection.putheader(key, str(value))
             pass
-        return;
-    
+        connection.endheaders()
+        connection.flush()
+
+        return self.parse_headers(connection, verbose=verbose)
+
     ##
     # Send a request to the destination.
     #
     # @param host The host name on which to execute the request
     # @param handler The python file that will handle the request.
     # @param request_body The XML-RPC encoded request.
-    # @param verbose unused.
-    # @return The value returned 
+    # @return The value returned by the peer method.
     #
-    def request(self, host, handler, request_body, verbose=0, path=None):
-        # Strip the leading slash in the handler, if there is one.
-        if path:
-            handler = path + handler
-            pass
-        elif handler.startswith('/'):
-            handler = handler[1:]
-            pass
+    def request(self, host, handler, request_body, path=None):
+        handler = self.munge_handler(handler, path)
 
         # Try to get a new connection,
-        if not self.connections.has_key((host,handler)):
-            if verbose:
-                sys.stderr.write("New connection for %s %s\n" %
-                                 (host, handler))
-                pass
-            
-            self.connections[(host,handler)] = SSHConnection(host, handler)
-            pass
-        connection = self.connections[(host,handler)]
+        connection = self.get_connection((host,handler))
 
         # ... send our request, and
-        if self.user_agent:
-            connection.putheader("user-agent", self.user_agent)
-            pass
+        connection.putheader("user-agent", self.user_agent)
         connection.putheader("content-length", len(request_body))
         connection.putheader("content-type", "text/xml")
         connection.endheaders()
@@ -301,6 +515,43 @@ class SSHTransport:
     def getparser(self):
         return xmlrpclib.getparser()
 
+    ##
+    # Munge the handler which means stripping the first slash, if it is there.
+    #
+    # @param handler The handler to munge.
+    # @return The munged handler string.
+    #
+    def munge_handler(self, handler, path=None):
+        # Strip the leading slash in the handler, if there is one.
+        if path:
+            retval = path + handler
+            pass
+        elif handler.startswith('/'):
+            retval = handler[1:]
+            pass
+        else:
+            retval = handler
+            pass
+        
+        return retval
+
+    ##
+    # Get a cached connection or make a new one.
+    #
+    # @param pair The host/handler pair that identifies the connection.
+    # @return An SSHConnection object for the given pair.
+    #
+    def get_connection(self, pair):
+        if not self.connections.has_key(pair):
+            __sxrdebug__("connect",
+                         "new connection for ", pair[0], " ", pair[1])
+            
+            self.connections[pair] = SSHConnection(
+                pair[0], pair[1],
+                ssh_identity=self.ssh_identity, ssh_opts=self.ssh_opts)
+            pass
+        return self.connections[pair]
+
     ##
     # @param connection The connection to drop.
     #
@@ -308,6 +559,29 @@ class SSHTransport:
         del self.connections[(connection.host,connection.handler)]
         connection.close()
         return
+
+    ##
+    # Parse the headers from the peer.
+    #
+    # @param connection The connection to read the headers from.
+    # @param verbose Be verbose in providing error information. (default: True)
+    #
+    def parse_headers(self, connection, verbose=True):
+        retval = SSHMessage(connection, False)
+        if retval.status != "":
+            if verbose:
+                connection.dump_stderr(sys.stderr,
+                                       connection.host + ",stderr: ")
+                sys.stderr.write("sshxmlrpc.py: Error while reading headers, "
+                                 "expected rfc822 headers, received:\n")
+                connection.dump_last_lines(sys.stderr, ">> ")
+                pass
+            self.drop_connection(connection)
+            raise BadResponse(connection.host,
+                              connection.handler,
+                              retval.status)
+        
+        return retval
     
     ##
     # Parse the response from the server.
@@ -319,24 +593,22 @@ class SSHTransport:
 
         try:
             # Get the headers,
-            headers = SSHMessage(connection, False)
-            if headers.status != "":
-                self.drop_connection(connection)
-                raise BadResponse(connection.host,
-                                  connection.handler,
-                                  headers.status)
+            headers = self.parse_headers(connection)
+            
             # ... the length of the body, and
             length = int(headers['content-length'])
             # ... read in the body.
             response = connection.read(length)
-            pass
         except KeyError, e:
+            connection.dump_stderr(sys.stderr, connection.host + ",stderr: ")
+            sys.stderr.write("sshxmlrpc.py: Error while reading headers, "
+                             + "expected rfc822 headers, received:\n")
+            connection.dump_last_lines(sys.stderr, ">> ")
             # Bad header, drop the connection, and
             self.drop_connection(connection)
             # ... tell the user.
             raise BadResponse(connection.host, connection.handler, e.args[0])
-        
-        # print "response /"+response+"/"
+
         parser.feed(response)
 
         return unmarshaller.close()
@@ -355,10 +627,17 @@ class SSHServerWrapper:
     # Initialize this object.
     #
     # @param object The object to wrap.
+    # @param probe_response The value to send back to clients in the
+    # 'probe-response' header.
     #
-    def __init__(self, object):
-        self.ssh_connection = os.environ['SSH_CONNECTION'].split()
+    def __init__(self, object, probe_response=None):
+        self.ssh_connection = os.environ.get(
+            "SSH_CONNECTION", "stdin 0 stdout 0").split()
         self.myObject = object
+        self.probe_response = probe_response
+        if self.probe_response is None:
+            self.probe_response = sys.argv[0] + " " + str(VERSION)
+            pass
 
         #
         # Init syslog
@@ -368,6 +647,8 @@ class SSHServerWrapper:
             syslog.syslog(syslog.LOG_INFO,
                           "Connect by " + os.environ['USER'] + " from " +
                           self.ssh_connection[0]);
+            pass
+        
         return
 
     ##
@@ -380,22 +661,50 @@ class SSHServerWrapper:
     def handle_request(self, connection):
         retval = False
         try:
-            # Read the request,
+            # Read the request headers,
             hdrs = SSHMessage(connection, False)
+
+            # ... make sure they are sane,
             if hdrs.status != "":
-                #sys.stderr.write("server error: Expecting rfc822 headers.\n");
-                raise BadRequest(connection.host, hdrs.status)
+                if not connection.closed:
+                    sys.stderr.write("server error: Expecting rfc822 headers, "
+                                     "received:\n");
+                    connection.dump_last_lines(sys.stderr, "<< ")
+                    sys.stderr.write("conn " + `connection.closed` + "\n")
+                    sys.stderr.write("server error: " + hdrs.status)
+                    raise BadRequest(connection.host, hdrs.status)
+                else:
+                    return True
+                pass
+
+            # ... respond to probes immediately,
+            if hdrs.has_key("probe"):
+                connection.putheader("probe-response", self.probe_response)
+                del hdrs["content-length"]
+                connection.putheader("content-length", 0)
+                for (key, value) in hdrs.items():
+                    connection.putheader(key, value)
+                    pass
+                connection.endheaders()
+                connection.flush()
+                return retval
+
+            # ... check for required headers, and
             if not hdrs.has_key('content-length'):
-                sys.stderr.write("server error: "
-                                 + "expecting content-length header\n")
+                sys.stderr.write("server error: expecting content-length "
+                                 "header, received:\n")
+                connection.dump_last_lines(sys.stderr, "<< ")
                 raise BadRequest(connection.host,
                                  "missing content-length header")
+
             if hdrs.has_key('user-agent'):
                 user_agent = hdrs['user-agent']
                 pass
             else:
                 user_agent = "unknown"
                 pass
+            
+            # ... start reading the body.
             length = int(hdrs['content-length'])
             params, method = xmlrpclib.loads(connection.read(length))
             if os.name != "nt":
@@ -471,8 +780,12 @@ class SSHServerWrapper:
             if os.name != "nt":
                 syslog.syslog(syslog.LOG_INFO, "Connection closed");
                 syslog.closelog()
+                pass
             pass
         return
+
+    def serve_stdio_forever(self):
+        return self.serve_forever((sys.stdin, sys.stdout))
     
     pass
 
@@ -489,38 +802,36 @@ class SSHServerProxy:
     # @param transport A python object that implements the Transport interface.
     # The default is to use a new SSHTransport object.
     # @param encoding Content encoding.
-    # @param verbose unused.
     # @param user_agent Symbolic name for the program acting on behalf of the
     #   user.
+    # @param ssh_opts List of additional options to pass to SSH_COMMAND.
     #
     def __init__(self,
                  uri,
                  transport=None,
                  encoding=None,
-                 verbose=0,
                  path=None,
-                 user_agent=None):
+                 user_agent=None,
+                 ssh_identity=None,
+                 ssh_opts={}):
         type, uri = urllib.splittype(uri)
         if type not in ("ssh", ):
-            raise IOError, "unsupported XML-RPC protocol"
+            raise IOError, "unsupported XML-RPC protocol: " + `type`
 
         self.__host, self.__handler = urllib.splithost(uri)
 
         if transport is None:
-            transport = SSHTransport(user_agent=user_agent)
+            transport = SSHTransport(user_agent=user_agent,
+                                     ssh_identity=ssh_identity,
+                                     ssh_opts=ssh_opts)
             pass
         
         self.__transport = transport
 
         self.__encoding = encoding
-        self.__verbose = verbose
         self.__path = path
         return
 
-    def __del__(self):
-        del self.__transport
-        return
-
     ##
     # Send a request to the server.
     #
@@ -536,7 +847,6 @@ class SSHServerProxy:
             self.__host,
             self.__handler,
             request,
-            verbose=self.__verbose,
             path=self.__path
             )
 
@@ -564,3 +874,165 @@ class SSHServerProxy:
         return True
 
     pass
+
+if __name__ == "__main__":
+    import time
+    import getopt
+
+    def usage():
+        print "SSH-based XML-RPC module/client."
+        print "Usage: sshxmlrpc.py [-hVq] [-u agent] [-i id] [-s opts] [<URL>]"
+        print "       sshxmlrpc.py [-u agent] [-i id] [-s opts] [<URL> <method>]"
+        print
+        print "Options:"
+        print "  -h, --help\t\t  Display this help message"
+        print "  -V, --version\t\t  Show the version number"
+        print "  -q, --quiet\t\t  Be less verbose"
+        print "  -u, --user-agent agent  Specify the user agent"
+        print "  -i, --identity id\t  Specify the SSH identity to use"
+        print "  -s, --ssh-opts opts\t  Specify additional SSH options"
+        print "                     \t  The format is 'opt=value' (e.g. l=stack)"
+        print
+        print "Required arguments:"
+        print "  URL\t\t\t  The URL of the server."
+        print "  method\t\t  The method name to call."
+        print
+        print "Environment Variables:"
+        print "  SSHXMLRPC_DEBUG\t  Activate debugging for the listed aspects."
+        print "                 \t  (e.g. all,config,connect,io,ssh)"
+        print "  SSHXMLRPC_SSH\t\t  Specify the ssh command to use."
+        print "  SSHXMLRPC_SSHKEYGEN\t  Specify the ssh-keygen command."
+        print
+        print "Examples:"
+        print "  $ sshxmlrpc.py ssh://localhost/server.py"
+        print
+        print "Configuration:"
+        print "  ssh command\t\t" + SSH_COMMAND
+        print "    ( The '%(-X)s' portions of the command are substituted    )"
+        print "    ( with the corresponding flags before the command is run. )"
+        print "    ( Do not include them if your version of SSH does not     )"
+        print "    ( fully support them.                                     )"
+        print "  ssh-keygen command\t" + SSHKEYGEN_COMMAND
+        return
+
+    verbose = True
+    user_agent = None
+    ssh_identity = None
+    ssh_opts = {}
+    
+    try:
+        opts, extra = getopt.getopt(sys.argv[1:],
+                                    "hVqu:i:s:",
+                                    [ "help",
+                                      "version",
+                                      "quiet",
+                                      "user-agent",
+                                      "identity",
+                                      "ssh-opts"])
+
+        for opt, val in opts:
+            if opt in ("-h", "--help"):
+                usage()
+                sys.exit()
+                pass
+            elif opt in ("-V", "--version"):
+                print VERSION
+                sys.exit()
+                pass
+            elif opt in ("-q", "--quiet"):
+                verbose = False
+                pass
+            elif opt in ("-u", "--user-agent"):
+                user_agent = val
+                pass
+            elif opt in ("-i", "--identity"):
+                ssh_identity = val
+                pass
+            elif opt in ("-s", "--ssh-opts"):
+                so = val.split("=")
+                if len(so) == 2:
+                    so_key, so_value = so
+                    pass
+                else:
+                    so_key = so[0]
+                    so_value = None
+                    pass
+                so_key = "-" + so_key
+                if so_value:
+                    ssh_opts[so_key] = so_key + " " + so_value
+                    pass
+                else:
+                    ssh_opts[so_key] = so_key
+                    pass
+                pass
+            else:
+                assert not "unhandled option"
+                pass
+            pass
+        pass
+    except getopt.error, e:
+        print e.args[0]
+        usage()
+        sys.exit(2)
+        pass
+
+    # Print usage if there are no arguments,
+    if len(extra) == 0:
+        usage()
+        sys.exit()
+        pass
+    # ... check the URL, then
+    elif not extra[0].startswith("ssh://"):
+        print "Invalid url: " + extra[0]
+        usage()
+        sys.exit(2)
+        pass
+    # ... probe the URL or
+    elif len(extra) == 1:
+        try:
+            st = SSHTransport(user_agent=user_agent,
+                              ssh_identity=ssh_identity,
+                              ssh_opts=ssh_opts)
+            type, uri = urllib.splittype(extra[0])
+            host, handler = urllib.splithost(uri)
+            rc = st.probe(host, handler,
+                          { "date" : time.ctime(time.time()) },
+                          verbose)
+            secs = time.mktime(time.strptime(rc["date"]))
+            
+            print "Probe results for: " + extra[0]
+            print "  response time=%.2f s" % (time.time() - secs)
+            print "Response Headers"
+            for pair in rc.items():
+                print "  %s: %s" % pair
+                pass
+            pass
+        except BadResponse, e:
+            print ("sshxmlrpc.py: error - bad response from "
+                   + extra[0]
+                   + "; "
+                   + e[2])
+            sys.exit(1)
+            pass
+        pass
+    # ... call a method.
+    else:
+        try:
+            sp = SSHServerProxy(extra[0],
+                                ssh_identity=ssh_identity,
+                                user_agent=user_agent,
+                                ssh_opts=ssh_opts)
+            method_name = extra[1]
+            method_args = extra[2:]
+            method = getattr(sp, method_name)
+            print str(apply(method, method_args))
+            pass
+        except BadResponse, e:
+            print ("sshxmlrpc.py: error - bad response from "
+                   + extra[0]
+                   + "; "
+                   + e[2])
+            sys.exit(1)
+            pass
+        pass
+    pass
diff --git a/xmlrpc/sshxmlrpc_server.py.in b/xmlrpc/sshxmlrpc_server.py.in
index 05f8d7b5d1173f61104656444eb5f43552e13603..fc8b829326adb6cdd6b7ac9d4435ca7022a9ba42 100755
--- a/xmlrpc/sshxmlrpc_server.py.in
+++ b/xmlrpc/sshxmlrpc_server.py.in
@@ -43,7 +43,7 @@ if len(sys.argv) > 1:
 # 
 # Construct and wrap our object.
 server  = eval(module + "(readonly=" + str(ReadOnly) + ")")
-wrapper = sshxmlrpc.SSHServerWrapper(server)
+wrapper = sshxmlrpc.SSHServerWrapper(server, module)
 # Handle the request on stdin and send the response to stdout.
-wrapper.serve_forever((sys.stdin, sys.stdout))
+wrapper.serve_stdio_forever()
 sys.exit(0)