[Libguestfs] [PATCH] Add support for SSH (Secure Shell) block device.

Richard W.M. Jones rjones at redhat.com
Fri Apr 5 13:06:08 UTC 2013


From: "Richard W.M. Jones" <rjones at redhat.com>

---
 generator/actions.ml   |  22 +++++++++-
 src/drives.c           | 112 +++++++++++++++++++++++++++++++++++++++++++------
 src/guestfs-internal.h |   4 ++
 src/guestfs.pod        |  18 ++++++++
 src/launch-libvirt.c   |  12 +++++-
 5 files changed, 153 insertions(+), 15 deletions(-)

diff --git a/generator/actions.ml b/generator/actions.ml
index 233590f..d13968e 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -1247,7 +1247,7 @@ not all belong to a single logical operating system
 
   { defaults with
     name = "add_drive";
-    style = RErr, [String "filename"], [OBool "readonly"; OString "format"; OString "iface"; OString "name"; OString "label"; OString "protocol"; OStringList "server"];
+    style = RErr, [String "filename"], [OBool "readonly"; OString "format"; OString "iface"; OString "name"; OString "label"; OString "protocol"; OStringList "server"; OString "username"];
     once_had_no_optargs = true;
     blocking = false;
     fish_alias = ["add"];
@@ -1362,6 +1362,15 @@ The C<server> parameter may also be supplied - see below.
 
 See also: L<guestfs(3)/SHEEPDOG>.
 
+=item C<protocol = \"ssh\">
+
+Connect to the Secure Shell (ssh) server.
+
+The C<server> parameter must be supplied.
+The C<username> parameter may be supplied.  See below.
+
+See also: L<guestfs(3)/SSH>.
+
 =back
 
 =item C<server>
@@ -1376,6 +1385,7 @@ is a list of server(s).
  nbd            Exactly one
  rbd            One or more
  sheepdog       Zero or more
+ ssh            Exactly one
 
 Each list element is a string specifying a server.  The string must be
 in one of the following formats:
@@ -1389,6 +1399,16 @@ in one of the following formats:
 If the port number is omitted, then the standard port number
 for the protocol is used (see C</etc/services>).
 
+=item C<username>
+
+For the C<ssh> protocol only, this specifies the remote username.
+
+If not given, then the local username is used.  But note this sometimes
+may give unexpected results, for example if using the libvirt backend
+and if the libvirt backend is configured to start the qemu appliance
+as a special user such as C<qemu.qemu>.  If in doubt, specify the
+remote username you want.
+
 =back" };
 
   { defaults with
diff --git a/src/drives.c b/src/drives.c
index 01d88e0..a13dd03 100644
--- a/src/drives.c
+++ b/src/drives.c
@@ -107,7 +107,7 @@ static struct drive *
 create_drive_non_file (guestfs_h *g,
                        enum drive_protocol protocol,
                        struct drive_server *servers, size_t nr_servers,
-                       const char *exportname,
+                       const char *exportname, const char *username,
                        bool readonly, const char *format,
                        const char *iface, const char *name,
                        const char *disk_label,
@@ -119,6 +119,7 @@ create_drive_non_file (guestfs_h *g,
   drv->src.servers = servers;
   drv->src.nr_servers = nr_servers;
   drv->src.u.exportname = safe_strdup (g, exportname);
+  drv->src.username = username ? safe_strdup (g, username) : NULL;
 
   drv->readonly = readonly;
   drv->format = format ? safe_strdup (g, format) : NULL;
@@ -135,12 +136,17 @@ create_drive_non_file (guestfs_h *g,
 static struct drive *
 create_drive_gluster (guestfs_h *g,
                       struct drive_server *servers, size_t nr_servers,
-                      const char *exportname,
+                      const char *exportname, const char *username,
                       bool readonly, const char *format,
                       const char *iface, const char *name,
                       const char *disk_label,
                       bool use_cache_none)
 {
+  if (username != NULL) {
+    error (g, _("gluster: you cannot specify a username with this protocol"));
+    return NULL;
+  }
+
   if (nr_servers != 1) {
     error (g, _("gluster: you must specify exactly one server"));
     return NULL;
@@ -159,7 +165,7 @@ create_drive_gluster (guestfs_h *g,
   }
 
   return create_drive_non_file (g, drive_protocol_gluster,
-                                servers, nr_servers, exportname,
+                                servers, nr_servers, exportname, username,
                                 readonly, format, iface, name, disk_label,
                                 use_cache_none);
 }
@@ -179,12 +185,17 @@ nbd_port (void)
 static struct drive *
 create_drive_nbd (guestfs_h *g,
                   struct drive_server *servers, size_t nr_servers,
-                  const char *exportname,
+                  const char *exportname, const char *username,
                   bool readonly, const char *format,
                   const char *iface, const char *name,
                   const char *disk_label,
                   bool use_cache_none)
 {
+  if (username != NULL) {
+    error (g, _("nbd: you cannot specify a username with this protocol"));
+    return NULL;
+  }
+
   if (nr_servers != 1) {
     error (g, _("nbd: you must specify exactly one server"));
     return NULL;
@@ -194,7 +205,7 @@ create_drive_nbd (guestfs_h *g,
     servers[0].port = nbd_port ();
 
   return create_drive_non_file (g, drive_protocol_nbd,
-                                servers, nr_servers, exportname,
+                                servers, nr_servers, exportname, username,
                                 readonly, format, iface, name, disk_label,
                                 use_cache_none);
 }
@@ -202,7 +213,7 @@ create_drive_nbd (guestfs_h *g,
 static struct drive *
 create_drive_rbd (guestfs_h *g,
                   struct drive_server *servers, size_t nr_servers,
-                  const char *exportname,
+                  const char *exportname, const char *username,
                   bool readonly, const char *format,
                   const char *iface, const char *name,
                   const char *disk_label,
@@ -210,6 +221,11 @@ create_drive_rbd (guestfs_h *g,
 {
   size_t i;
 
+  if (username != NULL) {
+    error (g, _("rbd: you cannot specify a username with this protocol"));
+    return NULL;
+  }
+
   if (nr_servers == 0) {
     error (g, _("rbd: you must specify one or more servers"));
     return NULL;
@@ -233,7 +249,7 @@ create_drive_rbd (guestfs_h *g,
   }
 
   return create_drive_non_file (g, drive_protocol_rbd,
-                                servers, nr_servers, exportname,
+                                servers, nr_servers, exportname, username,
                                 readonly, format, iface, name, disk_label,
                                 use_cache_none);
 }
@@ -241,7 +257,7 @@ create_drive_rbd (guestfs_h *g,
 static struct drive *
 create_drive_sheepdog (guestfs_h *g,
                        struct drive_server *servers, size_t nr_servers,
-                       const char *exportname,
+                       const char *exportname, const char *username,
                        bool readonly, const char *format,
                        const char *iface, const char *name,
                        const char *disk_label,
@@ -249,6 +265,11 @@ create_drive_sheepdog (guestfs_h *g,
 {
   size_t i;
 
+  if (username != NULL) {
+    error (g, _("sheepdog: you cannot specify a username with this protocol"));
+    return NULL;
+  }
+
   for (i = 0; i < nr_servers; ++i) {
     if (servers[i].transport != drive_transport_none &&
         servers[i].transport != drive_transport_tcp) {
@@ -267,7 +288,43 @@ create_drive_sheepdog (guestfs_h *g,
   }
 
   return create_drive_non_file (g, drive_protocol_sheepdog,
-                                servers, nr_servers, exportname,
+                                servers, nr_servers, exportname, username,
+                                readonly, format, iface, name, disk_label,
+                                use_cache_none);
+}
+
+static struct drive *
+create_drive_ssh (guestfs_h *g,
+                  struct drive_server *servers, size_t nr_servers,
+                  const char *exportname, const char *username,
+                  bool readonly, const char *format,
+                  const char *iface, const char *name,
+                  const char *disk_label,
+                  bool use_cache_none)
+{
+  if (nr_servers != 1) {
+    error (g, _("ssh: you must specify exactly one server"));
+    return NULL;
+  }
+
+  if (servers[0].transport != drive_transport_none &&
+      servers[0].transport != drive_transport_tcp) {
+    error (g, _("ssh: only tcp transport is supported"));
+    return NULL;
+  }
+
+  if (STREQ (exportname, "")) {
+    error (g, _("ssh: pathname should not be an empty string"));
+    return NULL;
+  }
+
+  if (username && STREQ (username, "")) {
+    error (g, _("ssh: username should not be an empty string"));
+    return NULL;
+  }
+
+  return create_drive_non_file (g, drive_protocol_ssh,
+                                servers, nr_servers, exportname, username,
                                 readonly, format, iface, name, disk_label,
                                 use_cache_none);
 }
@@ -637,6 +694,7 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
   const char *protocol;
   size_t nr_servers = 0;
   struct drive_server *servers = NULL;
+  const char *username;
   int use_cache_none;
   struct drive *drv;
   size_t i, drv_index;
@@ -665,6 +723,8 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
       return -1;
     nr_servers = r;
   }
+  username = optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_USERNAME_BITMASK
+    ? optargs->username : NULL;
 
   if (format && !valid_format_iface (format)) {
     error (g, _("%s parameter is empty or contains disallowed characters"),
@@ -690,6 +750,11 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
       free_drive_servers (servers, nr_servers);
       return -1;
     }
+    if (username != NULL) {
+      error (g, _("you cannot specify a username with file-backed disks"));
+      free_drive_servers (servers, nr_servers);
+      return -1;
+    }
 
     if (STREQ (filename, "/dev/null"))
       drv = create_drive_dev_null (g, readonly, format, iface, name,
@@ -715,25 +780,30 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
     }
   }
   else if (STREQ (protocol, "gluster")) {
-    drv = create_drive_gluster (g, servers, nr_servers, filename,
+    drv = create_drive_gluster (g, servers, nr_servers, filename, username,
                                 readonly, format, iface, name,
                                 disk_label, false);
   }
   else if (STREQ (protocol, "nbd")) {
-    drv = create_drive_nbd (g, servers, nr_servers, filename,
+    drv = create_drive_nbd (g, servers, nr_servers, filename, username,
                             readonly, format, iface, name,
                             disk_label, false);
   }
   else if (STREQ (protocol, "rbd")) {
-    drv = create_drive_rbd (g, servers, nr_servers, filename,
+    drv = create_drive_rbd (g, servers, nr_servers, filename, username,
                             readonly, format, iface, name,
                             disk_label, false);
   }
   else if (STREQ (protocol, "sheepdog")) {
-    drv = create_drive_sheepdog (g, servers, nr_servers, filename,
+    drv = create_drive_sheepdog (g, servers, nr_servers, filename, username,
                                  readonly, format, iface, name,
                                  disk_label, false);
   }
+  else if (STREQ (protocol, "ssh")) {
+    drv = create_drive_ssh (g, servers, nr_servers, filename, username,
+                            readonly, format, iface, name,
+                            disk_label, false);
+  }
   else {
     error (g, _("unknown protocol '%s'"), protocol);
     drv = NULL; /*FALLTHROUGH*/
@@ -1030,6 +1100,21 @@ guestfs___drive_source_qemu_param (guestfs_h *g, const struct drive_source *src)
       return safe_asprintf (g, "sheepdog:%s:%d:%s",
                             src->servers[0].u.hostname, src->servers[0].port,
                             src->u.exportname);
+
+  case drive_protocol_ssh: {
+    CLEANUP_FREE char *username = NULL, *port = NULL;
+
+    if (src->username)
+      username = safe_asprintf (g, "%s@", username);
+    if (src->servers[0].port != 0)
+      port = safe_asprintf (g, ":%d", src->servers[0].port);
+
+    return safe_asprintf (g, "ssh://%s%s%s/%s",
+                          username ? username : "",
+                          src->servers[0].u.hostname,
+                          port ? port : "",
+                          src->u.exportname);
+  }
   }
 
   abort ();
@@ -1040,6 +1125,7 @@ guestfs___free_drive_source (struct drive_source *src)
 {
   if (src) {
     free (src->u.path);
+    free (src->username);
     free_drive_servers (src->servers, src->nr_servers);
   }
 }
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 34d3bd7..49c36da 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -119,6 +119,7 @@ enum drive_protocol {
   drive_protocol_nbd,
   drive_protocol_rbd,
   drive_protocol_sheepdog,
+  drive_protocol_ssh,
 };
 
 enum drive_transport {
@@ -157,6 +158,9 @@ struct drive_source {
    */
   size_t nr_servers;
   struct drive_server *servers;
+
+  /* Optional username (may be NULL if not specified). */
+  char *username;
 };
 
 struct drive {
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 2173527..a271300 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -757,6 +757,24 @@ The optional list of C<servers> may be zero or more server addresses
 (C<"hostname:port">).  The format of the server strings is documented
 in L</guestfs_add_drive_opts>.
 
+=head3 SSH
+
+Libguestfs can access disks over a Secure Shell (SSH) connection.
+
+To do this, set the C<protocol> and C<server> and (optionally)
+C<username> parameters of L</guestfs_add_drive_opts> like this:
+
+ char **server = { "remote.example.com", NULL };
+ guestfs_add_drive_opts (g, "/path/to/disk.img",
+                         GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
+                         GUESTFS_ADD_DRIVE_OPTS_PROTOCOL, "ssh",
+                         GUESTFS_ADD_DRIVE_OPTS_SERVER, server,
+                         GUESTFS_ADD_DRIVE_OPTS_USERNAME, "remoteuser",
+                         -1);
+
+The format of the server string is documented in
+L</guestfs_add_drive_opts>.
+
 =head2 INSPECTION
 
 Libguestfs has APIs for inspecting an unknown disk image to find out
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
index f831ebc..335fb71 100644
--- a/src/launch-libvirt.c
+++ b/src/launch-libvirt.c
@@ -1120,7 +1120,9 @@ construct_libvirt_xml_disk (guestfs_h *g,
   case drive_protocol_rbd:
     protocol_str = "rbd"; goto network_protocols;
   case drive_protocol_sheepdog:
-    protocol_str = "sheepdog";
+    protocol_str = "sheepdog"; goto network_protocols;
+  case drive_protocol_ssh:
+    protocol_str = "ssh";
     /*FALLTHROUGH*/
   network_protocols:
     XMLERROR (-1,
@@ -1140,6 +1142,13 @@ construct_libvirt_xml_disk (guestfs_h *g,
     if (construct_libvirt_xml_disk_source_seclabel (g, xo) == -1)
       return -1;
     XMLERROR (-1, xmlTextWriterEndElement (xo));
+    if (drv_priv->real_src.username != NULL) {
+      XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "auth"));
+      XMLERROR (-1,
+                xmlTextWriterWriteAttribute (xo, BAD_CAST "username",
+                                             BAD_CAST drv_priv->real_src.username));
+      XMLERROR (-1, xmlTextWriterEndElement (xo));
+    }
   }
 
   XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "target"));
@@ -1592,6 +1601,7 @@ make_drive_priv (guestfs_h *g, struct drive *drv,
   case drive_protocol_nbd:
   case drive_protocol_rbd:
   case drive_protocol_sheepdog:
+  case drive_protocol_ssh:
     if (!drv->readonly) {
       guestfs___copy_drive_source (g, &drv->src, &drv_priv->real_src);
       drv_priv->format = drv->format ? safe_strdup (g, drv->format) : NULL;
-- 
1.8.1.4




More information about the Libguestfs mailing list