From 8b217a87bf9175e7e02a5913e5617e0d12dfd09c Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 21 Sep 2009 13:03:27 +0100 Subject: [PATCH] Flexible guestfs_vmchannel parameter for future appliances. This reimplements parts of commit da0a4f8d1f6ddd302ceba028d87c6e009589e503 in a different, but compatible way. We pass guestfs_vmchannel=tcp:: on the command line. This is intended to be used as follows (now and in future versions): tcp:10.0.2.4:6666 for guestfwd vmchannel tcp:10.0.2.2: for future "no vmchannel" implementation /dev/vcon4 for future virtio-console vmchannel* It also accepts the old-style guestfs=10.0.2.4:6666 parameter which is sent by older libraries, and turns this transparently into the correct format above. If no guestfs_vmchannel is passed, then this defaults to the guestfwd vmchannel which older libraries would expect. * Maybe this last one should be dev:/dev/vcon4 or file:/dev/vcon4, but we don't need to decide that now. --- daemon/guestfsd.c | 171 +++++++++++++++++++++++++++++++++++++++++++----------- src/guestfs.c | 4 ++ 2 files changed, 141 insertions(+), 34 deletions(-) diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index b4fb15e..e3b8883 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -40,7 +40,6 @@ #include "daemon.h" -static void usage (void); static char *read_cmdline (void); /* Also in guestfs.c */ @@ -65,18 +64,28 @@ static int print_arginfo (const struct printf_info *info, size_t n, int *argtype const char *sysroot = "/sysroot"; /* No trailing slash. */ int sysroot_len = 8; +static void +usage (void) +{ + fprintf (stderr, + "guestfsd [-f|--foreground] [-c|--channel vmchannel] [-v|--verbose]\n"); +} + int main (int argc, char *argv[]) { - static const char *options = "f?"; + static const char *options = "fc:v?"; static const struct option long_options[] = { + { "channel", required_argument, 0, 'c' }, { "foreground", 0, 0, 'f' }, { "help", 0, 0, '?' }, + { "verbose", 0, 0, 'v' }, { 0, 0, 0, 0 } }; int c; int dont_fork = 0; char *cmdline; + char *vmchannel = NULL; #ifdef HAVE_REGISTER_PRINTF_SPECIFIER /* http://udrepper.livejournal.com/20948.html */ @@ -96,10 +105,18 @@ main (int argc, char *argv[]) if (c == -1) break; switch (c) { + case 'c': + vmchannel = optarg; + break; + case 'f': dont_fork = 1; break; + case 'v': + verbose = 1; + break; + case '?': usage (); exit (0); @@ -118,7 +135,8 @@ main (int argc, char *argv[]) cmdline = read_cmdline (); /* Set the verbose flag. */ - verbose = cmdline && strstr (cmdline, "guestfs_verbose=1") != NULL; + verbose = verbose || + (cmdline && strstr (cmdline, "guestfs_verbose=1") != NULL); if (verbose) printf ("verbose daemon enabled\n"); @@ -148,38 +166,129 @@ main (int argc, char *argv[]) /* We document that umask defaults to 022 (it should be this anyway). */ umask (022); - /* Resolve the hostname. */ - struct addrinfo *res, *rr; - struct addrinfo hints; - int r; - memset (&hints, 0, sizeof hints); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - r = getaddrinfo (GUESTFWD_ADDR, GUESTFWD_PORT, &hints, &res); - if (r != 0) { - fprintf (stderr, "%s:%s: %s\n", - GUESTFWD_ADDR, GUESTFWD_PORT, gai_strerror (r)); - exit (1); + /* Get the vmchannel string. + * + * Sources: + * --channel/-c option on the command line + * guestfs_vmchannel=... from the kernel command line + * guestfs=... from the kernel command line + * built-in default + * + * At the moment we expect this to contain "tcp:ip:port" but in + * future it might contain a device name, eg. "/dev/vcon4" for + * virtio-console vmchannel. + */ + if (vmchannel == NULL && cmdline) { + char *p; + size_t len; + + p = strstr (cmdline, "guestfs_vmchannel="); + if (p) { + len = strcspn (p + 18, " \t\n"); + vmchannel = strndup (p + 18, len); + if (!vmchannel) { + perror ("strndup"); + exit (1); + } + } + + /* Old libraries passed guestfs=host:port. Rewrite it as tcp:host:port. */ + if (vmchannel == NULL) { + /* We will rewrite it part of the "guestfs=" string with + * "tcp:" hence p + 4 below. */ + p = strstr (cmdline, "guestfs="); + if (p) { + len = strcspn (p + 4, " \t\n"); + vmchannel = strndup (p + 4, len); + if (!vmchannel) { + perror ("strndup"); + exit (1); + } + memcpy (vmchannel, "tcp:", 4); + } + } } - /* Connect to the given TCP socket. */ + /* Default vmchannel. */ + if (vmchannel == NULL) { + vmchannel = strdup ("tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT); + if (!vmchannel) { + perror ("strdup"); + exit (1); + } + } + + if (verbose) + printf ("vmchannel: %s\n", vmchannel); + + /* Connect to vmchannel. */ int sock = -1; - for (rr = res; rr != NULL; rr = rr->ai_next) { - sock = socket (rr->ai_family, rr->ai_socktype, rr->ai_protocol); - if (sock != -1) { - if (connect (sock, rr->ai_addr, rr->ai_addrlen) == 0) - break; - perror ("connect"); - - close (sock); - sock = -1; + + if (strncmp (vmchannel, "tcp:", 4) == 0) { + /* Resolve the hostname. */ + struct addrinfo *res, *rr; + struct addrinfo hints; + int r; + char *host, *port; + + host = vmchannel+4; + port = strchr (host, ':'); + if (port) { + port[0] = '\0'; + port++; + } else { + fprintf (stderr, "vmchannel: expecting \"tcp::\": %s\n", + vmchannel); + exit (1); } + + memset (&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + r = getaddrinfo (host, port, &hints, &res); + if (r != 0) { + fprintf (stderr, "%s:%s: %s\n", + host, port, gai_strerror (r)); + exit (1); + } + + /* Connect to the given TCP socket. */ + for (rr = res; rr != NULL; rr = rr->ai_next) { + sock = socket (rr->ai_family, rr->ai_socktype, rr->ai_protocol); + if (sock != -1) { + if (connect (sock, rr->ai_addr, rr->ai_addrlen) == 0) + break; + perror ("connect"); + + close (sock); + sock = -1; + } + } + freeaddrinfo (res); + } else { + fprintf (stderr, + "unknown vmchannel connection type: %s\n" + "expecting \"tcp::\"\n", + vmchannel); + exit (1); } - freeaddrinfo (res); if (sock == -1) { - fprintf (stderr, "connection to %s:%s failed\n", - GUESTFWD_ADDR, GUESTFWD_PORT); + fprintf (stderr, + "\n" + "Failed to connect to any vmchannel implementation.\n" + "vmchannel: %s\n" + "\n" + "This is a fatal error and the appliance will now exit.\n" + "\n" + "Usually this error is caused by either QEMU or the appliance\n" + "kernel not supporting the vmchannel method that the\n" + "libguestfs library chose to use. Please run\n" + "'libguestfs-test-tool' and provide the complete, unedited\n" + "output to the libguestfs developers, either in a bug report\n" + "or on the libguestfs redhat com mailing list.\n" + "\n", + vmchannel); exit (1); } @@ -323,12 +432,6 @@ xread (int sock, void *v_buf, size_t len) return 0; } -static void -usage (void) -{ - fprintf (stderr, "guestfsd [-f]\n"); -} - int add_string (char ***argv, int *size, int *alloc, const char *str) { diff --git a/src/guestfs.c b/src/guestfs.c index 55732f9..ec7473e 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -984,6 +984,7 @@ guestfs__launch (guestfs_h *g) if (r == 0) { /* Child (qemu). */ char buf[256]; + const char *vmchannel = NULL; /* Set up the full command line. Do this in the subprocess so we * don't need to worry about cleaning up. @@ -1045,6 +1046,7 @@ guestfs__launch (guestfs_h *g) } add_cmdline (g, "-net"); add_cmdline (g, "nic,model=" NET_IF ",vlan=0"); + vmchannel = "guestfs_vmchannel=tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT " "; #define LINUX_CMDLINE \ "panic=1 " /* force kernel to panic if daemon exits */ \ @@ -1058,9 +1060,11 @@ guestfs__launch (guestfs_h *g) snprintf (buf, sizeof buf, LINUX_CMDLINE "%s" /* (selinux) */ + "%s" /* (vmchannel) */ "%s" /* (verbose) */ "%s", /* (append) */ g->selinux ? "selinux=1 enforcing=0 " : "selinux=0 ", + vmchannel ? vmchannel : "", g->verbose ? "guestfs_verbose=1 " : "", g->append ? g->append : ""); -- 1.8.3.1