lib: Implement attach-method unix:<path>
authorRichard W.M. Jones <rjones@redhat.com>
Thu, 27 Jan 2011 15:54:33 +0000 (15:54 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Thu, 3 Feb 2011 18:50:45 +0000 (18:50 +0000)
Allow connections to a Unix domain socket which is connected
(via virtio-serial) to a guestfsd running free in an existing
guest.

In order to use this you have to add the following element
to the libvirt XML:

  <channel type='unix'>
    <source mode='bind' path='/tmp/socket'/>
    <target type='virtio' name='org.libguestfs.channel.0'/>
  </channel>

(or perform the equivalent on the qemu command line).

Then in guestfish, you can do:

  guestfish \
    attach-method unix:/tmp/socket : \
    run : \
    ll /

(or any other commands as desired).

src/launch.c
src/proto.c

index 775f7a6..e50985d 100644 (file)
@@ -71,6 +71,7 @@
 #include "guestfs_protocol.h"
 
 static int launch_appliance (guestfs_h *g);
+static int connect_unix_socket (guestfs_h *g, const char *sock);
 static int qemu_supports (guestfs_h *g, const char *option);
 
 /* Add a string to the current command line. */
@@ -374,7 +375,17 @@ guestfs__launch (guestfs_h *g)
   if (chmod (g->tmpdir, 0755) == -1)
     fprintf (stderr, "chmod: %s: %m (ignored)\n", g->tmpdir);
 
-  return launch_appliance (g);
+  /* Launch the appliance or attach to an existing daemon. */
+  switch (g->attach_method) {
+  case ATTACH_METHOD_APPLIANCE:
+    return launch_appliance (g);
+
+  case ATTACH_METHOD_UNIX:
+    return connect_unix_socket (g, g->attach_method_arg);
+
+  default:
+    abort ();
+  }
 }
 
 static int
@@ -778,6 +789,78 @@ launch_appliance (guestfs_h *g)
   return -1;
 }
 
+/* Alternate attach method: instead of launching the appliance,
+ * connect to an existing unix socket.
+ */
+static int
+connect_unix_socket (guestfs_h *g, const char *sockpath)
+{
+  int r;
+  struct sockaddr_un addr;
+
+  /* Start the clock ... */
+  gettimeofday (&g->launch_t, NULL);
+
+  /* Set these to nothing so we don't try to kill random processes or
+   * read from random file descriptors.
+   */
+  g->pid = 0;
+  g->recoverypid = 0;
+  g->fd[0] = -1;
+  g->fd[1] = -1;
+
+  if (g->verbose)
+    guestfs___print_timestamped_message (g, "connecting to %s", sockpath);
+
+  g->sock = socket (AF_UNIX, SOCK_STREAM, 0);
+  if (g->sock == -1) {
+    perrorf (g, "socket");
+    return -1;
+  }
+
+  addr.sun_family = AF_UNIX;
+  strncpy (addr.sun_path, sockpath, UNIX_PATH_MAX);
+  addr.sun_path[UNIX_PATH_MAX-1] = '\0';
+
+  g->state = LAUNCHING;
+
+  if (connect (g->sock, &addr, sizeof addr) == -1) {
+    perrorf (g, "bind");
+    goto cleanup;
+  }
+
+  if (fcntl (g->sock, F_SETFL, O_NONBLOCK) == -1) {
+    perrorf (g, "fcntl");
+    goto cleanup;
+  }
+
+  uint32_t size;
+  void *buf = NULL;
+  r = guestfs___recv_from_daemon (g, &size, &buf);
+  free (buf);
+
+  if (r == -1) return -1;
+
+  if (size != GUESTFS_LAUNCH_FLAG) {
+    error (g, _("guestfs_launch failed, unexpected initial message from guestfsd"));
+    goto cleanup;
+  }
+
+  if (g->verbose)
+    guestfs___print_timestamped_message (g, "connected");
+
+  if (g->state != READY) {
+    error (g, _("contacted guestfsd, but state != READY"));
+    goto cleanup;
+  }
+
+  return 0;
+
+ cleanup:
+  close (g->sock);
+  return -1;
+}
+
 /* Return the location of the tmpdir (eg. "/tmp") and allow users
  * to override it at runtime using $TMPDIR.
  * http://www.pathname.com/fhs/pub/fhs-2.3.html#TMPTEMPORARYFILES
index 0d63af6..549734b 100644 (file)
@@ -183,8 +183,8 @@ child_cleanup (guestfs_h *g)
   if (g->recoverypid > 0) kill (g->recoverypid, 9);
   waitpid (g->pid, NULL, 0);
   if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);
-  close (g->fd[0]);
-  close (g->fd[1]);
+  if (g->fd[0] >= 0) close (g->fd[0]);
+  if (g->fd[1] >= 0) close (g->fd[1]);
   close (g->sock);
   g->fd[0] = -1;
   g->fd[1] = -1;
@@ -381,7 +381,8 @@ guestfs___send_to_daemon (guestfs_h *g, const void *v_buf, size_t n)
   FD_ZERO (&rset);
   FD_ZERO (&wset);
 
-  FD_SET (g->fd[1], &rset);     /* Read qemu stdout for log messages & EOF. */
+  if (g->fd[1] >= 0)            /* Read qemu stdout for log messages & EOF. */
+    FD_SET (g->fd[1], &rset);
   FD_SET (g->sock, &rset);      /* Read socket for cancellation & EOF. */
   FD_SET (g->sock, &wset);      /* Write to socket to send the data. */
 
@@ -398,7 +399,7 @@ guestfs___send_to_daemon (guestfs_h *g, const void *v_buf, size_t n)
       return -1;
     }
 
-    if (FD_ISSET (g->fd[1], &rset2)) {
+    if (g->fd[1] >= 0 && FD_ISSET (g->fd[1], &rset2)) {
       if (read_log_message_or_eof (g, g->fd[1], 0) == -1)
         return -1;
     }
@@ -460,7 +461,8 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn)
 
   FD_ZERO (&rset);
 
-  FD_SET (g->fd[1], &rset);     /* Read qemu stdout for log messages & EOF. */
+  if (g->fd[1] >= 0)            /* Read qemu stdout for log messages & EOF. */
+    FD_SET (g->fd[1], &rset);
   FD_SET (g->sock, &rset);      /* Read socket for data & EOF. */
 
   int max_fd = MAX (g->sock, g->fd[1]);
@@ -492,7 +494,7 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn)
       return -1;
     }
 
-    if (FD_ISSET (g->fd[1], &rset2)) {
+    if (g->fd[1] >= 0 && FD_ISSET (g->fd[1], &rset2)) {
       if (read_log_message_or_eof (g, g->fd[1], 0) == -1) {
         free (*buf_rtn);
         *buf_rtn = NULL;
@@ -650,7 +652,8 @@ guestfs___accept_from_daemon (guestfs_h *g)
 
   FD_ZERO (&rset);
 
-  FD_SET (g->fd[1], &rset);     /* Read qemu stdout for log messages & EOF. */
+  if (g->fd[1] >= 0)            /* Read qemu stdout for log messages & EOF. */
+    FD_SET (g->fd[1], &rset);
   FD_SET (g->sock, &rset);      /* Read socket for accept. */
 
   int max_fd = MAX (g->sock, g->fd[1]);
@@ -674,7 +677,7 @@ guestfs___accept_from_daemon (guestfs_h *g)
       return -1;
     }
 
-    if (FD_ISSET (g->fd[1], &rset2)) {
+    if (g->fd[1] >= 0 && FD_ISSET (g->fd[1], &rset2)) {
       if (read_log_message_or_eof (g, g->fd[1], 1) == -1)
         return -1;
     }