From: "Richard W.M. Jones" <rjones(a)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