#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include <ctype.h>
#include <signal.h>
#include <printf.h>
+#include "c-ctype.h"
#include "daemon.h"
-static void usage (void);
+static char *read_cmdline (void);
/* Also in guestfs.c */
-#define VMCHANNEL_PORT "6666"
-#define VMCHANNEL_ADDR "10.0.2.4"
+#define GUESTFWD_ADDR "10.0.2.4"
+#define GUESTFWD_PORT "6666"
int verbose = 0;
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 = "fh:p:?";
+ static const char *options = "fc:v?";
static const struct option long_options[] = {
+ { "channel", required_argument, 0, 'c' },
{ "foreground", 0, 0, 'f' },
{ "help", 0, 0, '?' },
- { "host", 1, 0, 'h' },
- { "port", 1, 0, 'p' },
+ { "verbose", 0, 0, 'v' },
{ 0, 0, 0, 0 }
};
- int c, n, r;
+ int c;
int dont_fork = 0;
- const char *host = NULL;
- const char *port = NULL;
- FILE *fp;
- char buf[4096];
- char *p, *p2;
- int sock;
- struct addrinfo *res, *rr;
- struct addrinfo hints;
- XDR xdr;
- uint32_t len;
- struct sigaction sa;
+ char *cmdline;
+ char *vmchannel = NULL;
#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
/* http://udrepper.livejournal.com/20948.html */
if (c == -1) break;
switch (c) {
- case 'f':
- dont_fork = 1;
+ case 'c':
+ vmchannel = optarg;
break;
- case 'h':
- host = optarg;
+ case 'f':
+ dont_fork = 1;
break;
- case 'p':
- port = optarg;
+ case 'v':
+ verbose = 1;
break;
case '?':
exit (1);
}
- /* If host and port aren't set yet, try /proc/cmdline. */
- if (!host || !port) {
- fp = fopen ("/proc/cmdline", "r");
- if (fp == NULL) {
- perror ("/proc/cmdline");
- goto next;
- }
- n = fread (buf, 1, sizeof buf - 1, fp);
- fclose (fp);
- buf[n] = '\0';
-
- /* Set the verbose flag. Not quite right because this will only
- * set the flag if host and port aren't set on the command line.
- * Don't worry about this for now. (XXX)
- */
- verbose = strstr (buf, "guestfs_verbose=1") != NULL;
- if (verbose)
- printf ("verbose daemon enabled\n");
+ cmdline = read_cmdline ();
- p = strstr (buf, "guestfs=");
+ /* Set the verbose flag. */
+ verbose = verbose ||
+ (cmdline && strstr (cmdline, "guestfs_verbose=1") != NULL);
+ if (verbose)
+ printf ("verbose daemon enabled\n");
- if (p) {
- p += 8;
- p2 = strchr (p, ':');
- if (p2) {
- *p2++ = '\0';
- host = p;
- r = strcspn (p2, " \n");
- p2[r] = '\0';
- port = p2;
- }
- }
- }
-
- next:
- /* Can't parse /proc/cmdline, so use built-in defaults. */
- if (!host || !port) {
- host = VMCHANNEL_ADDR;
- port = VMCHANNEL_PORT;
+ if (verbose) {
+ if (cmdline)
+ printf ("linux commmand line: %s\n", cmdline);
+ else
+ printf ("could not read linux command line\n");
}
/* Make sure SIGPIPE doesn't kill us. */
+ struct sigaction sa;
memset (&sa, 0, sizeof sa);
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
/* We document that umask defaults to 022 (it should be this anyway). */
umask (022);
- /* Resolve the hostname. */
- 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);
+ /* 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. */
- 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");
+ /* Default vmchannel. */
+ if (vmchannel == NULL) {
+ vmchannel = strdup ("tcp:" GUESTFWD_ADDR ":" GUESTFWD_PORT);
+ if (!vmchannel) {
+ perror ("strdup");
+ exit (1);
+ }
+ }
- close (sock);
- sock = -1;
+ if (verbose)
+ printf ("vmchannel: %s\n", vmchannel);
+
+ /* Connect to vmchannel. */
+ int 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", host, 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);
}
/* Send the magic length message which indicates that
* userspace is up inside the guest.
*/
- len = GUESTFS_LAUNCH_FLAG;
- xdrmem_create (&xdr, buf, sizeof buf, XDR_ENCODE);
- if (!xdr_uint32_t (&xdr, &len)) {
- fprintf (stderr, "xdr_uint32_t failed\n");
- exit (1);
- }
+ char lenbuf[4];
+ XDR xdr;
+ uint32_t len = GUESTFS_LAUNCH_FLAG;
+ xdrmem_create (&xdr, lenbuf, sizeof lenbuf, XDR_ENCODE);
+ xdr_uint32_t (&xdr, &len);
- (void) xwrite (sock, buf, xdr_getpos (&xdr));
+ if (xwrite (sock, lenbuf, sizeof lenbuf) == -1)
+ exit (1);
xdr_destroy (&xdr);
exit (0);
}
+/* Read /proc/cmdline. */
+static char *
+read_cmdline (void)
+{
+ int fd = open ("/proc/cmdline", O_RDONLY);
+ if (fd == -1) {
+ perror ("/proc/cmdline");
+ return NULL;
+ }
+
+ size_t len = 0;
+ ssize_t n;
+ char buf[256];
+ char *r = NULL;
+
+ for (;;) {
+ n = read (fd, buf, sizeof buf);
+ if (n == -1) {
+ perror ("read");
+ free (r);
+ close (fd);
+ return NULL;
+ }
+ if (n == 0)
+ break;
+ char *newr = realloc (r, len + n + 1); /* + 1 is for terminating NUL */
+ if (newr == NULL) {
+ perror ("realloc");
+ free (r);
+ close (fd);
+ return NULL;
+ }
+ r = newr;
+ memcpy (&r[len], buf, n);
+ len += n;
+ }
+
+ if (r)
+ r[len] = '\0';
+
+ if (close (fd) == -1) {
+ perror ("close");
+ free (r);
+ return NULL;
+ }
+
+ return r;
+}
+
/* Turn "/path" into "/sysroot/path".
*
* Caller must check for NULL and call reply_with_perror ("malloc")
}
int
-xwrite (int sock, const void *buf, size_t len)
+xwrite (int sock, const void *v_buf, size_t len)
{
int r;
+ const char *buf = v_buf;
while (len > 0) {
r = write (sock, buf, len);
}
int
-xread (int sock, void *buf, size_t len)
+xread (int sock, void *v_buf, size_t len)
{
int r;
+ char *buf = v_buf;
while (len > 0) {
r = read (sock, buf, len);
return 0;
}
-static void
-usage (void)
-{
- fprintf (stderr, "guestfsd [-f] [-h host -p port]\n");
-}
-
int
add_string (char ***argv, int *size, int *alloc, const char *str)
{
const struct printf_info *info ATTRIBUTE_UNUSED,
const void *const *args)
{
-#define SAFE(c) (isalnum((c)) || \
+#define SAFE(c) (c_isalnum((c)) || \
(c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
int i, len;
const char *str = *((const char **) (args[0]));
const struct printf_info *info,
const void *const *args)
{
-#define SAFE(c) (isalnum((c)) || \
- (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
fputs (sysroot, stream);
return sysroot_len + print_shell_quote (stream, info, args);
}