[Libguestfs] [PATCH nbdkit] Add ssh plugin using libssh.

Richard W.M. Jones rjones at redhat.com
Wed Mar 6 15:40:31 UTC 2019


---
 plugins/curl/nbdkit-curl-plugin.pod |  22 +-
 plugins/ssh/nbdkit-ssh-plugin.pod   | 150 ++++++++
 configure.ac                        |  17 +
 plugins/ssh/ssh.c                   | 521 ++++++++++++++++++++++++++++
 README                              |   4 +
 TODO                                |  10 +-
 plugins/ssh/Makefile.am             |  69 ++++
 7 files changed, 779 insertions(+), 14 deletions(-)

diff --git a/plugins/curl/nbdkit-curl-plugin.pod b/plugins/curl/nbdkit-curl-plugin.pod
index ef07890..3909baa 100644
--- a/plugins/curl/nbdkit-curl-plugin.pod
+++ b/plugins/curl/nbdkit-curl-plugin.pod
@@ -1,6 +1,6 @@
 =head1 NAME
 
-nbdkit-curl-plugin - nbdkit curl plugin (HTTP, FTP, SSH and other protocols)
+nbdkit-curl-plugin - nbdkit curl plugin (HTTP, FTP and other protocols)
 
 =head1 SYNOPSIS
 
@@ -11,12 +11,12 @@ nbdkit-curl-plugin - nbdkit curl plugin (HTTP, FTP, SSH and other protocols)
 =head1 DESCRIPTION
 
 C<nbdkit-curl-plugin> is a plugin for L<nbdkit(1)> which turns content
-served over HTTP, FTP, SSH, and more, into a Network Block Device.  It
-uses a library called libcurl (also known as cURL) to read data from
-URLs.  The exact list of protocols that libcurl can handle depends on
-how it was compiled, but most versions will handle HTTP, HTTPS, FTP,
-FTPS and SFTP (see: S<C<curl -V>>).  For more information about
-libcurl, see L<http://curl.haxx.se>.
+served over HTTP, FTP, and more, into a Network Block Device.  It uses
+a library called libcurl (also known as cURL) to read data from URLs.
+The exact list of protocols that libcurl can handle depends on how it
+was compiled, but most versions will handle HTTP, HTTPS, FTP, FTPS and
+SFTP (see: S<C<curl -V>>).  For more information about libcurl, see
+L<http://curl.haxx.se>.
 
 B<Note:> This plugin supports writes.  However:
 
@@ -47,8 +47,11 @@ control ports and protocols used to serve NBD see L<nbdkit(1)>).
 
 =head3 Accessing SSH servers
 
-You can also access SSH servers.  This uses the SFTP protocol which is
-built into most SSH servers:
+You can also access SSH servers via this plugin, although it is
+usually better to use the L<nbdkit-ssh-plugin(1)> instead.
+
+This plugin uses the SFTP protocol which is built into most SSH
+servers:
 
  nbdkit -r curl sftp://example.com/~/disk.img
 
@@ -131,6 +134,7 @@ L<curl(1)>,
 L<libcurl(3)>,
 L<CURLOPT_VERBOSE(3)>,
 L<nbdkit(1)>,
+L<nbdkit-ssh-plugin(1)>,
 L<nbdkit-plugin(3)>.
 
 =head1 AUTHORS
diff --git a/plugins/ssh/nbdkit-ssh-plugin.pod b/plugins/ssh/nbdkit-ssh-plugin.pod
new file mode 100644
index 0000000..bcedf4f
--- /dev/null
+++ b/plugins/ssh/nbdkit-ssh-plugin.pod
@@ -0,0 +1,150 @@
+=head1 NAME
+
+nbdkit-ssh-plugin - access disk images over the SSH protocol
+
+=head1 SYNOPSIS
+
+ nbdkit ssh host=HOST path=PATH
+            [port=PORT] [user=USER] [password=PASSWORD|-|+FILENAME]
+            [config=CONFIG_FILE]
+
+=head1 DESCRIPTION
+
+This is an L<nbdkit(1)> plugin which lets you access remote disk
+images over Secure Shell (SSH).  Any server which hosts disk images
+and runs an SSH server can be turned into an NBD source using this
+plugin.
+
+=head2 EXAMPLES
+
+=over 4
+
+=item nbdkit ssh host=ssh.example.com path=disk.img
+
+Open a file called F<disk.img> on remote host C<ssh.example.com>.
+Because the pathname is relative, it is opened relative to the user’s
+home directory on the remote server.
+
+The remote file can be read or written.  To force read-only access add
+the I<-r> flag.
+
+=item nbdkit ssh host=ssh.example.com path=disk.img user=bob
+
+As above but log in using username C<bob> (instead of trying the local
+username).
+
+=back
+
+=head1 PARAMETERS
+
+=over 4
+
+=item B<config=>CONFIG_FILE
+
+Read local SSH configuration from an alternate configuration file.
+
+=item B<config=>
+
+Do not read any local SSH configuration.
+
+The C<config> parameter is optional.  If not specified then
+F<~/.ssh/config> is read.
+
+=item B<host=>HOST
+
+Specify the name or IP address of the remote host.
+
+This parameter is required.
+
+=item B<password=>PASSWORD
+
+Set the password to use when connecting to the remote server.
+
+Note that passing this on the command line is not secure on shared
+machines.
+
+=item B<password=->
+
+Ask for the password (interactively) when nbdkit starts up.
+
+=item B<password=+>FILENAME
+
+Read the password from the named file.  This is the most secure method
+to supply a password, as long as you set the permissions on the file
+appropriately.
+
+=item B<path=>PATH
+
+Specify the path to the remote file.  This can be a relative path in
+which case it is relative to the remote home directory.
+
+This parameter is required.
+
+=item B<port=>PORT
+
+Specify the SSH protocol port name or number.
+
+This parameter is optional.  If not given then the default ssh port is
+used.
+
+=item B<user=>USER
+
+Specify the remote username.
+
+This parameter is optional.  If not given then the local username is
+used.
+
+=back
+
+=head1 NOTES
+
+=head2 Known hosts
+
+The SSH server’s host key is checked at connection time, and must be
+present and correct in the local "known hosts" file (usually
+F<~/.ssh/known_hosts>).  If you have never connected to the SSH server
+before then the connection will usually fail.  You may have to connect
+to the server first using L<ssh(1)> so you can manually accept the
+host key.
+
+=head2 Supported authentication methods
+
+This plugin supports only the following authentication methods:
+C<none>, C<publickey> or C<password>.  In particular note that
+C<keyboard-interactive> is I<not> supported.
+
+=head2 SSH agent
+
+There is no means for nbdkit to ask for the public key passphrase when
+it is running as a server.  Therefore C<publickey> authentication must
+be done in conjunction with L<ssh-agent(1)>.
+
+=head1 FILES
+
+=over 4
+
+=item F<~/.ssh/config>
+
+This is the default local SSH config file which is read to get other
+options.  You can change this using the C<config> option.
+
+=back
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-curl-plugin(1)>,
+L<nbdkit-plugin(3)>,
+L<ssh(1)>,
+L<ssh-agent(1)>,
+L<https://libssh.org>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+Parts derived from Pino Toscano’s qemu libssh driver.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2014-2019 Red Hat Inc.
diff --git a/configure.ac b/configure.ac
index 467d48f..748e5aa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -630,6 +630,21 @@ AS_IF([test "$with_curl" != "no"],[
 ])
 AM_CONDITIONAL([HAVE_CURL],[test "x$CURL_LIBS" != "x"])
 
+dnl Check for libssh (only if you want to compile the ssh plugin).
+AC_ARG_WITH([ssh],
+    [AS_HELP_STRING([--without-ssh],
+                    [disable ssh plugin @<:@default=check@:>@])],
+    [],
+    [with_ssh=check])
+AS_IF([test "$with_ssh" != "no"],[
+    PKG_CHECK_MODULES([SSH], [libssh],[
+        AC_SUBST([SSH_CFLAGS])
+        AC_SUBST([SSH_LIBS])
+    ],
+    [AC_MSG_WARN([libssh not found, ssh plugin will be disabled])])
+])
+AM_CONDITIONAL([HAVE_SSH],[test "x$SSH_LIBS" != "x"])
+
 dnl Check for genisoimage or mkisofs
 dnl (only if you want to compile the iso plugin).
 ISOPROG="no"
@@ -800,6 +815,7 @@ non_lang_plugins="\
         partitioning \
         pattern \
         random \
+        ssh \
         split \
         streaming \
         tar \
@@ -872,6 +888,7 @@ AC_CONFIG_FILES([Makefile
                  plugins/rust/Cargo.toml
                  plugins/rust/Makefile
                  plugins/sh/Makefile
+                 plugins/ssh/Makefile
                  plugins/split/Makefile
                  plugins/streaming/Makefile
                  plugins/tar/Makefile
diff --git a/plugins/ssh/ssh.c b/plugins/ssh/ssh.c
new file mode 100644
index 0000000..562f1a3
--- /dev/null
+++ b/plugins/ssh/ssh.c
@@ -0,0 +1,521 @@
+/* nbdkit
+ * Copyright (C) 2014-2019 Red Hat Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+
+#include <nbdkit-plugin.h>
+
+#include "minmax.h"
+
+static const char *host = NULL;
+static const char *path = NULL;
+static const char *port = NULL;
+static const char *user = NULL;
+static char *password = NULL;
+
+/* config can be:
+ * NULL => parse options from default file
+ * "" => do NOT parse options
+ * some filename => parse options from filename
+ */
+static const char *config = NULL;
+
+/* Use '-D ssh.log=N' to set.
+ *
+ * The log levels (N) are:
+ *
+ *   SSH_LOG_NONE  -> 0
+ *   SSH_LOG_WARN  -> 1
+ *   SSH_LOG_INFO  -> 2
+ *   SSH_LOG_DEBUG -> 3
+ *   SSH_LOG_TRACE -> 4
+ */
+int ssh_debug_log = 0;
+
+static void
+ssh_unload (void)
+{
+  free (password);
+}
+
+/* Called for each key=value passed on the command line. */
+static int
+ssh_config (const char *key, const char *value)
+{
+  if (strcmp (key, "host") == 0)
+    host = value;
+
+  else if (strcmp (key, "path") == 0)
+    path = value;
+
+  else if (strcmp (key, "port") == 0)
+    port = value;
+
+  else if (strcmp (key, "user") == 0)
+    user = value;
+
+  else if (strcmp (key, "password") == 0) {
+    free (password);
+    if (nbdkit_read_password (value, &password) == -1)
+      return -1;
+  }
+
+  else if (strcmp (key, "config") == 0)
+    config = value;
+
+  else {
+    nbdkit_error ("unknown parameter '%s'", key);
+    return -1;
+  }
+
+  return 0;
+}
+
+/* The host and path parameters are mandatory. */
+static int
+ssh_config_complete (void)
+{
+  if (host == NULL || path == NULL) {
+    nbdkit_error ("you must supply the host and path parameters "
+                  "after the plugin name on the command line");
+    return -1;
+  }
+
+  ssh_set_log_level (ssh_debug_log);
+
+  return 0;
+}
+
+#define ssh_config_help \
+  "host=<HOST>     (required) SSH server hostname.\n" \
+  "path=<PATH>     (required) SSH remote path.\n" \
+  "port=<PORT>                SSH protocol port number.\n" \
+  "user=<USER>                SSH user name.\n" \
+  "password=<PASSWORD>        SSH password.\n" \
+  "config=<CONFIG>            Alternate local SSH configuration file." \
+
+/* The per-connection handle. */
+struct ssh_handle {
+  ssh_session session;
+  sftp_session sftp;
+  sftp_file file;
+};
+
+/* Verify the remote host.
+ * See: http://api.libssh.org/master/libssh_tutor_guided_tour.html
+ */
+static int
+verify_remote_host (struct ssh_handle *h)
+{
+  enum ssh_known_hosts_e state;
+  unsigned char *hash = NULL;
+  ssh_key srv_pubkey = NULL;
+  size_t hlen;
+  int rc;
+
+  rc = ssh_get_server_publickey (h->session, &srv_pubkey);
+  if (rc < 0) {
+    nbdkit_error ("could not get server public key");
+    return -1;
+  }
+  rc = ssh_get_publickey_hash (srv_pubkey,
+                               SSH_PUBLICKEY_HASH_SHA1,
+                               &hash, &hlen);
+  ssh_key_free(srv_pubkey);
+  if (rc < 0) {
+    nbdkit_error ("could not get server public key SHA1 hash");
+    return -1;
+  }
+
+  state = ssh_session_is_known_server (h->session);
+  switch (state) {
+  case SSH_KNOWN_HOSTS_OK:
+    /* OK */
+    break;
+
+  case SSH_KNOWN_HOSTS_CHANGED:
+    nbdkit_error ("host key for server changed");
+    ssh_clean_pubkey_hash (&hash);
+    return -1;
+
+  case SSH_KNOWN_HOSTS_OTHER:
+    nbdkit_error ("host key for server was not found "
+                  "but another type of key exists");
+    ssh_clean_pubkey_hash (&hash);
+    return -1;
+
+  case SSH_KNOWN_HOSTS_NOT_FOUND:
+    /* This is not actually an error, but the user must ensure the
+     * host key is set up before using nbdkit so we error out here.
+     */
+    nbdkit_error ("could not find known_hosts file");
+    ssh_clean_pubkey_hash (&hash);
+    return -1;
+
+  case SSH_KNOWN_HOSTS_UNKNOWN:
+    nbdkit_error ("host key is unknown, you must use ssh first "
+                  "and accept the host key");
+    ssh_clean_pubkey_hash (&hash);
+    return -1;
+
+  case SSH_KNOWN_HOSTS_ERROR:
+    nbdkit_error ("known hosts error: %s", ssh_get_error (h->session));
+    ssh_clean_pubkey_hash (&hash);
+    return -1;
+  }
+
+  ssh_clean_pubkey_hash (&hash);
+  return 0;
+}
+
+/* Authenticate.
+ * See: http://api.libssh.org/master/libssh_tutor_authentication.html
+ */
+static int
+authenticate_pubkey (ssh_session session)
+{
+  int rc;
+
+  rc = ssh_userauth_publickey_auto (session, NULL, NULL);
+  if (rc == SSH_AUTH_ERROR)
+    nbdkit_debug ("public key authentication failed: %s",
+                  ssh_get_error (session));
+
+  return rc;
+}
+
+static int
+authenticate_password (ssh_session session, const char *password)
+{
+  int rc;
+
+  rc = ssh_userauth_password (session, NULL, password);
+  if (rc == SSH_AUTH_ERROR)
+    nbdkit_debug ("password authentication failed: %s",
+                  ssh_get_error (session));
+  return rc;
+}
+
+static int
+authenticate (struct ssh_handle *h)
+{
+  int method, rc;
+
+  rc = ssh_userauth_none (h->session, NULL);
+  if (rc == SSH_AUTH_SUCCESS)
+    return 0;
+  if (rc == SSH_AUTH_ERROR)
+    return -1;
+
+  method = ssh_userauth_list (h->session, NULL);
+
+  if (method & SSH_AUTH_METHOD_PUBLICKEY) {
+    rc = authenticate_pubkey (h->session);
+    if (rc == SSH_AUTH_SUCCESS) return 0;
+  }
+
+  /* Example code tries keyboard-interactive here, but we cannot use
+   * that method from a server.
+   */
+
+  if (password != NULL && (method & SSH_AUTH_METHOD_PASSWORD)) {
+    rc = authenticate_password (h->session, password);
+    if (rc == SSH_AUTH_SUCCESS) return 0;
+  }
+
+  nbdkit_error ("all possible authentication methods failed");
+  return -1;
+}
+
+/* Create the per-connection handle. */
+static void *
+ssh_open (int readonly)
+{
+  struct ssh_handle *h;
+  int r;
+  int access_type;
+
+  h = calloc (1, sizeof *h);
+  if (h == NULL) {
+    nbdkit_error ("calloc: %m");
+    return NULL;
+  }
+
+  /* Set up the SSH session. */
+  h->session = ssh_new ();
+  if (!h->session) {
+    nbdkit_error ("failed to initialize libssh session");
+    goto err;
+  }
+
+  r = ssh_options_set (h->session, SSH_OPTIONS_HOST, host);
+  if (r != SSH_OK) {
+    nbdkit_error ("failed to set host in libssh session: %s: %s",
+                  host, ssh_get_error (h->session));
+    goto err;
+  }
+  if (port != NULL) {
+    r = ssh_options_set (h->session, SSH_OPTIONS_PORT_STR, port);
+    if (r != SSH_OK) {
+      nbdkit_error ("failed to set port in libssh session: %s: %s",
+                    port, ssh_get_error (h->session));
+      goto err;
+    }
+  }
+  if (user != NULL) {
+    r = ssh_options_set (h->session, SSH_OPTIONS_USER, user);
+    if (r != SSH_OK) {
+      nbdkit_error ("failed to set user in libssh session: %s: %s",
+                    user, ssh_get_error (h->session));
+      goto err;
+    }
+  }
+
+  /* Read ~/.ssh/config or alternative file. */
+  if (config == NULL || strcmp (config, "") != 0) {
+    r = ssh_options_parse_config (h->session, config);
+    if (r != SSH_OK) {
+      nbdkit_error ("failed to parse local SSH configuration: %s",
+                    ssh_get_error (h->session));
+      goto err;
+    }
+  }
+
+  /* Connect. */
+  r = ssh_connect (h->session);
+  if (r != SSH_OK) {
+    nbdkit_error ("failed to connect to remote host: %s: %s",
+                  host, ssh_get_error (h->session));
+    goto err;
+  }
+
+  /* Verify the remote host. */
+  if (verify_remote_host (h) == -1)
+    goto err;
+
+  /* Authenticate. */
+  if (authenticate (h) == -1)
+    goto err;
+
+  /* Open the SFTP connection and file. */
+  h->sftp = sftp_new (h->session);
+  if (!h->sftp) {
+    nbdkit_error ("failed to allocate sftp session: %s",
+                  ssh_get_error (h->session));
+    goto err;
+  }
+  r = sftp_init (h->sftp);
+  if (r != SSH_OK) {
+    nbdkit_error ("failed to initialize sftp session: %s",
+                  ssh_get_error (h->session));
+    goto err;
+  }
+  access_type = readonly ? O_RDONLY : O_RDWR;
+  h->file = sftp_open (h->sftp, path, access_type, S_IRWXU);
+  if (!h->file) {
+    nbdkit_error ("cannot open file for %s: %s",
+                  readonly ? "reading" : "writing",
+                  ssh_get_error (h->session));
+    goto err;
+  }
+
+  nbdkit_debug ("opened libssh handle");
+
+  return h;
+
+ err:
+  if (h->file)
+    sftp_close (h->file);
+  if (h->sftp)
+    sftp_free (h->sftp);
+  if (h->session) {
+    ssh_disconnect (h->session);
+    ssh_free (h->session);
+  }
+  free (h);
+  return NULL;
+}
+
+/* Free up the per-connection handle. */
+static void
+ssh_close (void *handle)
+{
+  struct ssh_handle *h = handle;
+  int r;
+
+  r = sftp_close (h->file);
+  if (r != SSH_OK)
+    nbdkit_error ("cannot close file: %s", ssh_get_error (h->session));
+
+  sftp_free (h->sftp);
+  ssh_disconnect (h->session);
+  ssh_free (h->session);
+  free (h);
+}
+
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
+
+/* Get the file size. */
+static int64_t
+ssh_get_size (void *handle)
+{
+  struct ssh_handle *h = handle;
+  sftp_attributes attrs;
+  int64_t r;
+
+  attrs = sftp_fstat (h->file);
+  r = attrs->size;
+  sftp_attributes_free (attrs);
+
+  return r;
+}
+
+/* Read data from the remote server. */
+static int
+ssh_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
+{
+  struct ssh_handle *h = handle;
+  int r;
+  ssize_t rs;
+
+  r = sftp_seek64 (h->file, offset);
+  if (r != SSH_OK) {
+    nbdkit_error ("seek64 failed: %s", ssh_get_error (h->session));
+    return -1;
+  }
+
+  while (count > 0) {
+    rs = sftp_read (h->file, buf, count);
+    if (rs < 0) {
+      nbdkit_error ("read failed: %s (%zd)", ssh_get_error (h->session), rs);
+      return -1;
+    }
+    buf += rs;
+    count -= rs;
+  }
+
+  return 0;
+}
+
+/* Write data to the remote server. */
+static int
+ssh_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset)
+{
+  struct ssh_handle *h = handle;
+  int r;
+  ssize_t rs;
+
+  r = sftp_seek64 (h->file, offset);
+  if (r != SSH_OK) {
+    nbdkit_error ("seek64 failed: %s", ssh_get_error (h->session));
+    return -1;
+  }
+
+  while (count > 0) {
+    /* Openssh has a maximum packet size of 256K, so any write
+     * requests larger than this will fail in a peculiar way.  (This
+     * limit doesn't seem to include the SFTP protocol overhead).
+     * Therefore if the count is larger than 128K, reduce the size of
+     * the request.  I don't know whether 256K is a limit that applies
+     * to all servers.
+     */
+    rs = sftp_write (h->file, buf, MIN (count, 128*1024));
+    if (rs < 0) {
+      nbdkit_error ("write failed: %s (%zd)", ssh_get_error (h->session), rs);
+      return -1;
+    }
+    buf += rs;
+    count -= rs;
+  }
+
+  return 0;
+}
+
+static int
+ssh_can_flush (void *handle)
+{
+  struct ssh_handle *h = handle;
+
+  /* I added this extension to openssh 6.5 (April 2013).  It may not
+   * be available in other SSH servers.
+   */
+  return sftp_extension_supported (h->sftp, "fsync at openssh.com", "1");
+}
+
+static int
+ssh_flush (void *handle)
+{
+  struct ssh_handle *h = handle;
+  int r;
+
+ again:
+  r = sftp_fsync (h->file);
+  if (r == SSH_AGAIN)
+    goto again;
+  else if (r != SSH_OK) {
+    nbdkit_error ("fsync failed: %s", ssh_get_error (h->session));
+    return -1;
+  }
+
+  return 0;
+}
+
+static struct nbdkit_plugin plugin = {
+  .name              = "ssh",
+  .version           = PACKAGE_VERSION,
+  .unload            = ssh_unload,
+  .config            = ssh_config,
+  .config_complete   = ssh_config_complete,
+  .config_help       = ssh_config_help,
+  .open              = ssh_open,
+  .close             = ssh_close,
+  .get_size          = ssh_get_size,
+  .pread             = ssh_pread,
+  .pwrite            = ssh_pwrite,
+  .can_flush         = ssh_can_flush,
+  .flush             = ssh_flush,
+};
+
+NBDKIT_REGISTER_PLUGIN(plugin)
diff --git a/README b/README
index 9c4d844..e3b0149 100644
--- a/README
+++ b/README
@@ -77,6 +77,10 @@ For the curl (HTTP/FTP) plugin:
 
  - libcurl
 
+For the ssh plugin:
+
+ - libssh (this is a different library from libssh2 - that will not work)
+
 For the iso plugin:
 
  - genisoimage or mkisofs
diff --git a/TODO b/TODO
index b589127..6e7464b 100644
--- a/TODO
+++ b/TODO
@@ -74,11 +74,11 @@ General ideas for improvements
 Suggestions for plugins
 -----------------------
 
-Note: qemu supports other formats such as libssh, libnfs, iscsi,
-gluster and ceph/rbd, and while similar plugins could be written for
-nbdkit there is no compelling reason unless the result is better than
-qemu-nbd.  For the majority of users it would be better if they were
-directed to qemu-nbd for these use cases.
+Note: qemu supports other formats such as libnfs, iscsi, gluster and
+ceph/rbd, and while similar plugins could be written for nbdkit there
+is no compelling reason unless the result is better than qemu-nbd.
+For the majority of users it would be better if they were directed to
+qemu-nbd for these use cases.
 
 * XVA files
 
diff --git a/plugins/ssh/Makefile.am b/plugins/ssh/Makefile.am
new file mode 100644
index 0000000..89f43a2
--- /dev/null
+++ b/plugins/ssh/Makefile.am
@@ -0,0 +1,69 @@
+# nbdkit
+# Copyright (C) 2014-2019 Red Hat Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+include $(top_srcdir)/common-rules.mk
+
+EXTRA_DIST = nbdkit-ssh-plugin.pod
+
+if HAVE_SSH
+
+plugin_LTLIBRARIES = nbdkit-ssh-plugin.la
+
+nbdkit_ssh_plugin_la_SOURCES = \
+	ssh.c \
+	$(top_srcdir)/include/nbdkit-plugin.h
+
+nbdkit_ssh_plugin_la_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/common/include
+nbdkit_ssh_plugin_la_CFLAGS = \
+	$(WARNINGS_CFLAGS) \
+	$(SSH_CFLAGS)
+nbdkit_ssh_plugin_la_LIBADD = \
+	$(SSH_LIBS)
+nbdkit_ssh_plugin_la_LDFLAGS = \
+	-module -avoid-version -shared \
+	-Wl,--version-script=$(top_srcdir)/plugins/plugins.syms
+
+if HAVE_POD
+
+man_MANS = nbdkit-ssh-plugin.1
+CLEANFILES += $(man_MANS)
+
+nbdkit-ssh-plugin.1: nbdkit-ssh-plugin.pod
+	$(PODWRAPPER) --section=1 --man $@ \
+	    --html $(top_builddir)/html/$@.html \
+	    $<
+
+endif HAVE_POD
+
+endif
-- 
2.20.1




More information about the Libguestfs mailing list