Guest boots, and basic select/callbacks work.
[libguestfs.git] / src / guestfs.c
1 /* libguestfs
2  * Copyright (C) 2009 Red Hat Inc. 
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include <config.h>
20
21 #define _BSD_SOURCE /* for mkdtemp, usleep */
22 #define _GNU_SOURCE /* for vasprintf, GNU strerror_r */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <unistd.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <time.h>
32
33 #ifdef HAVE_ERRNO_H
34 #include <errno.h>
35 #endif
36
37 #ifdef HAVE_SYS_TYPES_H
38 #include <sys/types.h>
39 #endif
40
41 #ifdef HAVE_SYS_WAIT_H
42 #include <sys/wait.h>
43 #endif
44
45 #ifdef HAVE_SYS_SOCKET_H
46 #include <sys/socket.h>
47 #endif
48
49 #ifdef HAVE_SYS_UN_H
50 #include <sys/un.h>
51 #endif
52
53 #include <sys/select.h>
54
55 #include "guestfs.h"
56
57 static void error (guestfs_h *g, const char *fs, ...);
58 static void perrorf (guestfs_h *g, const char *fs, ...);
59 static void *safe_malloc (guestfs_h *g, int nbytes);
60 static void *safe_realloc (guestfs_h *g, void *ptr, int nbytes);
61 static char *safe_strdup (guestfs_h *g, const char *str);
62
63 static void default_error_cb (guestfs_h *g, void *data, const char *msg);
64 static void stdout_event (void *data, int watch, int fd, int events);
65 static void sock_read_event (void *data, int watch, int fd, int events);
66 //static void sock_write_event (void *data, int watch, int fd, int events);
67
68 static int select_add_handle (guestfs_h *g, int fd, int events, guestfs_handle_event_cb cb, void *data);
69 static int select_remove_handle (guestfs_h *g, int watch);
70 static int select_add_timeout (guestfs_h *g, int interval, guestfs_handle_timeout_cb cb, void *data);
71 static int select_remove_timeout (guestfs_h *g, int timer);
72 static void select_main_loop_run (guestfs_h *g);
73 static void select_main_loop_quit (guestfs_h *g);
74
75 #define UNIX_PATH_MAX 108
76
77 #define VMCHANNEL_PORT 6666
78 #define VMCHANNEL_ADDR "10.0.2.4"
79
80 /* Current main loop. */
81 static guestfs_main_loop main_loop = {
82   .add_handle = select_add_handle,
83   .remove_handle = select_remove_handle,
84   .add_timeout = select_add_timeout,
85   .remove_timeout = select_remove_timeout,
86   .main_loop_run = select_main_loop_run,
87   .main_loop_quit = select_main_loop_quit,
88 };
89
90 /* GuestFS handle and connection. */
91 enum state { CONFIG, LAUNCHING, READY, BUSY, NO_HANDLE };
92
93 struct guestfs_h
94 {
95   /* State: see the state machine diagram in the man page guestfs(3). */
96   enum state state;
97
98   int fd[2];                    /* Stdin/stdout of qemu. */
99   int sock;                     /* Daemon communications socket. */
100   int pid;                      /* Qemu PID. */
101   time_t start_t;               /* The time when we started qemu. */
102
103   int stdout_watch;             /* Watches qemu stdout for log messages. */
104   int sock_watch;               /* Watches daemon comm socket. */
105
106   char *tmpdir;                 /* Temporary directory containing socket. */
107
108   char **cmdline;               /* Qemu command line. */
109   int cmdline_size;
110
111   int verbose;
112
113   /* Callbacks. */
114   guestfs_abort_cb           abort_cb;
115   guestfs_error_handler_cb   error_cb;
116   void *                     error_cb_data;
117   guestfs_reply_cb           reply_cb;
118   void *                     reply_cb_data;
119   guestfs_log_message_cb     log_message_cb;
120   void *                     log_message_cb_data;
121   guestfs_subprocess_quit_cb subprocess_quit_cb;
122   void *                     subprocess_quit_cb_data;
123   guestfs_launch_done_cb     launch_done_cb;
124   void *                     launch_done_cb_data;
125
126   /* These callbacks are called before reply_cb and launch_done_cb,
127    * and are used to implement the high-level API without needing to
128    * interfere with callbacks that the user might have set.
129    */
130   guestfs_reply_cb           reply_cb_internal;
131   void *                     reply_cb_internal_data;
132   guestfs_launch_done_cb     launch_done_cb_internal;
133   void *                     launch_done_cb_internal_data;
134 };
135
136 guestfs_h *
137 guestfs_create (void)
138 {
139   guestfs_h *g;
140   const char *str;
141
142   g = malloc (sizeof (*g));
143   if (!g) return NULL;
144
145   memset (g, 0, sizeof (*g));
146
147   g->state = CONFIG;
148
149   g->fd[0] = -1;
150   g->fd[1] = -1;
151   g->sock = -1;
152   g->stdout_watch = -1;
153   g->sock_watch = -1;
154
155   g->abort_cb = abort;
156   g->error_cb = default_error_cb;
157   g->error_cb_data = NULL;
158
159   str = getenv ("LIBGUESTFS_DEBUG");
160   g->verbose = str != NULL && strcmp (str, "1") == 0;
161
162   return g;
163 }
164
165 void
166 guestfs_close (guestfs_h *g)
167 {
168   int i;
169   char filename[256];
170
171   if (g->state == NO_HANDLE) {
172     /* Not safe to call 'error' here, so ... */
173     fprintf (stderr, "guestfs_close: called twice on the same handle\n");
174     return;
175   }
176
177   /* Remove any handlers that might be called back before we kill the
178    * subprocess.
179    */
180   g->log_message_cb = NULL;
181
182   if (g->state != CONFIG)
183     guestfs_kill_subprocess (g);
184
185   if (g->tmpdir) {
186     snprintf (filename, sizeof filename, "%s/sock", g->tmpdir);
187     unlink (filename);
188
189     rmdir (g->tmpdir);
190
191     free (g->tmpdir);
192   }
193
194   if (g->cmdline) {
195     for (i = 0; i < g->cmdline_size; ++i)
196       free (g->cmdline[i]);
197     free (g->cmdline);
198   }
199
200   /* Mark the handle as dead before freeing it. */
201   g->state = NO_HANDLE;
202
203   free (g);
204 }
205
206 static void
207 default_error_cb (guestfs_h *g, void *data, const char *msg)
208 {
209   fprintf (stderr, "libguestfs: %s\n", msg);
210 }
211
212 static void
213 error (guestfs_h *g, const char *fs, ...)
214 {
215   va_list args;
216   char *msg;
217
218   if (!g->error_cb) return;
219
220   va_start (args, fs);
221   vasprintf (&msg, fs, args);
222   va_end (args);
223
224   g->error_cb (g, g->error_cb_data, msg);
225
226   free (msg);
227 }
228
229 static void
230 perrorf (guestfs_h *g, const char *fs, ...)
231 {
232   va_list args;
233   char *msg;
234   int err = errno;
235
236   if (!g->error_cb) return;
237
238   va_start (args, fs);
239   vasprintf (&msg, fs, args);
240   va_end (args);
241
242 #ifndef _GNU_SOURCE
243   char buf[256];
244   strerror_r (err, buf, sizeof buf);
245 #else
246   char _buf[256];
247   char *buf;
248   buf = strerror_r (err, _buf, sizeof _buf);
249 #endif
250
251   msg = safe_realloc (g, msg, strlen (msg) + 2 + strlen (buf) + 1);
252   strcat (msg, ": ");
253   strcat (msg, buf);
254
255   g->error_cb (g, g->error_cb_data, msg);
256
257   free (msg);
258 }
259
260 static void *
261 safe_malloc (guestfs_h *g, int nbytes)
262 {
263   void *ptr = malloc (nbytes);
264   if (!ptr) g->abort_cb ();
265   return ptr;
266 }
267
268 static void *
269 safe_realloc (guestfs_h *g, void *ptr, int nbytes)
270 {
271   void *p = realloc (ptr, nbytes);
272   if (!p) g->abort_cb ();
273   return p;
274 }
275
276 static char *
277 safe_strdup (guestfs_h *g, const char *str)
278 {
279   char *s = strdup (str);
280   if (!s) g->abort_cb ();
281   return s;
282 }
283
284 void
285 guestfs_set_out_of_memory_handler (guestfs_h *g, guestfs_abort_cb cb)
286 {
287   g->abort_cb = cb;
288 }
289
290 guestfs_abort_cb
291 guestfs_get_out_of_memory_handler (guestfs_h *g)
292 {
293   return g->abort_cb;
294 }
295
296 void
297 guestfs_set_error_handler (guestfs_h *g, guestfs_error_handler_cb cb, void *data)
298 {
299   g->error_cb = cb;
300   g->error_cb_data = data;
301 }
302
303 guestfs_error_handler_cb
304 guestfs_get_error_handler (guestfs_h *g, void **data_rtn)
305 {
306   if (data_rtn) *data_rtn = g->error_cb_data;
307   return g->error_cb;
308 }
309
310 void
311 guestfs_set_verbose (guestfs_h *g, int v)
312 {
313   g->verbose = v;
314 }
315
316 int
317 guestfs_get_verbose (guestfs_h *g)
318 {
319   return g->verbose;
320 }
321
322 /* Add a string to the current command line. */
323 static void
324 incr_cmdline_size (guestfs_h *g)
325 {
326   if (g->cmdline == NULL) {
327     /* g->cmdline[0] is reserved for argv[0], set in guestfs_launch. */
328     g->cmdline_size = 1;
329     g->cmdline = safe_malloc (g, sizeof (char *));
330     g->cmdline[0] = NULL;
331   }
332
333   g->cmdline_size++;
334   g->cmdline = safe_realloc (g, g->cmdline, sizeof (char *) * g->cmdline_size);
335 }
336
337 static int
338 add_cmdline (guestfs_h *g, const char *str)
339 {
340   if (g->state != CONFIG) {
341     error (g, "command line cannot be altered after qemu subprocess launched");
342     return -1;
343   }
344
345   incr_cmdline_size (g);
346   g->cmdline[g->cmdline_size-1] = safe_strdup (g, str);
347   return 0;
348 }
349
350 int
351 guestfs_config (guestfs_h *g,
352                 const char *qemu_param, const char *qemu_value)
353 {
354   if (qemu_param[0] != '-') {
355     error (g, "guestfs_config: parameter must begin with '-' character");
356     return -1;
357   }
358
359   /* A bit fascist, but the user will probably break the extra
360    * parameters that we add if they try to set any of these.
361    */
362   if (strcmp (qemu_param, "-kernel") == 0 ||
363       strcmp (qemu_param, "-initrd") == 0 ||
364       strcmp (qemu_param, "-nographic") == 0 ||
365       strcmp (qemu_param, "-serial") == 0 ||
366       strcmp (qemu_param, "-vnc") == 0 ||
367       strcmp (qemu_param, "-full-screen") == 0 ||
368       strcmp (qemu_param, "-std-vga") == 0 ||
369       strcmp (qemu_param, "-vnc") == 0) {
370     error (g, "guestfs_config: parameter '%s' isn't allowed", qemu_param);
371     return -1;
372   }
373
374   if (add_cmdline (g, qemu_param) != 0) return -1;
375
376   if (qemu_value != NULL) {
377     if (add_cmdline (g, qemu_value) != 0) return -1;
378   }
379
380   return 0;
381 }
382
383 int
384 guestfs_add_drive (guestfs_h *g, const char *filename)
385 {
386   int len = strlen (filename) + 64;
387   char buf[len];
388
389   if (strchr (filename, ',') != NULL) {
390     error (g, "filename cannot contain ',' (comma) character");
391     return -1;
392   }
393
394   snprintf (buf, len, "file=%s", filename);
395
396   return guestfs_config (g, "-drive", buf);
397 }
398
399 int
400 guestfs_add_cdrom (guestfs_h *g, const char *filename)
401 {
402   if (strchr (filename, ',') != NULL) {
403     error (g, "filename cannot contain ',' (comma) character");
404     return -1;
405   }
406
407   return guestfs_config (g, "-cdrom", filename);
408 }
409
410 int
411 guestfs_launch (guestfs_h *g)
412 {
413   static const char *dir_template = "/tmp/libguestfsXXXXXX";
414   int r, i;
415   int wfd[2], rfd[2];
416   int tries;
417   /*const char *qemu = QEMU;*/  /* XXX */
418   const char *qemu = "/usr/bin/qemu-system-x86_64";
419   const char *kernel = "vmlinuz.fedora-10.x86_64";
420   const char *initrd = "initramfs.fedora-10.x86_64.img";
421   char unixsock[256];
422   struct sockaddr_un addr;
423
424   /* XXX Choose which qemu to run. */
425   /* XXX Choose initrd, etc. */
426
427   /* Configured? */
428   if (!g->cmdline) {
429     error (g, "you must call guestfs_add_drive before guestfs_launch");
430     return -1;
431   }
432
433   if (g->state != CONFIG) {
434     error (g, "qemu has already been launched");
435     return -1;
436   }
437
438   /* Make the temporary directory containing the socket. */
439   if (!g->tmpdir) {
440     g->tmpdir = safe_strdup (g, dir_template);
441     if (mkdtemp (g->tmpdir) == NULL) {
442       perrorf (g, "%s: cannot create temporary directory", dir_template);
443       return -1;
444     }
445   }
446
447   snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir);
448
449   if (pipe (wfd) == -1 || pipe (rfd) == -1) {
450     perrorf (g, "pipe");
451     return -1;
452   }
453
454   r = fork ();
455   if (r == -1) {
456     perrorf (g, "fork");
457     close (wfd[0]);
458     close (wfd[1]);
459     close (rfd[0]);
460     close (rfd[1]);
461     return -1;
462   }
463
464   if (r == 0) {                 /* Child (qemu). */
465     char vmchannel[256];
466     char append[256];
467
468     /* Set up the full command line.  Do this in the subprocess so we
469      * don't need to worry about cleaning up.
470      */
471     g->cmdline[0] = (char *) qemu;
472
473     /* Construct the -net channel parameter for qemu. */
474     snprintf (vmchannel, sizeof vmchannel,
475               "channel,%d:unix:%s,server,nowait",
476               VMCHANNEL_PORT, unixsock);
477
478     /* Linux kernel command line. */
479     snprintf (append, sizeof append,
480               "console=ttyS0 guestfs=%s:%d", VMCHANNEL_ADDR, VMCHANNEL_PORT);
481
482     add_cmdline (g, "-m");
483     add_cmdline (g, "384");     /* XXX Choose best size. */
484     add_cmdline (g, "-kernel");
485     add_cmdline (g, (char *) kernel);
486     add_cmdline (g, "-initrd");
487     add_cmdline (g, (char *) initrd);
488     add_cmdline (g, "-append");
489     add_cmdline (g, append);
490     add_cmdline (g, "-nographic");
491     add_cmdline (g, "-serial");
492     add_cmdline (g, "stdio");
493     add_cmdline (g, "-net");
494     add_cmdline (g, vmchannel);
495     add_cmdline (g, "-net");
496     add_cmdline (g, "user,vlan=0");
497     add_cmdline (g, "-net");
498     add_cmdline (g, "nic,vlan=0");
499     incr_cmdline_size (g);
500     g->cmdline[g->cmdline_size-1] = NULL;
501
502     if (g->verbose) {
503       fprintf (stderr, "%s", qemu);
504       for (i = 0; g->cmdline[i]; ++i)
505         fprintf (stderr, " %s", g->cmdline[i]);
506       fprintf (stderr, "\n");
507     }
508
509     /* Set up stdin, stdout. */
510     close (0);
511     close (1);
512     close (wfd[1]);
513     close (rfd[0]);
514     dup (wfd[0]);
515     dup (rfd[1]);
516
517 #if 0
518     /* Set up a new process group, so we can signal this process
519      * and all subprocesses (eg. if qemu is really a shell script).
520      */
521     setpgid (0, 0);
522 #endif
523
524     execv (qemu, g->cmdline);   /* Run qemu. */
525     perror (qemu);
526     _exit (1);
527   }
528
529   /* Parent (library). */
530   g->pid = r;
531
532   /* Start the clock ... */
533   time (&g->start_t);
534
535   /* Close the other ends of the pipe. */
536   close (wfd[0]);
537   close (rfd[1]);
538
539   if (fcntl (wfd[1], F_SETFL, O_NONBLOCK) == -1 ||
540       fcntl (rfd[0], F_SETFL, O_NONBLOCK) == -1) {
541     perrorf (g, "fcntl");
542     goto cleanup1;
543   }
544
545   g->fd[0] = wfd[1];            /* stdin of child */
546   g->fd[1] = rfd[0];            /* stdout of child */
547
548   /* Open the Unix socket.  The vmchannel implementation that got
549    * merged with qemu sucks in a number of ways.  Both ends do
550    * connect(2), which means that no one knows what, if anything, is
551    * connected to the other end, or if it becomes disconnected.  Even
552    * worse, we have to wait some indeterminate time for qemu to create
553    * the socket and connect to it (which happens very early in qemu's
554    * start-up), so any code that uses vmchannel is inherently racy.
555    * Hence this silly loop.
556    */
557   g->sock = socket (AF_UNIX, SOCK_STREAM, 0);
558   if (g->sock == -1) {
559     perrorf (g, "socket");
560     goto cleanup1;
561   }
562
563   if (fcntl (g->sock, F_SETFL, O_NONBLOCK) == -1) {
564     perrorf (g, "fcntl");
565     goto cleanup2;
566   }
567
568   addr.sun_family = AF_UNIX;
569   strncpy (addr.sun_path, unixsock, UNIX_PATH_MAX);
570   addr.sun_path[UNIX_PATH_MAX-1] = '\0';
571
572   tries = 100;
573   while (tries > 0) {
574     /* Always sleep at least once to give qemu a small chance to start up. */
575     usleep (10000);
576
577     r = connect (g->sock, (struct sockaddr *) &addr, sizeof addr);
578     if ((r == -1 && errno == EINPROGRESS) || r == 0)
579       goto connected;
580
581     perrorf (g, "connect");
582     tries--;
583   }
584
585   error (g, "failed to connect to vmchannel socket");
586   goto cleanup2;
587
588  connected:
589   /* Watch the file descriptors. */
590   g->stdout_watch =
591     main_loop.add_handle (g, g->fd[1],
592                           GUESTFS_HANDLE_READABLE,
593                           stdout_event, g);
594   if (g->stdout_watch == -1) {
595     error (g, "could not watch qemu stdout");
596     goto cleanup3;
597   }
598
599   g->sock_watch =
600     main_loop.add_handle (g, g->sock,
601                           GUESTFS_HANDLE_READABLE |
602                           GUESTFS_HANDLE_HANGUP |
603                           GUESTFS_HANDLE_ERROR,
604                           sock_read_event, g);
605   if (g->sock_watch == -1) {
606     error (g, "could not watch daemon communications socket");
607     goto cleanup3;
608   }
609
610   g->state = LAUNCHING;
611   return 0;
612
613  cleanup3:
614   if (g->stdout_watch >= 0)
615     main_loop.remove_handle (g, g->stdout_watch);
616   if (g->sock_watch >= 0)
617     main_loop.remove_handle (g, g->sock_watch);
618
619  cleanup2:
620   close (g->sock);
621
622  cleanup1:
623   close (wfd[1]);
624   close (rfd[0]);
625   kill (g->pid, 9);
626   waitpid (g->pid, NULL, 0);
627   g->fd[0] = -1;
628   g->fd[1] = -1;
629   g->sock = -1;
630   g->pid = 0;
631   g->start_t = 0;
632   g->stdout_watch = -1;
633   g->sock_watch = -1;
634   return -1;
635 }
636
637 static void
638 finish_wait_ready (guestfs_h *g, void *vp)
639 {
640   *((int *)vp) = 1;
641   main_loop.main_loop_quit (g);
642 }
643
644 int
645 guestfs_wait_ready (guestfs_h *g)
646 {
647   int r = 0;
648
649   if (g->state == READY) return 0;
650
651   if (g->state == BUSY) {
652     error (g, "qemu has finished launching already");
653     return -1;
654   }
655
656   if (g->state != LAUNCHING) {
657     error (g, "qemu has not been launched yet");
658     return -1;
659   }
660
661   g->launch_done_cb_internal = finish_wait_ready;
662   g->launch_done_cb_internal_data = &r;
663   main_loop.main_loop_run (g);
664   g->launch_done_cb_internal = NULL;
665   g->launch_done_cb_internal_data = NULL;
666
667   if (r != 1) {
668     error (g, "guestfs_wait_ready failed, see earlier error messages");
669     return -1;
670   }
671
672   /* This is possible in some really strange situations, such as
673    * guestfsd starts up OK but then qemu immediately exits.  Check for
674    * it because the caller is probably expecting to be able to send
675    * commands after this function returns.
676    */
677   if (g->state != READY) {
678     error (g, "qemu launched and contacted daemon, but state != READY");
679     return -1;
680   }
681
682   return 0;
683 }
684
685 int
686 guestfs_kill_subprocess (guestfs_h *g)
687 {
688   if (g->state == CONFIG) {
689     error (g, "no subprocess to kill");
690     return -1;
691   }
692
693   if (g->verbose)
694     fprintf (stderr, "sending SIGTERM to process group %d\n", g->pid);
695
696   kill (g->pid, SIGTERM);
697
698   return 0;
699 }
700
701 /* This function is called whenever qemu prints something on stdout.
702  * Qemu's stdout is also connected to the guest's serial console, so
703  * we see kernel messages here too.
704  */
705 static void
706 stdout_event (void *data, int watch, int fd, int events)
707 {
708   guestfs_h *g = (guestfs_h *) data;
709   char buf[4096];
710   int n;
711
712 #if 0
713   if (g->verbose)
714     fprintf (stderr,
715              "stdout_event: %p g->state = %d, fd = %d, events = 0x%x\n",
716              g, g->state, fd, events);
717 #endif
718
719   if (g->fd[1] != fd) {
720     error (g, "stdout_event: internal error: %d != %d", g->fd[1], fd);
721     return;
722   }
723
724   n = read (fd, buf, sizeof buf);
725   if (n == 0) {
726     /* Hopefully this indicates the qemu child process has died. */
727     if (g->verbose)
728       fprintf (stderr, "stdout_event: %p: child process died\n", g);
729     /*kill (g->pid, SIGTERM);*/
730     waitpid (g->pid, NULL, 0);
731     if (g->stdout_watch >= 0)
732       main_loop.remove_handle (g, g->stdout_watch);
733     if (g->sock_watch >= 0)
734       main_loop.remove_handle (g, g->sock_watch);
735     close (g->fd[0]);
736     close (g->fd[1]);
737     close (g->sock);
738     g->fd[0] = -1;
739     g->fd[1] = -1;
740     g->sock = -1;
741     g->pid = 0;
742     g->start_t = 0;
743     g->stdout_watch = -1;
744     g->sock_watch = -1;
745     g->state = CONFIG;
746     if (g->subprocess_quit_cb)
747       g->subprocess_quit_cb (g, g->subprocess_quit_cb_data);
748     return;
749   }
750
751   if (n == -1) {
752     if (errno != EAGAIN)
753       perrorf (g, "read");
754     return;
755   }
756
757   /* In verbose mode, copy all log messages to stderr. */
758   if (g->verbose)
759     write (2, buf, n);
760
761   /* It's an actual log message, send it upwards if anyone is listening. */
762   if (g->log_message_cb)
763     g->log_message_cb (g, g->log_message_cb_data, buf, n);
764 }
765
766 /* The function is called whenever we can read something on the
767  * guestfsd (daemon inside the guest) communication socket.
768  */
769 static void
770 sock_read_event (void *data, int watch, int fd, int events)
771 {
772   /*guestfs_h *g = (guestfs_h *) data;*/
773
774
775
776
777
778
779
780 }
781
782 /* This is the default main loop implementation, using select(2). */
783
784 struct handle_cb_data {
785   guestfs_handle_event_cb cb;
786   void *data;
787 };
788
789 static fd_set rset;
790 static fd_set wset;
791 static fd_set xset;
792 static int select_init_done = 0;
793 static int max_fd = -1;
794 static struct handle_cb_data *handle_cb_data = NULL;
795
796 static void
797 select_init (void)
798 {
799   if (!select_init_done) {
800     FD_ZERO (&rset);
801     FD_ZERO (&wset);
802     FD_ZERO (&xset);
803
804     select_init_done = 1;
805   }
806 }
807
808 static int
809 select_add_handle (guestfs_h *g, int fd, int events,
810                    guestfs_handle_event_cb cb, void *data)
811 {
812   select_init ();
813
814   if (fd < 0 || fd >= FD_SETSIZE) {
815     error (g, "fd %d is out of range", fd);
816     return -1;
817   }
818
819   if ((events & ~(GUESTFS_HANDLE_READABLE |
820                   GUESTFS_HANDLE_WRITABLE |
821                   GUESTFS_HANDLE_HANGUP |
822                   GUESTFS_HANDLE_ERROR)) != 0) {
823     error (g, "set of events (0x%x) contains unknown events", events);
824     return -1;
825   }
826
827   if (events == 0) {
828     error (g, "set of events is empty");
829     return -1;
830   }
831
832   if (FD_ISSET (fd, &rset) || FD_ISSET (fd, &wset) || FD_ISSET (fd, &xset)) {
833     error (g, "fd %d is already registered", fd);
834     return -1;
835   }
836
837   if (cb == NULL) {
838     error (g, "callback is NULL");
839     return -1;
840   }
841
842   if ((events & GUESTFS_HANDLE_READABLE))
843     FD_SET (fd, &rset);
844   if ((events & GUESTFS_HANDLE_WRITABLE))
845     FD_SET (fd, &wset);
846   if ((events & GUESTFS_HANDLE_HANGUP) || (events & GUESTFS_HANDLE_ERROR))
847     FD_SET (fd, &xset);
848
849   if (fd > max_fd) {
850     max_fd = fd;
851     handle_cb_data = safe_realloc (g, handle_cb_data,
852                                    sizeof (struct handle_cb_data) * (max_fd+1));
853   }
854   handle_cb_data[fd].cb = cb;
855   handle_cb_data[fd].data = data;
856
857   /* Any integer >= 0 can be the handle, and this is as good as any ... */
858   return fd;
859 }
860
861 static int
862 select_remove_handle (guestfs_h *g, int fd)
863 {
864   select_init ();
865
866   if (fd < 0 || fd >= FD_SETSIZE) {
867     error (g, "fd %d is out of range", fd);
868     return -1;
869   }
870
871   if (!FD_ISSET (fd, &rset) && !FD_ISSET (fd, &wset) && !FD_ISSET (fd, &xset)) {
872     error (g, "fd %d was not registered", fd);
873     return -1;
874   }
875
876   FD_CLR (fd, &rset);
877   FD_CLR (fd, &wset);
878   FD_CLR (fd, &xset);
879
880   if (fd == max_fd) {
881     max_fd--;
882     handle_cb_data = safe_realloc (g, handle_cb_data,
883                                    sizeof (struct handle_cb_data) * (max_fd+1));
884   }
885
886   return 0;
887 }
888
889 static int
890 select_add_timeout (guestfs_h *g, int interval,
891                     guestfs_handle_timeout_cb cb, void *data)
892 {
893   select_init ();
894
895   abort ();                     /* XXX not implemented yet */
896 }
897
898 static int
899 select_remove_timeout (guestfs_h *g, int timer)
900 {
901   select_init ();
902
903   abort ();                     /* XXX not implemented yet */
904 }
905
906 /* Note that main loops can be nested. */
907 static int level = 0;
908
909 static void
910 select_main_loop_run (guestfs_h *g)
911 {
912   int old_level, fd, r, events;
913   fd_set rset2, wset2, xset2;
914
915   select_init ();
916
917   old_level = level++;
918   while (level > old_level) {
919     rset2 = rset;
920     wset2 = wset;
921     xset2 = xset;
922     r = select (max_fd+1, &rset2, &wset2, &xset2, NULL);
923     if (r == -1) {
924       perrorf (g, "select");
925       level = old_level;
926       break;
927     }
928
929     for (fd = 0; r > 0 && fd <= max_fd; ++fd) {
930       events = 0;
931       if (FD_ISSET (fd, &rset2))
932         events |= GUESTFS_HANDLE_READABLE;
933       if (FD_ISSET (fd, &wset2))
934         events |= GUESTFS_HANDLE_WRITABLE;
935       if (FD_ISSET (fd, &xset2))
936         events |= GUESTFS_HANDLE_ERROR | GUESTFS_HANDLE_HANGUP;
937       if (events) {
938         r--;
939         handle_cb_data[fd].cb (handle_cb_data[fd].data,
940                                fd, fd, events);
941       }
942     }
943   }
944 }
945
946 static void
947 select_main_loop_quit (guestfs_h *g)
948 {
949   select_init ();
950
951   if (level == 0) {
952     error (g, "cannot quit, we are not in a main loop");
953     return;
954   }
955
956   level--;
957 }