Richard W.M. Jones
2017-Feb-03 14:21 UTC
[Libguestfs] [PATCH 0/5] Support socket activation in virt-p2v.
As the subject says, support socket activation in virt-p2v. I have added upstream support for socket activation to nbdkit already: https://github.com/libguestfs/nbdkit/commit/7ff39d028c6359f5c0925ed2cf4a2c4c751af2e4 I posted a patch for qemu-nbd, still waiting on more reviews for that one: https://www.mail-archive.com/qemu-devel@nongnu.org/msg427246.html I tested this against old and new qemu and old and new nbdkit (note that it falls back to non-SA if SA fails). Rich.
Richard W.M. Jones
2017-Feb-03 14:21 UTC
[Libguestfs] [PATCH 1/5] p2v: Move NBD-related functions into a separate file.
This is almost pure code motion, but I changed the name and prototype of the function 'wait_nbd' to make its purpose clearer, and to remove the unnecessary timeout setting (which is hard-coded). --- p2v/Makefile.am | 1 + p2v/conversion.c | 329 +------------------------------------ p2v/main.c | 86 +--------- p2v/nbd.c | 488 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ p2v/p2v.h | 16 +- 5 files changed, 506 insertions(+), 414 deletions(-) create mode 100644 p2v/nbd.c diff --git a/p2v/Makefile.am b/p2v/Makefile.am index 0a2c2a8..93d7f47 100644 --- a/p2v/Makefile.am +++ b/p2v/Makefile.am @@ -81,6 +81,7 @@ virt_p2v_SOURCES = \ kernel.c \ kernel-cmdline.c \ main.c \ + nbd.c \ p2v.h \ ssh.c \ utils.c \ diff --git a/p2v/conversion.c b/p2v/conversion.c index aa9cd4c..44a3c58 100644 --- a/p2v/conversion.c +++ b/p2v/conversion.c @@ -82,21 +82,14 @@ xmlBufferDetach (xmlBufferPtr buf) } #endif -/* How long to wait for the NBD server to start (seconds). */ -#define WAIT_NBD_TIMEOUT 10 - /* Data per NBD connection / physical disk. */ struct data_conn { mexp_h *h; /* miniexpect handle to ssh */ - pid_t nbd_pid; /* qemu pid */ + pid_t nbd_pid; /* NBD server PID */ int nbd_local_port; /* local NBD port on physical machine */ int nbd_remote_port; /* remote NBD port on conversion server */ }; -static pid_t start_nbd (int nbd_local_port, const char *device); -static pid_t start_qemu_nbd (int nbd_local_port, const char *device); -static pid_t start_nbdkit (int nbd_local_port, const char *device); -static int wait_nbd (int nbd_local_port, int timeout_seconds); static void cleanup_data_conns (struct data_conn *data_conns, size_t nr); static void generate_name (struct config *, const char *filename); static void generate_libvirt_xml (struct config *, struct data_conn *, const char *filename); @@ -288,14 +281,17 @@ start_conversion (struct config *config, /* Start NBD server listening on the given port number. */ data_conns[i].nbd_pid - start_nbd (data_conns[i].nbd_local_port, device); - if (data_conns[i].nbd_pid == 0) + start_nbd_server (data_conns[i].nbd_local_port, device); + if (data_conns[i].nbd_pid == 0) { + set_conversion_error ("NBD server error: %s", get_nbd_error ()); goto out; + } - /* Wait for NBD server to listen */ - if (wait_nbd (data_conns[i].nbd_local_port, - WAIT_NBD_TIMEOUT) == -1) + /* Wait for NBD server to start up and listen. */ + if (wait_for_nbd_server_to_start (data_conns[i].nbd_local_port) == -1) { + set_conversion_error ("NBD server error: %s", get_nbd_error ()); goto out; + } } /* Create a remote directory name which will be used for libvirt @@ -465,313 +461,6 @@ cancel_conversion (void) set_cancel_requested (1); } -/** - * Start the first server found in the C<nbd_servers> list. - * - * We previously tested all NBD servers (see C<test_nbd_servers>) so - * we only need to run the first server in the list. - * - * Returns the process ID (E<gt> 0) or C<0> if there is an error. - */ -static pid_t -start_nbd (int port, const char *device) -{ - size_t i; - - for (i = 0; i < NR_NBD_SERVERS; ++i) { - switch (nbd_servers[i]) { - case NO_SERVER: - /* ignore */ - break; - - case QEMU_NBD: - return start_qemu_nbd (port, device); - - case NBDKIT: - return start_nbdkit (port, device); - - default: - abort (); - } - } - - /* This should never happen because of the checks in - * test_nbd_servers. - */ - abort (); -} - -/** - * Start a local L<qemu-nbd(1)> process. - * - * Returns the process ID (E<gt> 0) or C<0> if there is an error. - */ -static pid_t -start_qemu_nbd (int port, const char *device) -{ - pid_t pid; - char port_str[64]; - -#if DEBUG_STDERR - fprintf (stderr, "starting qemu-nbd for %s on port %d\n", device, port); -#endif - - snprintf (port_str, sizeof port_str, "%d", port); - - pid = fork (); - if (pid == -1) { - set_conversion_error ("fork: %m"); - return 0; - } - - if (pid == 0) { /* Child. */ - close (0); - open ("/dev/null", O_RDONLY); - - execlp ("qemu-nbd", - "qemu-nbd", - "-r", /* readonly (vital!) */ - "-p", port_str, /* listening port */ - "-t", /* persistent */ - "-f", "raw", /* force raw format */ - "-b", "localhost", /* listen only on loopback interface */ - "--cache=unsafe", /* use unsafe caching for speed */ - device, /* a device like /dev/sda */ - NULL); - perror ("qemu-nbd"); - _exit (EXIT_FAILURE); - } - - /* Parent. */ - return pid; -} - -/** - * Start a local L<nbdkit(1)> process using the - * L<nbdkit-file-plugin(1)>. - * - * Returns the process ID (E<gt> 0) or C<0> if there is an error. - */ -static pid_t -start_nbdkit (int port, const char *device) -{ - pid_t pid; - char port_str[64]; - CLEANUP_FREE char *file_str = NULL; - -#if DEBUG_STDERR - fprintf (stderr, "starting nbdkit for %s on port %d\n", device, port); -#endif - - snprintf (port_str, sizeof port_str, "%d", port); - - if (asprintf (&file_str, "file=%s", device) == -1) - error (EXIT_FAILURE, errno, "asprintf"); - - pid = fork (); - if (pid == -1) { - set_conversion_error ("fork: %m"); - return 0; - } - - if (pid == 0) { /* Child. */ - close (0); - open ("/dev/null", O_RDONLY); - - execlp ("nbdkit", - "nbdkit", - "-r", /* readonly (vital!) */ - "-p", port_str, /* listening port */ - "-i", "localhost", /* listen only on loopback interface */ - "-f", /* don't fork */ - "file", /* file plugin */ - file_str, /* a device like file=/dev/sda */ - NULL); - perror ("nbdkit"); - _exit (EXIT_FAILURE); - } - - /* Parent. */ - return pid; -} - -static int bind_source_port (int sockfd, int family, int source_port); - -/** - * Connect to C<hostname:dest_port>, resolving the address using - * L<getaddrinfo(3)>. - * - * This also sets the source port of the connection to the first free - * port number E<ge> C<source_port>. - * - * This may involve multiple connections - to IPv4 and IPv6 for - * instance. - */ -static int -connect_with_source_port (const char *hostname, int dest_port, int source_port) -{ - struct addrinfo hints; - struct addrinfo *results, *rp; - char dest_port_str[16]; - int r, sockfd = -1; - int reuseaddr = 1; - - snprintf (dest_port_str, sizeof dest_port_str, "%d", dest_port); - - memset (&hints, 0, sizeof hints); - hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */ - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; /* numeric dest port number */ - hints.ai_protocol = 0; /* any protocol */ - - r = getaddrinfo (hostname, dest_port_str, &hints, &results); - if (r != 0) { - set_conversion_error ("getaddrinfo: %s/%s: %s", - hostname, dest_port_str, gai_strerror (r)); - return -1; - } - - for (rp = results; rp != NULL; rp = rp->ai_next) { - sockfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sockfd == -1) - continue; - - /* If we run p2v repeatedly (say, running the tests in a loop), - * there's a decent chance we'll end up trying to bind() to a port - * that is in TIME_WAIT from a prior run. Handle that gracefully - * with SO_REUSEADDR. - */ - if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, - &reuseaddr, sizeof reuseaddr) == -1) - perror ("warning: setsockopt"); - - /* Need to bind the source port. */ - if (bind_source_port (sockfd, rp->ai_family, source_port) == -1) { - close (sockfd); - sockfd = -1; - continue; - } - - /* Connect. */ - if (connect (sockfd, rp->ai_addr, rp->ai_addrlen) == -1) { - set_conversion_error ("waiting for NBD server to start: " - "connect to %s/%s: %m", - hostname, dest_port_str); - close (sockfd); - sockfd = -1; - continue; - } - - break; - } - - freeaddrinfo (results); - return sockfd; -} - -static int -bind_source_port (int sockfd, int family, int source_port) -{ - struct addrinfo hints; - struct addrinfo *results, *rp; - char source_port_str[16]; - int r; - - snprintf (source_port_str, sizeof source_port_str, "%d", source_port); - - memset (&hints, 0, sizeof (hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* numeric port number */ - hints.ai_protocol = 0; /* any protocol */ - - r = getaddrinfo ("localhost", source_port_str, &hints, &results); - if (r != 0) { - set_conversion_error ("getaddrinfo (bind): localhost/%s: %s", - source_port_str, gai_strerror (r)); - return -1; - } - - for (rp = results; rp != NULL; rp = rp->ai_next) { - if (bind (sockfd, rp->ai_addr, rp->ai_addrlen) == 0) - goto bound; - } - - set_conversion_error ("waiting for NBD server to start: " - "bind to source port %d: %m", - source_port); - freeaddrinfo (results); - return -1; - - bound: - freeaddrinfo (results); - return 0; -} - -static int -wait_nbd (int nbd_local_port, int timeout_seconds) -{ - int sockfd = -1; - int result = -1; - time_t start_t, now_t; - struct timespec half_sec = { .tv_sec = 0, .tv_nsec = 500000000 }; - struct timeval timeout = { .tv_usec = 0 }; - char magic[8]; /* NBDMAGIC */ - size_t bytes_read = 0; - ssize_t recvd; - - time (&start_t); - - for (;;) { - time (&now_t); - - if (now_t - start_t >= timeout_seconds) - goto cleanup; - - /* Source port for probing NBD server should be one greater than - * nbd_local_port. It's not guaranteed to always bind to this - * port, but it will hint the kernel to start there and try - * incrementally higher ports if needed. This avoids the case - * where the kernel selects nbd_local_port as our source port, and - * we immediately connect to ourself. See: - * https://bugzilla.redhat.com/show_bug.cgi?id=1167774#c9 - */ - sockfd = connect_with_source_port ("localhost", nbd_local_port, - nbd_local_port+1); - if (sockfd >= 0) - break; - - nanosleep (&half_sec, NULL); - } - - time (&now_t); - timeout.tv_sec = (start_t + timeout_seconds) - now_t; - setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout); - - do { - recvd = recv (sockfd, magic, sizeof magic - bytes_read, 0); - - if (recvd == -1) { - set_conversion_error ("waiting for NBD server to start: recv: %m"); - goto cleanup; - } - - bytes_read += recvd; - } while (bytes_read < sizeof magic); - - if (memcmp (magic, "NBDMAGIC", sizeof magic) != 0) { - set_conversion_error ("waiting for NBD server to start: " - "'NBDMAGIC' was not received from NBD server"); - goto cleanup; - } - - result = 0; - cleanup: - close (sockfd); - - return result; -} - static void cleanup_data_conns (struct data_conn *data_conns, size_t nr) { diff --git a/p2v/main.c b/p2v/main.c index f616b4f..d1eece6 100644 --- a/p2v/main.c +++ b/p2v/main.c @@ -54,7 +54,6 @@ int is_iso_environment = 0; int nbd_local_port; int feature_colours_option = 0; int force_colour = 0; -enum nbd_server nbd_servers[NR_NBD_SERVERS] = { QEMU_NBD, NBDKIT }; static const char *test_disk = NULL; static void udevadm_settle (void); @@ -62,7 +61,6 @@ static void set_config_defaults (struct config *config); static void find_all_disks (void); static void find_all_interfaces (void); static int cpuinfo_flags (void); -static void test_nbd_servers (void); enum { HELP_OPTION = CHAR_MAX + 1 }; static const char options[] = "Vv"; @@ -133,35 +131,6 @@ display_long_options (const struct option *long_options) exit (EXIT_SUCCESS); } -static void -set_nbd_servers (const char *opt) -{ - size_t i; - CLEANUP_FREE_STRING_LIST char **strs - = guestfs_int_split_string (',', opt); - - if (strs == NULL) - error (EXIT_FAILURE, errno, _("malloc")); - - if (strs[0] == NULL) - error (EXIT_FAILURE, 0, _("--nbd option cannot be empty")); - - for (i = 0; strs[i] != NULL; ++i) { - if (i >= NR_NBD_SERVERS) - error (EXIT_FAILURE, 0, _("too many --nbd servers")); - - if (STREQ (strs[i], "qemu-nbd") || STREQ (strs[i], "qemu")) - nbd_servers[i] = QEMU_NBD; - else if (STREQ (strs[i], "nbdkit")) - nbd_servers[i] = NBDKIT; - else - error (EXIT_FAILURE, 0, _("--nbd: unknown server: %s"), strs[i]); - } - - for (; i < NR_NBD_SERVERS; ++i) - nbd_servers[i] = NO_SERVER; -} - int main (int argc, char *argv[]) { @@ -214,7 +183,7 @@ main (int argc, char *argv[]) is_iso_environment = 1; } else if (STREQ (long_options[option_index].name, "nbd")) { - set_nbd_servers (optarg); + set_nbd_option (optarg); /* in nbd.c */ } else if (STREQ (long_options[option_index].name, "test-disk")) { if (test_disk != NULL) @@ -309,59 +278,6 @@ udevadm_settle (void) ignore_value (system ("udevadm settle")); } -/* Test the nbd_servers[] array to see which servers are actually - * installed and appear to be working. If a server is not installed - * we set the corresponding nbd_servers[i] = NO_SERVER. - */ -static void -test_nbd_servers (void) -{ - size_t i; - int r; - - for (i = 0; i < NR_NBD_SERVERS; ++i) { - switch (nbd_servers[i]) { - case NO_SERVER: - /* ignore entry */ - break; - - case QEMU_NBD: - r = system ("qemu-nbd --version" -#ifndef DEBUG_STDERR - " >/dev/null 2>&1" -#endif - ); - if (r != 0) - nbd_servers[i] = NO_SERVER; - break; - - case NBDKIT: - r = system ("nbdkit file --version" -#ifndef DEBUG_STDERR - " >/dev/null 2>&1" -#endif - ); - if (r != 0) - nbd_servers[i] = NO_SERVER; - break; - - default: - abort (); - } - } - - for (i = 0; i < NR_NBD_SERVERS; ++i) - if (nbd_servers[i] != NO_SERVER) - return; - - /* If we reach here, there are no servers left, so we have to exit. */ - fprintf (stderr, - _("%s: no working NBD server was found, cannot continue.\n" - "Please check the --nbd option in the virt-p2v(1) man page.\n"), - getprogname ()); - exit (EXIT_FAILURE); -} - static void set_config_defaults (struct config *config) { diff --git a/p2v/nbd.c b/p2v/nbd.c new file mode 100644 index 0000000..519774f --- /dev/null +++ b/p2v/nbd.c @@ -0,0 +1,488 @@ +/* virt-p2v + * Copyright (C) 2009-2017 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * This file handles the virt-p2v I<--nbd> command line option + * and running either L<qemu-nbd(8)> or L<nbdkit(1)>. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <netdb.h> +#include <errno.h> +#include <error.h> +#include <libintl.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "getprogname.h" + +#include "p2v.h" + +/* How long to wait for the NBD server to start (seconds). */ +#define WAIT_NBD_TIMEOUT 10 + +/* virt-p2v --nbd option. */ +enum nbd_server { + NO_SERVER = 0, + QEMU_NBD = 1, + NBDKIT = 2, +#define NR_NBD_SERVERS 2 +}; +static enum nbd_server nbd_servers[NR_NBD_SERVERS] = { QEMU_NBD, NBDKIT }; + +static pid_t start_qemu_nbd (int nbd_local_port, const char *device); +static pid_t start_nbdkit (int nbd_local_port, const char *device); +static int connect_with_source_port (const char *hostname, int dest_port, int source_port); +static int bind_source_port (int sockfd, int family, int source_port); + +static char *nbd_error; + +static void set_nbd_error (const char *fs, ...) + __attribute__((format(printf,1,2))); + +static void +set_nbd_error (const char *fs, ...) +{ + va_list args; + char *msg; + int len; + + va_start (args, fs); + len = vasprintf (&msg, fs, args); + va_end (args); + + if (len < 0) + error (EXIT_FAILURE, errno, + "vasprintf (original error format string: %s)", fs); + + free (nbd_error); + nbd_error = msg; +} + +const char * +get_nbd_error (void) +{ + return nbd_error; +} + +/** + * The main program calls this to set the I<--nbd> option. + */ +void +set_nbd_option (const char *opt) +{ + size_t i; + CLEANUP_FREE_STRING_LIST char **strs + = guestfs_int_split_string (',', opt); + + if (strs == NULL) + error (EXIT_FAILURE, errno, _("malloc")); + + if (strs[0] == NULL) + error (EXIT_FAILURE, 0, _("--nbd option cannot be empty")); + + for (i = 0; strs[i] != NULL; ++i) { + if (i >= NR_NBD_SERVERS) + error (EXIT_FAILURE, 0, _("too many --nbd servers")); + + if (STREQ (strs[i], "qemu-nbd") || STREQ (strs[i], "qemu")) + nbd_servers[i] = QEMU_NBD; + else if (STREQ (strs[i], "nbdkit")) + nbd_servers[i] = NBDKIT; + else + error (EXIT_FAILURE, 0, _("--nbd: unknown server: %s"), strs[i]); + } + + for (; i < NR_NBD_SERVERS; ++i) + nbd_servers[i] = NO_SERVER; +} + +/** + * Test the I<--nbd> option (or built-in default list) to see which + * servers are actually installed and appear to be working. + * + * If a server is not installed we set the corresponding + * C<nbd_servers[i] = NO_SERVER>. + */ +void +test_nbd_servers (void) +{ + size_t i; + int r; + + for (i = 0; i < NR_NBD_SERVERS; ++i) { + switch (nbd_servers[i]) { + case NO_SERVER: + /* ignore entry */ + break; + + case QEMU_NBD: + r = system ("qemu-nbd --version" +#ifndef DEBUG_STDERR + " >/dev/null 2>&1" +#endif + ); + if (r != 0) + nbd_servers[i] = NO_SERVER; + break; + + case NBDKIT: + r = system ("nbdkit file --version" +#ifndef DEBUG_STDERR + " >/dev/null 2>&1" +#endif + ); + if (r != 0) + nbd_servers[i] = NO_SERVER; + break; + + default: + abort (); + } + } + + for (i = 0; i < NR_NBD_SERVERS; ++i) + if (nbd_servers[i] != NO_SERVER) + return; + + /* If we reach here, there are no servers left, so we have to exit. */ + fprintf (stderr, + _("%s: no working NBD server was found, cannot continue.\n" + "Please check the --nbd option in the virt-p2v(1) man page.\n"), + getprogname ()); + exit (EXIT_FAILURE); +} + +/** + * Start the first server found in the C<nbd_servers> list. + * + * We previously tested all NBD servers (see C<test_nbd_servers>) so + * we only need to run the first server in the list. + * + * Returns the process ID (E<gt> 0) or C<0> if there is an error. + */ +pid_t +start_nbd_server (int port, const char *device) +{ + size_t i; + + for (i = 0; i < NR_NBD_SERVERS; ++i) { + switch (nbd_servers[i]) { + case NO_SERVER: + /* ignore */ + break; + + case QEMU_NBD: + return start_qemu_nbd (port, device); + + case NBDKIT: + return start_nbdkit (port, device); + + default: + abort (); + } + } + + /* This should never happen because of the checks in + * test_nbd_servers. + */ + abort (); +} + +/** + * Start a local L<qemu-nbd(1)> process. + * + * Returns the process ID (E<gt> 0) or C<0> if there is an error. + */ +static pid_t +start_qemu_nbd (int port, const char *device) +{ + pid_t pid; + char port_str[64]; + +#if DEBUG_STDERR + fprintf (stderr, "starting qemu-nbd for %s on port %d\n", device, port); +#endif + + snprintf (port_str, sizeof port_str, "%d", port); + + pid = fork (); + if (pid == -1) { + set_nbd_error ("fork: %m"); + return 0; + } + + if (pid == 0) { /* Child. */ + close (0); + open ("/dev/null", O_RDONLY); + + execlp ("qemu-nbd", + "qemu-nbd", + "-r", /* readonly (vital!) */ + "-p", port_str, /* listening port */ + "-t", /* persistent */ + "-f", "raw", /* force raw format */ + "-b", "localhost", /* listen only on loopback interface */ + "--cache=unsafe", /* use unsafe caching for speed */ + device, /* a device like /dev/sda */ + NULL); + perror ("qemu-nbd"); + _exit (EXIT_FAILURE); + } + + /* Parent. */ + return pid; +} + +/** + * Start a local L<nbdkit(1)> process using the + * L<nbdkit-file-plugin(1)>. + * + * Returns the process ID (E<gt> 0) or C<0> if there is an error. + */ +static pid_t +start_nbdkit (int port, const char *device) +{ + pid_t pid; + char port_str[64]; + CLEANUP_FREE char *file_str = NULL; + +#if DEBUG_STDERR + fprintf (stderr, "starting nbdkit for %s on port %d\n", device, port); +#endif + + snprintf (port_str, sizeof port_str, "%d", port); + + if (asprintf (&file_str, "file=%s", device) == -1) + error (EXIT_FAILURE, errno, "asprintf"); + + pid = fork (); + if (pid == -1) { + set_nbd_error ("fork: %m"); + return 0; + } + + if (pid == 0) { /* Child. */ + close (0); + open ("/dev/null", O_RDONLY); + + execlp ("nbdkit", + "nbdkit", + "-r", /* readonly (vital!) */ + "-p", port_str, /* listening port */ + "-i", "localhost", /* listen only on loopback interface */ + "-f", /* don't fork */ + "file", /* file plugin */ + file_str, /* a device like file=/dev/sda */ + NULL); + perror ("nbdkit"); + _exit (EXIT_FAILURE); + } + + /* Parent. */ + return pid; +} + +/** + * Wait for a local NBD server to start and be listening for + * connections. + */ +int +wait_for_nbd_server_to_start (int nbd_local_port) +{ + int sockfd = -1; + int result = -1; + time_t start_t, now_t; + struct timespec half_sec = { .tv_sec = 0, .tv_nsec = 500000000 }; + struct timeval timeout = { .tv_usec = 0 }; + char magic[8]; /* NBDMAGIC */ + size_t bytes_read = 0; + ssize_t recvd; + + time (&start_t); + + for (;;) { + time (&now_t); + + if (now_t - start_t >= WAIT_NBD_TIMEOUT) { + set_nbd_error ("timed out waiting for NBD server to start"); + goto cleanup; + } + + /* Source port for probing NBD server should be one greater than + * nbd_local_port. It's not guaranteed to always bind to this + * port, but it will hint the kernel to start there and try + * incrementally higher ports if needed. This avoids the case + * where the kernel selects nbd_local_port as our source port, and + * we immediately connect to ourself. See: + * https://bugzilla.redhat.com/show_bug.cgi?id=1167774#c9 + */ + sockfd = connect_with_source_port ("localhost", nbd_local_port, + nbd_local_port+1); + if (sockfd >= 0) + break; + + nanosleep (&half_sec, NULL); + } + + time (&now_t); + timeout.tv_sec = (start_t + WAIT_NBD_TIMEOUT) - now_t; + setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout); + + do { + recvd = recv (sockfd, magic, sizeof magic - bytes_read, 0); + + if (recvd == -1) { + set_nbd_error ("waiting for NBD server to start: recv: %m"); + goto cleanup; + } + + bytes_read += recvd; + } while (bytes_read < sizeof magic); + + if (memcmp (magic, "NBDMAGIC", sizeof magic) != 0) { + set_nbd_error ("waiting for NBD server to start: " + "'NBDMAGIC' was not received from NBD server"); + goto cleanup; + } + + result = 0; + cleanup: + close (sockfd); + + return result; +} + +/** + * Connect to C<hostname:dest_port>, resolving the address using + * L<getaddrinfo(3)>. + * + * This also sets the source port of the connection to the first free + * port number E<ge> C<source_port>. + * + * This may involve multiple connections - to IPv4 and IPv6 for + * instance. + */ +static int +connect_with_source_port (const char *hostname, int dest_port, int source_port) +{ + struct addrinfo hints; + struct addrinfo *results, *rp; + char dest_port_str[16]; + int r, sockfd = -1; + int reuseaddr = 1; + + snprintf (dest_port_str, sizeof dest_port_str, "%d", dest_port); + + memset (&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; /* numeric dest port number */ + hints.ai_protocol = 0; /* any protocol */ + + r = getaddrinfo (hostname, dest_port_str, &hints, &results); + if (r != 0) { + set_nbd_error ("getaddrinfo: %s/%s: %s", + hostname, dest_port_str, gai_strerror (r)); + return -1; + } + + for (rp = results; rp != NULL; rp = rp->ai_next) { + sockfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sockfd == -1) + continue; + + /* If we run p2v repeatedly (say, running the tests in a loop), + * there's a decent chance we'll end up trying to bind() to a port + * that is in TIME_WAIT from a prior run. Handle that gracefully + * with SO_REUSEADDR. + */ + if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, + &reuseaddr, sizeof reuseaddr) == -1) + perror ("warning: setsockopt"); + + /* Need to bind the source port. */ + if (bind_source_port (sockfd, rp->ai_family, source_port) == -1) { + close (sockfd); + sockfd = -1; + continue; + } + + /* Connect. */ + if (connect (sockfd, rp->ai_addr, rp->ai_addrlen) == -1) { + set_nbd_error ("waiting for NBD server to start: " + "connect to %s/%s: %m", + hostname, dest_port_str); + close (sockfd); + sockfd = -1; + continue; + } + + break; + } + + freeaddrinfo (results); + return sockfd; +} + +static int +bind_source_port (int sockfd, int family, int source_port) +{ + struct addrinfo hints; + struct addrinfo *results, *rp; + char source_port_str[16]; + int r; + + snprintf (source_port_str, sizeof source_port_str, "%d", source_port); + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* numeric port number */ + hints.ai_protocol = 0; /* any protocol */ + + r = getaddrinfo ("localhost", source_port_str, &hints, &results); + if (r != 0) { + set_nbd_error ("getaddrinfo (bind): localhost/%s: %s", + source_port_str, gai_strerror (r)); + return -1; + } + + for (rp = results; rp != NULL; rp = rp->ai_next) { + if (bind (sockfd, rp->ai_addr, rp->ai_addrlen) == 0) + goto bound; + } + + set_nbd_error ("waiting for NBD server to start: " + "bind to source port %d: %m", + source_port); + freeaddrinfo (results); + return -1; + + bound: + freeaddrinfo (results); + return 0; +} diff --git a/p2v/p2v.h b/p2v/p2v.h index d805aaf..800862d 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -63,15 +63,6 @@ extern int feature_colours_option; /* virt-p2v --colours option (used by ansi_* macros). */ extern int force_colour; -/* virt-p2v --nbd option. */ -enum nbd_server { - NO_SERVER = 0, - QEMU_NBD = 1, - NBDKIT = 2, -#define NR_NBD_SERVERS 2 -}; -extern enum nbd_server nbd_servers[NR_NBD_SERVERS]; - /* config.c */ struct config { char *server; @@ -144,6 +135,13 @@ extern mexp_h *start_remote_connection (struct config *, const char *remote_dir) extern const char *get_ssh_error (void); extern int scp_file (struct config *config, const char *localfile, const char *remotefile); +/* nbd.c */ +extern void set_nbd_option (const char *opt); +extern void test_nbd_servers (void); +extern pid_t start_nbd_server (int nbd_local_port, const char *device); +extern int wait_for_nbd_server_to_start (int nbd_local_port); +const char *get_nbd_error (void); + /* utils.c */ extern uint64_t get_blockdev_size (const char *dev); extern char *get_blockdev_model (const char *dev); -- 2.9.3
Richard W.M. Jones
2017-Feb-03 14:21 UTC
[Libguestfs] [PATCH 2/5] p2v: Clean up ugly implementation of --nbd option.
The previous implementation was pretty ugly. This reimplements things a bit more cleanly. --- p2v/nbd.c | 129 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/p2v/nbd.c b/p2v/nbd.c index 519774f..ce90361 100644 --- a/p2v/nbd.c +++ b/p2v/nbd.c @@ -36,6 +36,7 @@ #include <libintl.h> #include <sys/types.h> #include <sys/socket.h> +#include <assert.h> #include "getprogname.h" @@ -44,14 +45,24 @@ /* How long to wait for the NBD server to start (seconds). */ #define WAIT_NBD_TIMEOUT 10 -/* virt-p2v --nbd option. */ +/* List of servers specified by the --nbd option. */ enum nbd_server { - NO_SERVER = 0, + /* 0 is reserved for "end of list" */ QEMU_NBD = 1, NBDKIT = 2, -#define NR_NBD_SERVERS 2 }; -static enum nbd_server nbd_servers[NR_NBD_SERVERS] = { QEMU_NBD, NBDKIT }; +static enum nbd_server *cmdline_servers = NULL; + +/* If no --nbd option is passed, we use this standard list instead. + * Must match the documentation in virt-p2v(1). + */ +static const enum nbd_server standard_servers[] + { QEMU_NBD, NBDKIT, 0 }; + +/* After testing the list of servers passed by the user, this is + * server we decide to use. + */ +static enum nbd_server use_server; static pid_t start_qemu_nbd (int nbd_local_port, const char *device); static pid_t start_nbdkit (int nbd_local_port, const char *device); @@ -94,59 +105,70 @@ get_nbd_error (void) void set_nbd_option (const char *opt) { - size_t i; - CLEANUP_FREE_STRING_LIST char **strs - = guestfs_int_split_string (',', opt); + size_t i, len; + CLEANUP_FREE_STRING_LIST char **strs = NULL; + + if (cmdline_servers != NULL) + error (EXIT_FAILURE, 0, _("--nbd option appears multiple times")); + + strs = guestfs_int_split_string (',', opt); if (strs == NULL) error (EXIT_FAILURE, errno, _("malloc")); - if (strs[0] == NULL) + len = guestfs_int_count_strings (strs); + if (len == 0) error (EXIT_FAILURE, 0, _("--nbd option cannot be empty")); - for (i = 0; strs[i] != NULL; ++i) { - if (i >= NR_NBD_SERVERS) - error (EXIT_FAILURE, 0, _("too many --nbd servers")); + cmdline_servers = malloc (sizeof (enum nbd_server) * (len + 1)); + if (cmdline_servers == NULL) + error (EXIT_FAILURE, errno, _("malloc")); + for (i = 0; strs[i] != NULL; ++i) { if (STREQ (strs[i], "qemu-nbd") || STREQ (strs[i], "qemu")) - nbd_servers[i] = QEMU_NBD; + cmdline_servers[i] = QEMU_NBD; else if (STREQ (strs[i], "nbdkit")) - nbd_servers[i] = NBDKIT; + cmdline_servers[i] = NBDKIT; else error (EXIT_FAILURE, 0, _("--nbd: unknown server: %s"), strs[i]); } - for (; i < NR_NBD_SERVERS; ++i) - nbd_servers[i] = NO_SERVER; + assert (i == len); + cmdline_servers[i] = 0; /* marks the end of the list */ } /** * Test the I<--nbd> option (or built-in default list) to see which * servers are actually installed and appear to be working. * - * If a server is not installed we set the corresponding - * C<nbd_servers[i] = NO_SERVER>. + * Set the C<use_server> global accordingly. */ void test_nbd_servers (void) { size_t i; int r; + const enum nbd_server *servers; - for (i = 0; i < NR_NBD_SERVERS; ++i) { - switch (nbd_servers[i]) { - case NO_SERVER: - /* ignore entry */ - break; + if (cmdline_servers != NULL) + servers = cmdline_servers; + else + servers = standard_servers; + + use_server = 0; + for (i = 0; servers[i] != 0; ++i) { + switch (servers[i]) { case QEMU_NBD: r = system ("qemu-nbd --version" #ifndef DEBUG_STDERR " >/dev/null 2>&1" #endif ); - if (r != 0) - nbd_servers[i] = NO_SERVER; + if (r == 0) { + use_server = servers[i]; + goto finish; + } break; case NBDKIT: @@ -155,8 +177,10 @@ test_nbd_servers (void) " >/dev/null 2>&1" #endif ); - if (r != 0) - nbd_servers[i] = NO_SERVER; + if (r == 0) { + use_server = servers[i]; + goto finish; + } break; default: @@ -164,52 +188,41 @@ test_nbd_servers (void) } } - for (i = 0; i < NR_NBD_SERVERS; ++i) - if (nbd_servers[i] != NO_SERVER) - return; + finish: + if (use_server == 0) { + fprintf (stderr, + _("%s: no working NBD server was found, cannot continue.\n" + "Please check the --nbd option in the virt-p2v(1) man page.\n"), + getprogname ()); + exit (EXIT_FAILURE); + } - /* If we reach here, there are no servers left, so we have to exit. */ - fprintf (stderr, - _("%s: no working NBD server was found, cannot continue.\n" - "Please check the --nbd option in the virt-p2v(1) man page.\n"), - getprogname ()); - exit (EXIT_FAILURE); + /* Release memory used by the --nbd option. */ + free (cmdline_servers); + cmdline_servers = NULL; } /** - * Start the first server found in the C<nbd_servers> list. + * Start the NBD server. * - * We previously tested all NBD servers (see C<test_nbd_servers>) so - * we only need to run the first server in the list. + * We previously tested all NBD servers (see C<test_nbd_servers>) and + * hopefully found one which will work. * * Returns the process ID (E<gt> 0) or C<0> if there is an error. */ pid_t start_nbd_server (int port, const char *device) { - size_t i; - - for (i = 0; i < NR_NBD_SERVERS; ++i) { - switch (nbd_servers[i]) { - case NO_SERVER: - /* ignore */ - break; + switch (use_server) { + case QEMU_NBD: + return start_qemu_nbd (port, device); - case QEMU_NBD: - return start_qemu_nbd (port, device); + case NBDKIT: + return start_nbdkit (port, device); - case NBDKIT: - return start_nbdkit (port, device); - - default: - abort (); - } + default: + abort (); } - - /* This should never happen because of the checks in - * test_nbd_servers. - */ - abort (); } /** -- 2.9.3
Richard W.M. Jones
2017-Feb-03 14:21 UTC
[Libguestfs] [PATCH 3/5] p2v: Move nbd_local_port to nbd.c.
In preparation for using socket activation, move the nbd_local_port global inside nbd.c and make it private. This is largely code motion. However I rearranged the order in which the ssh data connection and the NBD server are started up. Previously the ssh connection was made first, and the NBD server was then started. Now the NBD server starts first. This happens so that nbd.c can choose the local port, and when we implement socket activation it will allow nbd.c to open the socket and choose a free port too. --- p2v/conversion.c | 69 ++++++++++++++++++++++++++++++++------------------------ p2v/main.c | 13 ----------- p2v/nbd.c | 28 ++++++++++++++++++++--- p2v/p2v.h | 9 ++------ p2v/ssh.c | 6 ++--- 5 files changed, 68 insertions(+), 57 deletions(-) diff --git a/p2v/conversion.c b/p2v/conversion.c index 44a3c58..26d321f 100644 --- a/p2v/conversion.c +++ b/p2v/conversion.c @@ -86,7 +86,6 @@ xmlBufferDetach (xmlBufferPtr buf) struct data_conn { mexp_h *h; /* miniexpect handle to ssh */ pid_t nbd_pid; /* NBD server PID */ - int nbd_local_port; /* local NBD port on physical machine */ int nbd_remote_port; /* remote NBD port on conversion server */ }; @@ -231,33 +230,14 @@ start_conversion (struct config *config, for (i = 0; config->disks[i] != NULL; ++i) { data_conns[i].h = NULL; data_conns[i].nbd_pid = 0; - data_conns[i].nbd_local_port = -1; data_conns[i].nbd_remote_port = -1; } /* Start the data connections and NBD server processes, one per disk. */ for (i = 0; config->disks[i] != NULL; ++i) { + int nbd_local_port; CLEANUP_FREE char *device = NULL; - if (notify_ui) { - CLEANUP_FREE char *msg; - if (asprintf (&msg, - _("Opening data connection for %s ..."), - config->disks[i]) == -1) - error (EXIT_FAILURE, errno, "asprintf"); - notify_ui (NOTIFY_STATUS, msg); - } - - data_conns[i].h = open_data_connection (config, - &data_conns[i].nbd_local_port, - &data_conns[i].nbd_remote_port); - if (data_conns[i].h == NULL) { - const char *err = get_ssh_error (); - - set_conversion_error ("could not open data connection over SSH to the conversion server: %s", err); - goto out; - } - if (config->disks[i][0] == '/') { device = strdup (config->disks[i]); if (!device) { @@ -272,26 +252,55 @@ start_conversion (struct config *config, exit (EXIT_FAILURE); } -#if DEBUG_STDERR - fprintf (stderr, - "%s: data connection for %s: SSH remote port %d, local port %d\n", - getprogname (), device, - data_conns[i].nbd_remote_port, data_conns[i].nbd_local_port); -#endif + if (notify_ui) { + CLEANUP_FREE char *msg; + if (asprintf (&msg, + _("Starting local NBD server for %s ..."), + config->disks[i]) == -1) + error (EXIT_FAILURE, errno, "asprintf"); + notify_ui (NOTIFY_STATUS, msg); + } /* Start NBD server listening on the given port number. */ - data_conns[i].nbd_pid - start_nbd_server (data_conns[i].nbd_local_port, device); + data_conns[i].nbd_pid = start_nbd_server (&nbd_local_port, device); if (data_conns[i].nbd_pid == 0) { set_conversion_error ("NBD server error: %s", get_nbd_error ()); goto out; } /* Wait for NBD server to start up and listen. */ - if (wait_for_nbd_server_to_start (data_conns[i].nbd_local_port) == -1) { + if (wait_for_nbd_server_to_start (nbd_local_port) == -1) { set_conversion_error ("NBD server error: %s", get_nbd_error ()); goto out; } + + if (notify_ui) { + CLEANUP_FREE char *msg; + if (asprintf (&msg, + _("Opening data connection for %s ..."), + config->disks[i]) == -1) + error (EXIT_FAILURE, errno, "asprintf"); + notify_ui (NOTIFY_STATUS, msg); + } + + /* Open the SSH data connection, with reverse port forwarding + * back to the NBD server. + */ + data_conns[i].h = open_data_connection (config, nbd_local_port, + &data_conns[i].nbd_remote_port); + if (data_conns[i].h == NULL) { + const char *err = get_ssh_error (); + + set_conversion_error ("could not open data connection over SSH to the conversion server: %s", err); + goto out; + } + +#if DEBUG_STDERR + fprintf (stderr, + "%s: data connection for %s: SSH remote port %d, local port %d\n", + getprogname (), device, + data_conns[i].nbd_remote_port, nbd_local_port); +#endif } /* Create a remote directory name which will be used for libvirt diff --git a/p2v/main.c b/p2v/main.c index d1eece6..42d3bbb 100644 --- a/p2v/main.c +++ b/p2v/main.c @@ -51,7 +51,6 @@ char **all_disks; char **all_removable; char **all_interfaces; int is_iso_environment = 0; -int nbd_local_port; int feature_colours_option = 0; int force_colour = 0; static const char *test_disk = NULL; @@ -224,18 +223,6 @@ main (int argc, char *argv[]) usage (EXIT_FAILURE); } - if (is_iso_environment) - /* The p2v ISO should allow us to open up just about any port, so - * we can fix a port number in that case. Using a predictable - * port number in this case should avoid rare errors if the port - * colides with another (ie. it'll either always fail or never - * fail). - */ - nbd_local_port = 50123; - else - /* When testing on the local machine, choose a random port. */ - nbd_local_port = 50000 + (random () % 10000); - test_nbd_servers (); set_config_defaults (config); diff --git a/p2v/nbd.c b/p2v/nbd.c index ce90361..ed6f9a1 100644 --- a/p2v/nbd.c +++ b/p2v/nbd.c @@ -45,6 +45,11 @@ /* How long to wait for the NBD server to start (seconds). */ #define WAIT_NBD_TIMEOUT 10 +/* The local port that the NBD server listens on (incremented for + * each server which is started). + */ +static int nbd_local_port; + /* List of servers specified by the --nbd option. */ enum nbd_server { /* 0 is reserved for "end of list" */ @@ -150,6 +155,19 @@ test_nbd_servers (void) int r; const enum nbd_server *servers; + /* Initialize nbd_local_port. */ + if (is_iso_environment) + /* The p2v ISO should allow us to open up just about any port, so + * we can fix a port number in that case. Using a predictable + * port number in this case should avoid rare errors if the port + * colides with another (ie. it'll either always fail or never + * fail). + */ + nbd_local_port = 50123; + else + /* When testing on the local machine, choose a random port. */ + nbd_local_port = 50000 + (random () % 10000); + if (cmdline_servers != NULL) servers = cmdline_servers; else @@ -211,14 +229,18 @@ test_nbd_servers (void) * Returns the process ID (E<gt> 0) or C<0> if there is an error. */ pid_t -start_nbd_server (int port, const char *device) +start_nbd_server (int *port, const char *device) { + /* Choose a local port. */ + *port = nbd_local_port; + nbd_local_port++; + switch (use_server) { case QEMU_NBD: - return start_qemu_nbd (port, device); + return start_qemu_nbd (*port, device); case NBDKIT: - return start_nbdkit (port, device); + return start_nbdkit (*port, device); default: abort (); diff --git a/p2v/p2v.h b/p2v/p2v.h index 800862d..1cde808 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -52,11 +52,6 @@ extern char **all_interfaces; */ extern int is_iso_environment; -/* The local port that the NBD server listens on (incremented for - * each server which is started). - */ -extern int nbd_local_port; - /* True if virt-v2v supports the --colours option. */ extern int feature_colours_option; @@ -130,7 +125,7 @@ extern int inhibit_power_saving (void); /* ssh.c */ extern int test_connection (struct config *); -extern mexp_h *open_data_connection (struct config *, int *local_port, int *remote_port); +extern mexp_h *open_data_connection (struct config *, int local_port, int *remote_port); extern mexp_h *start_remote_connection (struct config *, const char *remote_dir); extern const char *get_ssh_error (void); extern int scp_file (struct config *config, const char *localfile, const char *remotefile); @@ -138,7 +133,7 @@ extern int scp_file (struct config *config, const char *localfile, const char *r /* nbd.c */ extern void set_nbd_option (const char *opt); extern void test_nbd_servers (void); -extern pid_t start_nbd_server (int nbd_local_port, const char *device); +extern pid_t start_nbd_server (int *nbd_local_port, const char *device); extern int wait_for_nbd_server_to_start (int nbd_local_port); const char *get_nbd_error (void); diff --git a/p2v/ssh.c b/p2v/ssh.c index 4c1d64c..d29aa40 100644 --- a/p2v/ssh.c +++ b/p2v/ssh.c @@ -1039,7 +1039,7 @@ compatible_version (const char *v2v_version) } mexp_h * -open_data_connection (struct config *config, int *local_port, int *remote_port) +open_data_connection (struct config *config, int local_port, int *remote_port) { mexp_h *h; char remote_arg[32]; @@ -1052,9 +1052,7 @@ open_data_connection (struct config *config, int *local_port, int *remote_port) const int ovecsize = 12; int ovector[ovecsize]; - snprintf (remote_arg, sizeof remote_arg, "0:localhost:%d", nbd_local_port); - *local_port = nbd_local_port; - nbd_local_port++; + snprintf (remote_arg, sizeof remote_arg, "0:localhost:%d", local_port); h = start_ssh (0, config, (char **) extra_args, 0); if (h == NULL) -- 2.9.3
Richard W.M. Jones
2017-Feb-03 14:21 UTC
[Libguestfs] [PATCH 4/5] p2v: Don't hard code "localhost" (NBD listening address) everywhere.
No functional change. --- p2v/conversion.c | 14 +++++++++----- p2v/nbd.c | 35 ++++++++++++++++++----------------- p2v/p2v.h | 6 +++--- p2v/ssh.c | 6 ++++-- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/p2v/conversion.c b/p2v/conversion.c index 26d321f..0c17ef2 100644 --- a/p2v/conversion.c +++ b/p2v/conversion.c @@ -235,6 +235,7 @@ start_conversion (struct config *config, /* Start the data connections and NBD server processes, one per disk. */ for (i = 0; config->disks[i] != NULL; ++i) { + const char *nbd_local_ipaddr; int nbd_local_port; CLEANUP_FREE char *device = NULL; @@ -262,14 +263,15 @@ start_conversion (struct config *config, } /* Start NBD server listening on the given port number. */ - data_conns[i].nbd_pid = start_nbd_server (&nbd_local_port, device); + data_conns[i].nbd_pid + start_nbd_server (&nbd_local_ipaddr, &nbd_local_port, device); if (data_conns[i].nbd_pid == 0) { set_conversion_error ("NBD server error: %s", get_nbd_error ()); goto out; } /* Wait for NBD server to start up and listen. */ - if (wait_for_nbd_server_to_start (nbd_local_port) == -1) { + if (wait_for_nbd_server_to_start (nbd_local_ipaddr, nbd_local_port) == -1) { set_conversion_error ("NBD server error: %s", get_nbd_error ()); goto out; } @@ -286,7 +288,8 @@ start_conversion (struct config *config, /* Open the SSH data connection, with reverse port forwarding * back to the NBD server. */ - data_conns[i].h = open_data_connection (config, nbd_local_port, + data_conns[i].h = open_data_connection (config, + nbd_local_ipaddr, nbd_local_port, &data_conns[i].nbd_remote_port); if (data_conns[i].h == NULL) { const char *err = get_ssh_error (); @@ -297,9 +300,10 @@ start_conversion (struct config *config, #if DEBUG_STDERR fprintf (stderr, - "%s: data connection for %s: SSH remote port %d, local port %d\n", + "%s: data connection for %s: SSH remote port %d, local port %s:%d\n", getprogname (), device, - data_conns[i].nbd_remote_port, nbd_local_port); + data_conns[i].nbd_remote_port, + nbd_local_ipaddr, nbd_local_port); #endif } diff --git a/p2v/nbd.c b/p2v/nbd.c index ed6f9a1..92f864a 100644 --- a/p2v/nbd.c +++ b/p2v/nbd.c @@ -69,8 +69,8 @@ static const enum nbd_server standard_servers[] */ static enum nbd_server use_server; -static pid_t start_qemu_nbd (int nbd_local_port, const char *device); -static pid_t start_nbdkit (int nbd_local_port, const char *device); +static pid_t start_qemu_nbd (const char *ipaddr, int nbd_local_port, const char *device); +static pid_t start_nbdkit (const char *ipaddr, int nbd_local_port, const char *device); static int connect_with_source_port (const char *hostname, int dest_port, int source_port); static int bind_source_port (int sockfd, int family, int source_port); @@ -229,7 +229,7 @@ test_nbd_servers (void) * Returns the process ID (E<gt> 0) or C<0> if there is an error. */ pid_t -start_nbd_server (int *port, const char *device) +start_nbd_server (const char **ipaddr, int *port, const char *device) { /* Choose a local port. */ *port = nbd_local_port; @@ -237,10 +237,12 @@ start_nbd_server (int *port, const char *device) switch (use_server) { case QEMU_NBD: - return start_qemu_nbd (*port, device); + *ipaddr = "localhost"; + return start_qemu_nbd (*ipaddr, *port, device); case NBDKIT: - return start_nbdkit (*port, device); + *ipaddr = "localhost"; + return start_nbdkit (*ipaddr, *port, device); default: abort (); @@ -253,7 +255,7 @@ start_nbd_server (int *port, const char *device) * Returns the process ID (E<gt> 0) or C<0> if there is an error. */ static pid_t -start_qemu_nbd (int port, const char *device) +start_qemu_nbd (const char *ipaddr, int port, const char *device) { pid_t pid; char port_str[64]; @@ -280,7 +282,7 @@ start_qemu_nbd (int port, const char *device) "-p", port_str, /* listening port */ "-t", /* persistent */ "-f", "raw", /* force raw format */ - "-b", "localhost", /* listen only on loopback interface */ + "-b", ipaddr, /* listen only on loopback interface */ "--cache=unsafe", /* use unsafe caching for speed */ device, /* a device like /dev/sda */ NULL); @@ -299,7 +301,7 @@ start_qemu_nbd (int port, const char *device) * Returns the process ID (E<gt> 0) or C<0> if there is an error. */ static pid_t -start_nbdkit (int port, const char *device) +start_nbdkit (const char *ipaddr, int port, const char *device) { pid_t pid; char port_str[64]; @@ -328,7 +330,7 @@ start_nbdkit (int port, const char *device) "nbdkit", "-r", /* readonly (vital!) */ "-p", port_str, /* listening port */ - "-i", "localhost", /* listen only on loopback interface */ + "-i", ipaddr , /* listen only on loopback interface */ "-f", /* don't fork */ "file", /* file plugin */ file_str, /* a device like file=/dev/sda */ @@ -346,7 +348,7 @@ start_nbdkit (int port, const char *device) * connections. */ int -wait_for_nbd_server_to_start (int nbd_local_port) +wait_for_nbd_server_to_start (const char *ipaddr, int port) { int sockfd = -1; int result = -1; @@ -368,15 +370,14 @@ wait_for_nbd_server_to_start (int nbd_local_port) } /* Source port for probing NBD server should be one greater than - * nbd_local_port. It's not guaranteed to always bind to this - * port, but it will hint the kernel to start there and try - * incrementally higher ports if needed. This avoids the case - * where the kernel selects nbd_local_port as our source port, and - * we immediately connect to ourself. See: + * port. It's not guaranteed to always bind to this port, but it + * will hint the kernel to start there and try incrementally + * higher ports if needed. This avoids the case where the kernel + * selects port as our source port, and we immediately connect to + * ourself. See: * https://bugzilla.redhat.com/show_bug.cgi?id=1167774#c9 */ - sockfd = connect_with_source_port ("localhost", nbd_local_port, - nbd_local_port+1); + sockfd = connect_with_source_port (ipaddr, port, port+1); if (sockfd >= 0) break; diff --git a/p2v/p2v.h b/p2v/p2v.h index 1cde808..5223aa2 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -125,7 +125,7 @@ extern int inhibit_power_saving (void); /* ssh.c */ extern int test_connection (struct config *); -extern mexp_h *open_data_connection (struct config *, int local_port, int *remote_port); +extern mexp_h *open_data_connection (struct config *, const char *local_ipaddr, int local_port, int *remote_port); extern mexp_h *start_remote_connection (struct config *, const char *remote_dir); extern const char *get_ssh_error (void); extern int scp_file (struct config *config, const char *localfile, const char *remotefile); @@ -133,8 +133,8 @@ extern int scp_file (struct config *config, const char *localfile, const char *r /* nbd.c */ extern void set_nbd_option (const char *opt); extern void test_nbd_servers (void); -extern pid_t start_nbd_server (int *nbd_local_port, const char *device); -extern int wait_for_nbd_server_to_start (int nbd_local_port); +extern pid_t start_nbd_server (const char **ipaddr, int *port, const char *device); +extern int wait_for_nbd_server_to_start (const char *ipaddr, int port); const char *get_nbd_error (void); /* utils.c */ diff --git a/p2v/ssh.c b/p2v/ssh.c index d29aa40..20acb03 100644 --- a/p2v/ssh.c +++ b/p2v/ssh.c @@ -1039,7 +1039,9 @@ compatible_version (const char *v2v_version) } mexp_h * -open_data_connection (struct config *config, int local_port, int *remote_port) +open_data_connection (struct config *config, + const char *local_ipaddr, int local_port, + int *remote_port) { mexp_h *h; char remote_arg[32]; @@ -1052,7 +1054,7 @@ open_data_connection (struct config *config, int local_port, int *remote_port) const int ovecsize = 12; int ovector[ovecsize]; - snprintf (remote_arg, sizeof remote_arg, "0:localhost:%d", local_port); + snprintf (remote_arg, sizeof remote_arg, "0:%s:%d", local_ipaddr, local_port); h = start_ssh (0, config, (char **) extra_args, 0); if (h == NULL) -- 2.9.3
Richard W.M. Jones
2017-Feb-03 14:21 UTC
[Libguestfs] [PATCH 5/5] p2v: Use socket activation with qemu-nbd and nbdkit.
If supported, use socket activation to pass a pre-opened listening socket to the NBD server. This means we can guarantee to choose an unused port even if there are multiple instances of virt-p2v running (ie. when testing) or if there are unexpected services running on the same machine. This change applies to both NBD servers, since both now (or will shortly) support this feature. --- p2v/dependencies.m4 | 4 + p2v/nbd.c | 381 +++++++++++++++++++++++++++++++++++++++----- p2v/test-virt-p2v-nbdkit.sh | 2 +- p2v/virt-p2v.pod | 18 ++- 4 files changed, 361 insertions(+), 44 deletions(-) diff --git a/p2v/dependencies.m4 b/p2v/dependencies.m4 index 04e80b7..bd62d34 100644 --- a/p2v/dependencies.m4 +++ b/p2v/dependencies.m4 @@ -32,6 +32,7 @@ ifelse(REDHAT,1, curl ethtool util-linux + which xterm pciutils lsscsi @@ -82,6 +83,7 @@ ifelse(DEBIAN,1, curl ethtool util-linux + debianutils xterm pciutils lsscsi @@ -113,6 +115,7 @@ ifelse(ARCHLINUX,1, curl ethtool util-linux + which xterm pciutils lsscsi @@ -145,6 +148,7 @@ ifelse(SUSE,1, curl ethtool util-linux + dnl /usr/bin/which is in util-linux on SUSE xterm pciutils lsscsi diff --git a/p2v/nbd.c b/p2v/nbd.c index 92f864a..c596d8e 100644 --- a/p2v/nbd.c +++ b/p2v/nbd.c @@ -54,23 +54,46 @@ static int nbd_local_port; enum nbd_server { /* 0 is reserved for "end of list" */ QEMU_NBD = 1, - NBDKIT = 2, + QEMU_NBD_NO_SA = 2, + NBDKIT = 3, + NBDKIT_NO_SA = 4, }; static enum nbd_server *cmdline_servers = NULL; +static const char * +nbd_server_string (enum nbd_server s) +{ + const char *ret = NULL; + + switch (s) { + case QEMU_NBD: ret = "qemu-nbd"; break; + case QEMU_NBD_NO_SA: ret = "qemu-nbd-no-sa"; break; + case NBDKIT: ret = "nbdkit"; break; + case NBDKIT_NO_SA: ret = "nbdkit-no-sa"; break; + } + + if (ret == NULL) + abort (); + + return ret; +} + /* If no --nbd option is passed, we use this standard list instead. * Must match the documentation in virt-p2v(1). */ static const enum nbd_server standard_servers[] - { QEMU_NBD, NBDKIT, 0 }; + { QEMU_NBD, QEMU_NBD_NO_SA, NBDKIT, NBDKIT_NO_SA, 0 }; /* After testing the list of servers passed by the user, this is * server we decide to use. */ static enum nbd_server use_server; -static pid_t start_qemu_nbd (const char *ipaddr, int nbd_local_port, const char *device); -static pid_t start_nbdkit (const char *ipaddr, int nbd_local_port, const char *device); +static pid_t start_qemu_nbd (const char *device, const char *ipaddr, int port, int *fds, size_t nr_fds); +static pid_t start_nbdkit (const char *device, const char *ipaddr, int port, int *fds, size_t nr_fds); +static int get_local_port (void); +static int open_listening_socket (const char *ipaddr, int **fds, size_t *nr_fds); +static int bind_tcpip_socket (const char *ipaddr, const char *port, int **fds, size_t *nr_fds); static int connect_with_source_port (const char *hostname, int dest_port, int source_port); static int bind_source_port (int sockfd, int family, int source_port); @@ -132,8 +155,12 @@ set_nbd_option (const char *opt) for (i = 0; strs[i] != NULL; ++i) { if (STREQ (strs[i], "qemu-nbd") || STREQ (strs[i], "qemu")) cmdline_servers[i] = QEMU_NBD; + else if (STREQ (strs[i], "qemu-nbd-no-sa") || STREQ (strs[i], "qemu-no-sa")) + cmdline_servers[i] = QEMU_NBD_NO_SA; else if (STREQ (strs[i], "nbdkit")) cmdline_servers[i] = NBDKIT; + else if (STREQ (strs[i], "nbdkit-no-sa")) + cmdline_servers[i] = NBDKIT_NO_SA; else error (EXIT_FAILURE, 0, _("--nbd: unknown server: %s"), strs[i]); } @@ -176,8 +203,25 @@ test_nbd_servers (void) use_server = 0; for (i = 0; servers[i] != 0; ++i) { +#if DEBUG_STDERR + fprintf (stderr, "checking for %s ...\n", nbd_server_string (servers[i])); +#endif + switch (servers[i]) { - case QEMU_NBD: + case QEMU_NBD: /* with socket activation */ + r = system ("qemu-nbd --version" +#ifndef DEBUG_STDERR + " >/dev/null 2>&1" +#endif + " && grep -sq LISTEN_PID `which qemu-nbd`" + ); + if (r == 0) { + use_server = servers[i]; + goto finish; + } + break; + + case QEMU_NBD_NO_SA: r = system ("qemu-nbd --version" #ifndef DEBUG_STDERR " >/dev/null 2>&1" @@ -189,7 +233,20 @@ test_nbd_servers (void) } break; - case NBDKIT: + case NBDKIT: /* with socket activation */ + r = system ("nbdkit file --version" +#ifndef DEBUG_STDERR + " >/dev/null 2>&1" +#endif + " && grep -sq LISTEN_PID `which nbdkit`" + ); + if (r == 0) { + use_server = servers[i]; + goto finish; + } + break; + + case NBDKIT_NO_SA: r = system ("nbdkit file --version" #ifndef DEBUG_STDERR " >/dev/null 2>&1" @@ -218,6 +275,10 @@ test_nbd_servers (void) /* Release memory used by the --nbd option. */ free (cmdline_servers); cmdline_servers = NULL; + +#if DEBUG_STDERR + fprintf (stderr, "picked %s\n", nbd_server_string (use_server)); +#endif } /** @@ -231,31 +292,95 @@ test_nbd_servers (void) pid_t start_nbd_server (const char **ipaddr, int *port, const char *device) { - /* Choose a local port. */ - *port = nbd_local_port; - nbd_local_port++; + int *fds = NULL; + size_t i, nr_fds; + pid_t pid; switch (use_server) { - case QEMU_NBD: + case QEMU_NBD: /* qemu-nbd with socket activation */ + /* Ideally we would bind this socket to "localhost", but that + * requires two listening FDs, and qemu-nbd currently cannot + * support socket activation with two FDs. So we only bind to the + * IPv4 address. + */ + *ipaddr = "127.0.0.1"; + *port = open_listening_socket (*ipaddr, &fds, &nr_fds); + if (*port == -1) return -1; + pid = start_qemu_nbd (device, *ipaddr, *port, fds, nr_fds); + for (i = 0; i < nr_fds; ++i) + close (fds[i]); + free (fds); + return pid; + + case QEMU_NBD_NO_SA: /* qemu-nbd without socket activation */ *ipaddr = "localhost"; - return start_qemu_nbd (*ipaddr, *port, device); + *port = get_local_port (); + if (*port == -1) return -1; + return start_qemu_nbd (device, *ipaddr, *port, NULL, 0); - case NBDKIT: + case NBDKIT: /* nbdkit with socket activation */ + *ipaddr = "localhost"; + *port = open_listening_socket (*ipaddr, &fds, &nr_fds); + if (*port == -1) return -1; + pid = start_nbdkit (device, *ipaddr, *port, fds, nr_fds); + for (i = 0; i < nr_fds; ++i) + close (fds[i]); + free (fds); + return pid; + + case NBDKIT_NO_SA: /* nbdkit without socket activation */ *ipaddr = "localhost"; - return start_nbdkit (*ipaddr, *port, device); + *port = get_local_port (); + if (*port == -1) return -1; + return start_nbdkit (device, *ipaddr, *port, NULL, 0); + } - default: - abort (); + abort (); +} + +#define FIRST_SOCKET_ACTIVATION_FD 3 + +/** + * Set up file descriptors and environment variables for + * socket activation. + * + * Note this function runs in the child between fork and exec. + */ +static inline void +socket_activation (int *fds, size_t nr_fds) +{ + size_t i; + char nr_fds_str[16]; + char pid_str[16]; + + if (fds == NULL) return; + + for (i = 0; i < nr_fds; ++i) { + int fd = FIRST_SOCKET_ACTIVATION_FD + i; + if (fds[i] != fd) { + dup2 (fds[i], fd); + close (fds[i]); + } } + + snprintf (nr_fds_str, sizeof nr_fds_str, "%zu", nr_fds); + setenv ("LISTEN_FDS", nr_fds_str, 1); + snprintf (pid_str, sizeof pid_str, "%d", (int) getpid ()); + setenv ("LISTEN_PID", pid_str, 1); } /** * Start a local L<qemu-nbd(1)> process. * + * If we are using socket activation, C<fds> and C<nr_fds> will + * contain the locally pre-opened file descriptors for this. + * Otherwise if C<fds == NULL> we pass the port number. + * * Returns the process ID (E<gt> 0) or C<0> if there is an error. */ static pid_t -start_qemu_nbd (const char *ipaddr, int port, const char *device) +start_qemu_nbd (const char *device, + const char *ipaddr, int port, int *fds, size_t nr_fds) { pid_t pid; char port_str[64]; @@ -276,18 +401,34 @@ start_qemu_nbd (const char *ipaddr, int port, const char *device) close (0); open ("/dev/null", O_RDONLY); - execlp ("qemu-nbd", - "qemu-nbd", - "-r", /* readonly (vital!) */ - "-p", port_str, /* listening port */ - "-t", /* persistent */ - "-f", "raw", /* force raw format */ - "-b", ipaddr, /* listen only on loopback interface */ - "--cache=unsafe", /* use unsafe caching for speed */ - device, /* a device like /dev/sda */ - NULL); - perror ("qemu-nbd"); - _exit (EXIT_FAILURE); + if (fds == NULL) { /* without socket activation */ + execlp ("qemu-nbd", + "qemu-nbd", + "-r", /* readonly (vital!) */ + "-p", port_str, /* listening port */ + "-t", /* persistent */ + "-f", "raw", /* force raw format */ + "-b", ipaddr, /* listen only on loopback interface */ + "--cache=unsafe", /* use unsafe caching for speed */ + device, /* a device like /dev/sda */ + NULL); + perror ("qemu-nbd"); + _exit (EXIT_FAILURE); + } + else { /* socket activation */ + socket_activation (fds, nr_fds); + + execlp ("qemu-nbd", + "qemu-nbd", + "-r", /* readonly (vital!) */ + "-t", /* persistent */ + "-f", "raw", /* force raw format */ + "--cache=unsafe", /* use unsafe caching for speed */ + device, /* a device like /dev/sda */ + NULL); + perror ("qemu-nbd"); + _exit (EXIT_FAILURE); + } } /* Parent. */ @@ -298,10 +439,15 @@ start_qemu_nbd (const char *ipaddr, int port, const char *device) * Start a local L<nbdkit(1)> process using the * L<nbdkit-file-plugin(1)>. * + * If we are using socket activation, C<fds> and C<nr_fds> will + * contain the locally pre-opened file descriptors for this. + * Otherwise if C<fds == NULL> we pass the port number. + * * Returns the process ID (E<gt> 0) or C<0> if there is an error. */ static pid_t -start_nbdkit (const char *ipaddr, int port, const char *device) +start_nbdkit (const char *device, + const char *ipaddr, int port, int *fds, size_t nr_fds) { pid_t pid; char port_str[64]; @@ -326,17 +472,32 @@ start_nbdkit (const char *ipaddr, int port, const char *device) close (0); open ("/dev/null", O_RDONLY); - execlp ("nbdkit", - "nbdkit", - "-r", /* readonly (vital!) */ - "-p", port_str, /* listening port */ - "-i", ipaddr , /* listen only on loopback interface */ - "-f", /* don't fork */ - "file", /* file plugin */ - file_str, /* a device like file=/dev/sda */ - NULL); - perror ("nbdkit"); - _exit (EXIT_FAILURE); + if (fds == NULL) { /* without socket activation */ + execlp ("nbdkit", + "nbdkit", + "-r", /* readonly (vital!) */ + "-p", port_str, /* listening port */ + "-i", ipaddr, /* listen only on loopback interface */ + "-f", /* don't fork */ + "file", /* file plugin */ + file_str, /* a device like file=/dev/sda */ + NULL); + perror ("nbdkit"); + _exit (EXIT_FAILURE); + } + else { /* socket activation */ + socket_activation (fds, nr_fds); + + execlp ("nbdkit", + "nbdkit", + "-r", /* readonly (vital!) */ + "-f", /* don't fork */ + "file", /* file plugin */ + file_str, /* a device like file=/dev/sda */ + NULL); + perror ("nbdkit"); + _exit (EXIT_FAILURE); + } } /* Parent. */ @@ -344,6 +505,146 @@ start_nbdkit (const char *ipaddr, int port, const char *device) } /** + * This is used when we are starting an NBD server that does not + * support socket activation. We have to pass the '-p' option to + * the NBD server, but there's no good way to choose a free port, + * so we have to just guess. + * + * Returns the port number on success or C<-1> on error. + */ +static int +get_local_port (void) +{ + int port = nbd_local_port; + nbd_local_port++; + return port; +} + +/** + * This is used when we are starting an NBD server which supports + * socket activation. We can open a listening socket on an unused + * local port and return it. + * + * Returns the port number on success or C<-1> on error. + * + * The file descriptor(s) bound are returned in the array *fds, *nr_fds. + * The caller must free the array. + */ +static int +open_listening_socket (const char *ipaddr, int **fds, size_t *nr_fds) +{ + int port; + char port_str[16]; + + /* This just ensures we don't try the port we previously bound to. */ + port = nbd_local_port; + + /* Search for a free port. */ + for (; port < 60000; ++port) { + snprintf (port_str, sizeof port_str, "%d", port); + if (bind_tcpip_socket (ipaddr, port_str, fds, nr_fds) == 0) { + /* See above. */ + nbd_local_port = port + 1; + return port; + } + } + + set_nbd_error ("cannot find a free local port"); + return -1; +} + +static int +bind_tcpip_socket (const char *ipaddr, const char *port, + int **fds_rtn, size_t *nr_fds_rtn) +{ + struct addrinfo *ai = NULL; + struct addrinfo hints; + struct addrinfo *a; + int err; + int *fds = NULL; + size_t nr_fds; + int addr_in_use = 0; + + memset (&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + + err = getaddrinfo (ipaddr, port, &hints, &ai); + if (err != 0) { +#if DEBUG_STDERR + fprintf (stderr, "%s: getaddrinfo: %s: %s: %s", + getprogname (), ipaddr ? ipaddr : "<any>", port, + gai_strerror (err)); +#endif + return -1; + } + + nr_fds = 0; + + for (a = ai; a != NULL; a = a->ai_next) { + int sock, opt; + + sock = socket (a->ai_family, a->ai_socktype, a->ai_protocol); + if (sock == -1) + error (EXIT_FAILURE, errno, "socket"); + + opt = 1; + if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) + perror ("setsockopt: SO_REUSEADDR"); + +#ifdef IPV6_V6ONLY + if (a->ai_family == PF_INET6) { + if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof opt) == -1) + perror ("setsockopt: IPv6 only"); + } +#endif + + if (bind (sock, a->ai_addr, a->ai_addrlen) == -1) { + if (errno == EADDRINUSE) { + addr_in_use = 1; + close (sock); + continue; + } + perror ("bind"); + close (sock); + continue; + } + + if (listen (sock, SOMAXCONN) == -1) { + perror ("listen"); + close (sock); + continue; + } + + nr_fds++; + fds = realloc (fds, sizeof (int) * nr_fds); + if (!fds) + error (EXIT_FAILURE, errno, "realloc"); + fds[nr_fds-1] = sock; + } + + freeaddrinfo (ai); + + if (nr_fds == 0 && addr_in_use) { +#if DEBUG_STDERR + fprintf (stderr, "%s: unable to bind to %s:%s: %s\n", + getprogname (), ipaddr ? ipaddr : "<any>", port, + strerror (EADDRINUSE)); +#endif + return -1; + } + +#if DEBUG_STDERR + fprintf (stderr, "%s: bound to IP address %s:%s (%zu socket(s))\n", + getprogname (), ipaddr ? ipaddr : "<any>", port, nr_fds); +#endif + + *fds_rtn = fds; + *nr_fds_rtn = nr_fds; + return 0; +} + +/** * Wait for a local NBD server to start and be listening for * connections. */ diff --git a/p2v/test-virt-p2v-nbdkit.sh b/p2v/test-virt-p2v-nbdkit.sh index 07150a6..7044434 100755 --- a/p2v/test-virt-p2v-nbdkit.sh +++ b/p2v/test-virt-p2v-nbdkit.sh @@ -70,7 +70,7 @@ export PATH=$d:$PATH cmdline="p2v.server=localhost p2v.name=windows p2v.disks=$f1,$f2 p2v.o=local p2v.os=$(pwd)/$d p2v.network=em1:wired,other p2v.post=" # Only use nbdkit, disable qemu-nbd. -virt-p2v --cmdline="$cmdline" --nbd=nbdkit +virt-p2v --cmdline="$cmdline" --nbd=nbdkit,nbdkit-no-sa # Test the libvirt XML metadata and a disk was created. test -f $d/windows.xml diff --git a/p2v/virt-p2v.pod b/p2v/virt-p2v.pod index e04c1f1..4739835 100644 --- a/p2v/virt-p2v.pod +++ b/p2v/virt-p2v.pod @@ -599,9 +599,9 @@ features such as the Reboot button. =item B<--nbd=server[,server...]> -Select which NBD server is used. By default the following -servers are checked and the first one found is used: -I<--nbd=qemu-nbd,nbdkit> +Select which NBD server is used. By default the following servers are +checked and the first one found is used: +I<--nbd=qemu-nbd,qemu-nbd-no-sa,nbdkit,nbdkit-no-sa> =over 4 @@ -609,12 +609,24 @@ I<--nbd=qemu-nbd,nbdkit> Use qemu-nbd. +=item B<qemu-nbd-no-sa> + +Use qemu-nbd, but disable socket activation. + =item B<nbdkit> Use nbdkit with the file plugin (see: L<nbdkit-file-plugin(1)>). +=item B<nbdkit-no-sa> + +Use nbdkit, but disable socket activation + =back +The C<*-no-sa> variants allow virt-p2v to fall back to older versions +of qemu-nbd and nbdkit which did not support +L<socket activation|http://0pointer.de/blog/projects/socket-activation.html>. + =item B<--test-disk=/PATH/TO/DISK.IMG> For testing or debugging purposes, replace F</dev/sda> with a local -- 2.9.3
Possibly Parallel Threads
- [PATCH 0/3] p2v: Allow nbdkit as an alternative NBD server.
- [PATCH 0/2] Remove virt-p2v from libguestfs
- [PATCH v2 0/1] p2v: avoid connecting to ourself while probing qemu-nbd
- [PATCH 0/4] p2v: Send ^C to remote end to cancel the conversion.
- [PATCH] p2v: wait for qemu-nbd before starting conversion (RHBZ#1167774)