From: Richard Jones <rjones(a)trick.home.annexia.org>
This reimplements parts of commit da0a4f8d1f6ddd302ceba028d87c6e009589e503
in a different, but compatible way.
We pass guestfs_vmchannel=tcp:<ip>:<port> 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:<port> 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 | 147 +++++++++++++++++++++++++++++++++++++++++++---------
src/guestfs.c | 4 ++
2 files changed, 125 insertions(+), 26 deletions(-)
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index a38452c..ce14dbf 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -68,8 +68,9 @@ int sysroot_len = 8;
int
main (int argc, char *argv[])
{
- static const char *options = "f?";
+ static const char *options = "fc:?";
static const struct option long_options[] = {
+ { "channel", 0, 0, 'c' },
{ "foreground", 0, 0, 'f' },
{ "help", 0, 0, '?' },
{ 0, 0, 0, 0 }
@@ -77,6 +78,7 @@ main (int argc, char *argv[])
int c;
int dont_fork = 0;
char *cmdline;
+ char *vmchannel = NULL;
#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
/*
http://udrepper.livejournal.com/20948.html */
@@ -96,6 +98,10 @@ main (int argc, char *argv[])
if (c == -1) break;
switch (c) {
+ case 'c':
+ vmchannel = optarg;
+ break;
+
case 'f':
dont_fork = 1;
break;
@@ -148,38 +154,127 @@ 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;
+ int 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) {
+ 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:<ip>:<port>\": %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:<ip>:<port>\"\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);
}
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.6.2.5