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