>From 77914a698bcd1de409e8da653e9663593890d959 Mon Sep 17 00:00:00 2001 From: Richard W.M. Jones Date: Thu, 27 Jan 2011 15:54:33 +0000 Subject: [PATCH 4/5] lib: Implement attach-method unix: 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: (or perform the equivalent on the qemu command line). Then in guestfish, you can do: guestfish -n \ attach-method unix:/tmp/socket : \ run : \ ll / (or any other commands as desired). Note the '-n' option is necessary to stop autosync, which would try to unmount all of the guest's disks. --- src/launch.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/proto.c | 19 +++++++----- 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/launch.c b/src/launch.c index 775f7a6..e50985d 100644 --- a/src/launch.c +++ b/src/launch.c @@ -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 diff --git a/src/proto.c b/src/proto.c index 0d63af6..549734b 100644 --- a/src/proto.c +++ b/src/proto.c @@ -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; } -- 1.7.3.5