+ xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_vg_list, (char *) x);
+ free (x);
+}
+
+void
+guestfs_free_lvm_lv_list (struct guestfs_lvm_lv_list *x)
+{
+ xdr_free ((xdrproc_t) xdr_guestfs_lvm_int_lv_list, (char *) x);
+ free (x);
+}
+
+/* This is the default main loop implementation, using select(2). */
+
+struct handle_cb_data {
+ guestfs_handle_event_cb cb;
+ void *data;
+};
+
+static fd_set rset;
+static fd_set wset;
+static fd_set xset;
+static int select_init_done = 0;
+static int max_fd = -1;
+static int nr_fds = 0;
+static struct handle_cb_data *handle_cb_data = NULL;
+
+static void
+select_init (void)
+{
+ if (!select_init_done) {
+ FD_ZERO (&rset);
+ FD_ZERO (&wset);
+ FD_ZERO (&xset);
+
+ select_init_done = 1;
+ }
+}
+
+static int
+select_add_handle (guestfs_h *g, int fd, int events,
+ guestfs_handle_event_cb cb, void *data)
+{
+ select_init ();
+
+ if (fd < 0 || fd >= FD_SETSIZE) {
+ error (g, "fd %d is out of range", fd);
+ return -1;
+ }
+
+ if ((events & ~(GUESTFS_HANDLE_READABLE |
+ GUESTFS_HANDLE_WRITABLE |
+ GUESTFS_HANDLE_HANGUP |
+ GUESTFS_HANDLE_ERROR)) != 0) {
+ error (g, "set of events (0x%x) contains unknown events", events);
+ return -1;
+ }
+
+ if (events == 0) {
+ error (g, "set of events is empty");
+ return -1;
+ }
+
+ if (FD_ISSET (fd, &rset) || FD_ISSET (fd, &wset) || FD_ISSET (fd, &xset)) {
+ error (g, "fd %d is already registered", fd);
+ return -1;
+ }
+
+ if (cb == NULL) {
+ error (g, "callback is NULL");
+ return -1;
+ }
+
+ if ((events & GUESTFS_HANDLE_READABLE))
+ FD_SET (fd, &rset);
+ if ((events & GUESTFS_HANDLE_WRITABLE))
+ FD_SET (fd, &wset);
+ if ((events & GUESTFS_HANDLE_HANGUP) || (events & GUESTFS_HANDLE_ERROR))
+ FD_SET (fd, &xset);
+
+ if (fd > max_fd) {
+ max_fd = fd;
+ handle_cb_data = safe_realloc (g, handle_cb_data,
+ sizeof (struct handle_cb_data) * (max_fd+1));
+ }
+ handle_cb_data[fd].cb = cb;
+ handle_cb_data[fd].data = data;
+
+ nr_fds++;
+
+ /* Any integer >= 0 can be the handle, and this is as good as any ... */
+ return fd;
+}
+
+static int
+select_remove_handle (guestfs_h *g, int fd)
+{
+ select_init ();
+
+ if (fd < 0 || fd >= FD_SETSIZE) {
+ error (g, "fd %d is out of range", fd);
+ return -1;
+ }
+
+ if (!FD_ISSET (fd, &rset) && !FD_ISSET (fd, &wset) && !FD_ISSET (fd, &xset)) {
+ error (g, "fd %d was not registered", fd);
+ return -1;
+ }
+
+ FD_CLR (fd, &rset);
+ FD_CLR (fd, &wset);
+ FD_CLR (fd, &xset);
+
+ if (fd == max_fd) {
+ max_fd--;
+ handle_cb_data = safe_realloc (g, handle_cb_data,
+ sizeof (struct handle_cb_data) * (max_fd+1));
+ }
+
+ nr_fds--;
+
+ return 0;
+}
+
+static int
+select_add_timeout (guestfs_h *g, int interval,
+ guestfs_handle_timeout_cb cb, void *data)
+{
+ select_init ();
+
+ abort (); /* XXX not implemented yet */
+}
+
+static int
+select_remove_timeout (guestfs_h *g, int timer)
+{
+ select_init ();
+
+ abort (); /* XXX not implemented yet */
+}
+
+/* Note that main loops can be nested. */
+static int level = 0;
+
+static void
+select_main_loop_run (guestfs_h *g)
+{
+ int old_level, fd, r, events;
+ fd_set rset2, wset2, xset2;
+
+ select_init ();
+
+ old_level = level++;
+ while (level > old_level) {
+ if (nr_fds == 0) {
+ level = old_level;
+ break;
+ }
+
+ rset2 = rset;
+ wset2 = wset;
+ xset2 = xset;
+ r = select (max_fd+1, &rset2, &wset2, &xset2, NULL);
+ if (r == -1) {
+ perrorf (g, "select");
+ level = old_level;
+ break;
+ }
+
+ for (fd = 0; r > 0 && fd <= max_fd; ++fd) {
+ events = 0;
+ if (FD_ISSET (fd, &rset2))
+ events |= GUESTFS_HANDLE_READABLE;
+ if (FD_ISSET (fd, &wset2))
+ events |= GUESTFS_HANDLE_WRITABLE;
+ if (FD_ISSET (fd, &xset2))
+ events |= GUESTFS_HANDLE_ERROR | GUESTFS_HANDLE_HANGUP;
+ if (events) {
+ r--;
+ handle_cb_data[fd].cb (handle_cb_data[fd].data,
+ fd, fd, events);
+ }
+ }
+ }
+}
+
+static void
+select_main_loop_quit (guestfs_h *g)
+{
+ select_init ();