mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Add support for SSH (Secure Shell) block device.
This commit is contained in:
@@ -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
|
||||
|
||||
112
src/drives.c
112
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user