X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=src%2Fguestfs.c;h=fea8107d18cdb486705b391d83b75aefd3e063d9;hb=179778adeed965a9652c344a201d2078d868d391;hp=be7b0c794e6117b904419d31b2bf49f55e1c145a;hpb=33377fc61d4356a0e83cf7ef39d545842647481c;p=libguestfs.git diff --git a/src/guestfs.c b/src/guestfs.c index be7b0c7..fea8107 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -58,6 +58,15 @@ #include "guestfs.h" #include "guestfs_protocol.h" +#ifdef HAVE_GETTEXT +#include "gettext.h" +#define _(str) dgettext(PACKAGE, (str)) +#define N_(str) dgettext(PACKAGE, (str)) +#else +#define _(str) str +#define N_(str) str +#endif + #define error guestfs_error #define perrorf guestfs_perrorf #define safe_malloc guestfs_safe_malloc @@ -158,8 +167,9 @@ struct guestfs_h int verbose; int autosync; - const char *path; - const char *qemu; + char *path; /* Path to kernel, initrd. */ + char *qemu; /* Qemu binary. */ + char *append; /* Append to kernel command line. */ char *last_error; @@ -220,10 +230,18 @@ guestfs_create (void) g->verbose = str != NULL && strcmp (str, "1") == 0; str = getenv ("LIBGUESTFS_PATH"); - g->path = str != NULL ? str : GUESTFS_DEFAULT_PATH; + g->path = str != NULL ? strdup (str) : strdup (GUESTFS_DEFAULT_PATH); + if (!g->path) goto error; str = getenv ("LIBGUESTFS_QEMU"); - g->qemu = str != NULL ? str : QEMU; + g->qemu = str != NULL ? strdup (str) : strdup (QEMU); + if (!g->qemu) goto error; + + str = getenv ("LIBGUESTFS_APPEND"); + if (str) { + g->append = strdup (str); + if (!g->append) goto error; + } g->main_loop = guestfs_get_default_main_loop (); @@ -248,6 +266,13 @@ guestfs_create (void) fprintf (stderr, "new guestfs handle %p\n", g); return g; + + error: + free (g->path); + free (g->qemu); + free (g->append); + free (g); + return NULL; } void @@ -259,7 +284,7 @@ guestfs_close (guestfs_h *g) if (g->state == NO_HANDLE) { /* Not safe to call 'error' here, so ... */ - fprintf (stderr, "guestfs_close: called twice on the same handle\n"); + fprintf (stderr, _("guestfs_close: called twice on the same handle\n")); return; } @@ -308,7 +333,12 @@ guestfs_close (guestfs_h *g) } /* release mutex (XXX) */ + free (g->msg_in); + free (g->msg_out); free (g->last_error); + free (g->path); + free (g->qemu); + free (g->append); free (g); } @@ -335,7 +365,7 @@ set_last_error (guestfs_h *g, const char *msg) static void default_error_cb (guestfs_h *g, void *data, const char *msg) { - fprintf (stderr, "libguestfs: error: %s\n", msg); + fprintf (stderr, _("libguestfs: error: %s\n"), msg); } void @@ -388,7 +418,7 @@ void * guestfs_safe_malloc (guestfs_h *g, size_t nbytes) { void *ptr = malloc (nbytes); - if (!ptr) g->abort_cb (); + if (nbytes > 0 && !ptr) g->abort_cb (); return ptr; } @@ -396,7 +426,7 @@ void * guestfs_safe_realloc (guestfs_h *g, void *ptr, int nbytes) { void *p = realloc (ptr, nbytes); - if (!p) g->abort_cb (); + if (nbytes > 0 && !p) g->abort_cb (); return p; } @@ -509,10 +539,12 @@ guestfs_get_autosync (guestfs_h *g) int guestfs_set_path (guestfs_h *g, const char *path) { - if (path == NULL) - g->path = GUESTFS_DEFAULT_PATH; - else - g->path = path; + free (g->path); + g->path = NULL; + + g->path = + path == NULL ? + safe_strdup (g, GUESTFS_DEFAULT_PATH) : safe_strdup (g, path); return 0; } @@ -525,10 +557,10 @@ guestfs_get_path (guestfs_h *g) int guestfs_set_qemu (guestfs_h *g, const char *qemu) { - if (qemu == NULL) - g->qemu = QEMU; - else - g->qemu = qemu; + free (g->qemu); + g->qemu = NULL; + + g->qemu = qemu == NULL ? safe_strdup (g, QEMU) : safe_strdup (g, qemu); return 0; } @@ -538,6 +570,22 @@ guestfs_get_qemu (guestfs_h *g) return g->qemu; } +int +guestfs_set_append (guestfs_h *g, const char *append) +{ + free (g->append); + g->append = NULL; + + g->append = append ? safe_strdup (g, append) : NULL; + return 0; +} + +const char * +guestfs_get_append (guestfs_h *g) +{ + return g->append; +} + /* Add a string to the current command line. */ static void incr_cmdline_size (guestfs_h *g) @@ -557,7 +605,8 @@ static int add_cmdline (guestfs_h *g, const char *str) { if (g->state != CONFIG) { - error (g, "command line cannot be altered after qemu subprocess launched"); + error (g, + _("command line cannot be altered after qemu subprocess launched")); return -1; } @@ -571,7 +620,7 @@ guestfs_config (guestfs_h *g, const char *qemu_param, const char *qemu_value) { if (qemu_param[0] != '-') { - error (g, "guestfs_config: parameter must begin with '-' character"); + error (g, _("guestfs_config: parameter must begin with '-' character")); return -1; } @@ -586,7 +635,7 @@ guestfs_config (guestfs_h *g, strcmp (qemu_param, "-full-screen") == 0 || strcmp (qemu_param, "-std-vga") == 0 || strcmp (qemu_param, "-vnc") == 0) { - error (g, "guestfs_config: parameter '%s' isn't allowed", qemu_param); + error (g, _("guestfs_config: parameter '%s' isn't allowed"), qemu_param); return -1; } @@ -606,7 +655,7 @@ guestfs_add_drive (guestfs_h *g, const char *filename) char buf[len]; if (strchr (filename, ',') != NULL) { - error (g, "filename cannot contain ',' (comma) character"); + error (g, _("filename cannot contain ',' (comma) character")); return -1; } @@ -621,10 +670,31 @@ guestfs_add_drive (guestfs_h *g, const char *filename) } int +guestfs_add_drive_ro (guestfs_h *g, const char *filename) +{ + size_t len = strlen (filename) + 64; + char buf[len]; + + if (strchr (filename, ',') != NULL) { + error (g, _("filename cannot contain ',' (comma) character")); + return -1; + } + + if (access (filename, F_OK) == -1) { + perrorf (g, "%s", filename); + return -1; + } + + snprintf (buf, len, "file=%s,snapshot=on", filename); + + return guestfs_config (g, "-drive", buf); +} + +int guestfs_add_cdrom (guestfs_h *g, const char *filename) { if (strchr (filename, ',') != NULL) { - error (g, "filename cannot contain ',' (comma) character"); + error (g, _("filename cannot contain ',' (comma) character")); return -1; } @@ -653,12 +723,12 @@ guestfs_launch (guestfs_h *g) /* Configured? */ if (!g->cmdline) { - error (g, "you must call guestfs_add_drive before guestfs_launch"); + error (g, _("you must call guestfs_add_drive before guestfs_launch")); return -1; } if (g->state != CONFIG) { - error (g, "qemu has already been launched"); + error (g, _("qemu has already been launched")); return -1; } @@ -704,7 +774,7 @@ guestfs_launch (guestfs_h *g) free (path); if (kernel == NULL || initrd == NULL) { - error (g, "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)", + error (g, _("cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)"), kernel_name, initrd_name, g->path); goto cleanup0; } @@ -722,7 +792,7 @@ guestfs_launch (guestfs_h *g) if (!g->tmpdir) { g->tmpdir = safe_strdup (g, dir_template); if (mkdtemp (g->tmpdir) == NULL) { - perrorf (g, "%s: cannot create temporary directory", dir_template); + perrorf (g, _("%s: cannot create temporary directory"), dir_template); goto cleanup0; } } @@ -753,7 +823,7 @@ guestfs_launch (guestfs_h *g) /* Set up the full command line. Do this in the subprocess so we * don't need to worry about cleaning up. */ - g->cmdline[0] = (char *) g->qemu; + g->cmdline[0] = g->qemu; /* Construct the -net channel parameter for qemu. */ snprintf (vmchannel, sizeof vmchannel, @@ -762,9 +832,10 @@ guestfs_launch (guestfs_h *g) /* Linux kernel command line. */ snprintf (append, sizeof append, - "panic=1 console=ttyS0 guestfs=%s:%d%s", + "panic=1 console=ttyS0 guestfs=%s:%d%s%s%s", VMCHANNEL_ADDR, VMCHANNEL_PORT, - g->verbose ? " guestfs_verbose=1" : ""); + g->verbose ? " guestfs_verbose=1" : "", + g->append ? " " : "", g->append ? g->append : ""); snprintf (memsize_str, sizeof memsize_str, "%d", memsize); @@ -824,6 +895,11 @@ guestfs_launch (guestfs_h *g) /* Parent (library). */ g->pid = r; + free (kernel); + kernel = NULL; + free (initrd); + initrd = NULL; + /* Fork the recovery process off which will kill qemu if the parent * process fails to do so (eg. if the parent segfaults). */ @@ -899,10 +975,9 @@ guestfs_launch (guestfs_h *g) addr.sun_path[UNIX_PATH_MAX-1] = '\0'; tries = 100; + /* Always sleep at least once to give qemu a small chance to start up. */ + usleep (10000); while (tries > 0) { - /* Always sleep at least once to give qemu a small chance to start up. */ - usleep (10000); - r = connect (g->sock, (struct sockaddr *) &addr, sizeof addr); if ((r == -1 && errno == EINPROGRESS) || r == 0) goto connected; @@ -910,9 +985,10 @@ guestfs_launch (guestfs_h *g) if (errno != ENOENT) perrorf (g, "connect"); tries--; + usleep (100000); } - error (g, "failed to connect to vmchannel socket"); + error (g, _("failed to connect to vmchannel socket")); goto cleanup2; connected: @@ -931,7 +1007,7 @@ guestfs_launch (guestfs_h *g) GUESTFS_HANDLE_READABLE, stdout_event, NULL); if (g->stdout_watch == -1) { - error (g, "could not watch qemu stdout"); + error (g, _("could not watch qemu stdout")); goto cleanup3; } @@ -990,12 +1066,12 @@ guestfs_wait_ready (guestfs_h *g) if (g->state == READY) return 0; if (g->state == BUSY) { - error (g, "qemu has finished launching already"); + error (g, _("qemu has finished launching already")); return -1; } if (g->state != LAUNCHING) { - error (g, "qemu has not been launched yet"); + error (g, _("qemu has not been launched yet")); return -1; } @@ -1008,7 +1084,7 @@ guestfs_wait_ready (guestfs_h *g) if (r == -1) return -1; if (finished != 1) { - error (g, "guestfs_wait_ready failed, see earlier error messages"); + error (g, _("guestfs_wait_ready failed, see earlier error messages")); return -1; } @@ -1018,7 +1094,7 @@ guestfs_wait_ready (guestfs_h *g) * commands after this function returns. */ if (g->state != READY) { - error (g, "qemu launched and contacted daemon, but state != READY"); + error (g, _("qemu launched and contacted daemon, but state != READY")); return -1; } @@ -1029,7 +1105,7 @@ int guestfs_kill_subprocess (guestfs_h *g) { if (g->state == CONFIG) { - error (g, "no subprocess to kill"); + error (g, _("no subprocess to kill")); return -1; } @@ -1077,7 +1153,8 @@ int guestfs_set_ready (guestfs_h *g) { if (g->state != BUSY) { - error (g, "guestfs_set_ready: called when in state %d != BUSY", g->state); + error (g, _("guestfs_set_ready: called when in state %d != BUSY"), + g->state); return -1; } g->state = READY; @@ -1088,7 +1165,8 @@ int guestfs_set_busy (guestfs_h *g) { if (g->state != READY) { - error (g, "guestfs_set_busy: called when in state %d != READY", g->state); + error (g, _("guestfs_set_busy: called when in state %d != READY"), + g->state); return -1; } g->state = BUSY; @@ -1108,7 +1186,7 @@ guestfs_end_busy (guestfs_h *g) break; case LAUNCHING: case NO_HANDLE: - error (g, "guestfs_end_busy: called when in state %d", g->state); + error (g, _("guestfs_end_busy: called when in state %d"), g->state); return -1; } return 0; @@ -1197,7 +1275,7 @@ stdout_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, #endif if (g->fd[1] != fd) { - error (g, "stdout_event: internal error: %d != %d", g->fd[1], fd); + error (g, _("stdout_event: internal error: %d != %d"), g->fd[1], fd); return; } @@ -1240,7 +1318,7 @@ sock_read_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, g, g->state, fd, events); if (g->sock != fd) { - error (g, "sock_read_event: internal error: %d != %d", g->sock, fd); + error (g, _("sock_read_event: internal error: %d != %d"), g->sock, fd); return; } @@ -1270,7 +1348,7 @@ sock_read_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, xdrmem_create (&xdr, g->msg_in, g->msg_in_size, XDR_DECODE); if (!xdr_uint32_t (&xdr, &len)) { - error (g, "can't decode length word"); + error (g, _("can't decode length word")); goto cleanup; } @@ -1280,10 +1358,10 @@ sock_read_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, */ if (len == GUESTFS_LAUNCH_FLAG) { if (g->state != LAUNCHING) - error (g, "received magic signature from guestfsd, but in state %d", + error (g, _("received magic signature from guestfsd, but in state %d"), g->state); else if (g->msg_in_size != 4) - error (g, "received magic signature from guestfsd, but msg size is %d", + error (g, _("received magic signature from guestfsd, but msg size is %d"), g->msg_in_size); else { g->state = READY; @@ -1308,7 +1386,7 @@ sock_read_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, * synchronization. */ if (len > GUESTFS_MESSAGE_MAX) { - error (g, "message length (%u) > maximum possible size (%d)", + error (g, _("message length (%u) > maximum possible size (%d)"), len, GUESTFS_MESSAGE_MAX); goto cleanup; } @@ -1341,7 +1419,7 @@ sock_read_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, /* Not in the expected state. */ if (g->state != BUSY) - error (g, "state %d != BUSY", g->state); + error (g, _("state %d != BUSY"), g->state); /* Push the message up to the higher layer. */ if (g->reply_cb) @@ -1381,12 +1459,12 @@ sock_write_event (struct guestfs_main_loop *ml, guestfs_h *g, void *data, g, g->state, fd, events); if (g->sock != fd) { - error (g, "sock_write_event: internal error: %d != %d", g->sock, fd); + error (g, _("sock_write_event: internal error: %d != %d"), g->sock, fd); return; } if (g->state != BUSY) { - error (g, "sock_write_event: state %d != BUSY", g->state); + error (g, _("sock_write_event: state %d != BUSY"), g->state); return; } @@ -1490,7 +1568,7 @@ guestfs__switch_to_sending (guestfs_h *g) { if (g->sock_watch >= 0) { if (g->main_loop->remove_handle (g->main_loop, g, g->sock_watch) == -1) { - error (g, "remove_handle failed"); + error (g, _("remove_handle failed")); g->sock_watch = -1; return -1; } @@ -1501,7 +1579,7 @@ guestfs__switch_to_sending (guestfs_h *g) GUESTFS_HANDLE_WRITABLE, sock_write_event, NULL); if (g->sock_watch == -1) { - error (g, "add_handle failed"); + error (g, _("add_handle failed")); return -1; } @@ -1513,7 +1591,7 @@ guestfs__switch_to_receiving (guestfs_h *g) { if (g->sock_watch >= 0) { if (g->main_loop->remove_handle (g->main_loop, g, g->sock_watch) == -1) { - error (g, "remove_handle failed"); + error (g, _("remove_handle failed")); g->sock_watch = -1; return -1; } @@ -1524,7 +1602,7 @@ guestfs__switch_to_receiving (guestfs_h *g) GUESTFS_HANDLE_READABLE, sock_read_event, NULL); if (g->sock_watch == -1) { - error (g, "add_handle failed"); + error (g, _("add_handle failed")); return -1; } @@ -1557,7 +1635,7 @@ guestfs__send_sync (guestfs_h *g, int proc_nr, guestfs_main_loop *ml = guestfs_get_main_loop (g); if (g->state != BUSY) { - error (g, "guestfs__send_sync: state %d != BUSY", g->state); + error (g, _("guestfs__send_sync: state %d != BUSY"), g->state); return -1; } @@ -1565,7 +1643,7 @@ guestfs__send_sync (guestfs_h *g, int proc_nr, * free the buffer anyway? */ if (g->msg_out != NULL) { - error (g, "guestfs__send_sync: msg_out should be NULL"); + error (g, _("guestfs__send_sync: msg_out should be NULL")); return -1; } @@ -1587,7 +1665,7 @@ guestfs__send_sync (guestfs_h *g, int proc_nr, hdr.status = GUESTFS_STATUS_OK; if (!xdr_guestfs_message_header (&xdr, &hdr)) { - error (g, "xdr_guestfs_message_header failed"); + error (g, _("xdr_guestfs_message_header failed")); goto cleanup1; } @@ -1596,7 +1674,7 @@ guestfs__send_sync (guestfs_h *g, int proc_nr, */ if (xdrp) { if (!(*xdrp) (&xdr, args)) { - error (g, "dispatch failed to marshal args"); + error (g, _("dispatch failed to marshal args")); goto cleanup1; } } @@ -1622,7 +1700,7 @@ guestfs__send_sync (guestfs_h *g, int proc_nr, if (ml->main_loop_run (ml, g) == -1) goto cleanup1; if (sent != 1) { - error (g, "send failed, see earlier error messages"); + error (g, _("send failed, see earlier error messages")); goto cleanup1; } @@ -1737,7 +1815,7 @@ send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t buflen) guestfs_main_loop *ml = guestfs_get_main_loop (g); if (g->state != BUSY) { - error (g, "send_file_chunk_sync: state %d != READY", g->state); + error (g, _("send_file_chunk_sync: state %d != READY"), g->state); return -1; } @@ -1745,7 +1823,7 @@ send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t buflen) * free the buffer anyway? */ if (g->msg_out != NULL) { - error (g, "guestfs__send_sync: msg_out should be NULL"); + error (g, _("guestfs__send_sync: msg_out should be NULL")); return -1; } @@ -1768,7 +1846,7 @@ send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t buflen) chunk.data.data_val = (char *) buf; if (!xdr_guestfs_chunk (&xdr, &chunk)) { - error (g, "xdr_guestfs_chunk failed (buf = %p, buflen = %zu)", + error (g, _("xdr_guestfs_chunk failed (buf = %p, buflen = %zu)"), buf, buflen); xdr_destroy (&xdr); goto cleanup1; @@ -1793,7 +1871,7 @@ send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t buflen) if (ml->main_loop_run (ml, g) == -1) goto cleanup1; if (sent != 1) { - error (g, "send file chunk failed, see earlier error messages"); + error (g, _("send file chunk failed, see earlier error messages")); goto cleanup1; } @@ -1844,7 +1922,7 @@ check_for_daemon_cancellation (guestfs_h *g) xdr_destroy (&xdr); if (flag != GUESTFS_CANCEL_FLAG) { - error (g, "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n", + error (g, _("check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n"), flag, GUESTFS_CANCEL_FLAG); return 0; } @@ -1882,7 +1960,7 @@ guestfs__receive_file_sync (guestfs_h *g, const char *filename) } if (r == -1) { - error (g, "%s: error in chunked encoding", filename); + error (g, _("%s: error in chunked encoding"), filename); return -1; } @@ -1906,7 +1984,7 @@ guestfs__receive_file_sync (guestfs_h *g, const char *filename) xdr_destroy (&xdr); if (xwrite (g->sock, fbuf, sizeof fbuf) == -1) { - perrorf (g, "write to daemon socket"); + perrorf (g, _("write to daemon socket")); return -1; } @@ -1953,7 +2031,7 @@ receive_file_cb (guestfs_h *g, void *data, XDR *xdr) memset (&chunk, 0, sizeof chunk); if (!xdr_guestfs_chunk (xdr, &chunk)) { - error (g, "failed to parse file chunk"); + error (g, _("failed to parse file chunk")); free_chunks (ctx); ctx->chunks = NULL; ctx->count = -1; @@ -1985,12 +2063,12 @@ receive_file_data_sync (guestfs_h *g, void **buf, size_t *len_r) guestfs_set_reply_callback (g, NULL, NULL); if (ctx.count == 0) { - error (g, "receive_file_data_sync: reply callback not called\n"); + error (g, _("receive_file_data_sync: reply callback not called\n")); return -1; } if (ctx.count == -1) { - error (g, "receive_file_data_sync: parse error in reply callback\n"); + error (g, _("receive_file_data_sync: parse error in reply callback\n")); /* callback already freed the chunks */ return -1; } @@ -2004,7 +2082,7 @@ receive_file_data_sync (guestfs_h *g, void **buf, size_t *len_r) for (i = 0; i < ctx.count; ++i) { if (ctx.chunks[i].cancel) { - error (g, "file receive cancelled by daemon"); + error (g, _("file receive cancelled by daemon")); free_chunks (&ctx); if (buf) free (*buf); if (len_r) *len_r = 0; @@ -2039,7 +2117,7 @@ select_add_handle (guestfs_main_loop *mlv, guestfs_h *g, int fd, int events, struct select_main_loop *ml = (struct select_main_loop *) mlv; if (fd < 0 || fd >= FD_SETSIZE) { - error (g, "fd %d is out of range", fd); + error (g, _("fd %d is out of range"), fd); return -1; } @@ -2047,24 +2125,24 @@ select_add_handle (guestfs_main_loop *mlv, guestfs_h *g, int fd, int events, GUESTFS_HANDLE_WRITABLE | GUESTFS_HANDLE_HANGUP | GUESTFS_HANDLE_ERROR)) != 0) { - error (g, "set of events (0x%x) contains unknown events", events); + error (g, _("set of events (0x%x) contains unknown events"), events); return -1; } if (events == 0) { - error (g, "set of events is empty"); + error (g, _("set of events is empty")); return -1; } if (FD_ISSET (fd, &ml->rset) || FD_ISSET (fd, &ml->wset) || FD_ISSET (fd, &ml->xset)) { - error (g, "fd %d is already registered", fd); + error (g, _("fd %d is already registered"), fd); return -1; } if (cb == NULL) { - error (g, "callback is NULL"); + error (g, _("callback is NULL")); return -1; } @@ -2097,14 +2175,14 @@ select_remove_handle (guestfs_main_loop *mlv, guestfs_h *g, int fd) struct select_main_loop *ml = (struct select_main_loop *) mlv; if (fd < 0 || fd >= FD_SETSIZE) { - error (g, "fd %d is out of range", fd); + error (g, _("fd %d is out of range"), fd); return -1; } if (!FD_ISSET (fd, &ml->rset) && !FD_ISSET (fd, &ml->wset) && !FD_ISSET (fd, &ml->xset)) { - error (g, "fd %d was not registered", fd); + error (g, _("fd %d was not registered"), fd); return -1; } @@ -2153,7 +2231,7 @@ select_main_loop_run (guestfs_main_loop *mlv, guestfs_h *g) fd_set rset2, wset2, xset2; if (ml->is_running) { - error (g, "select_main_loop_run: this cannot be called recursively"); + error (g, _("select_main_loop_run: this cannot be called recursively")); return -1; } @@ -2168,6 +2246,8 @@ select_main_loop_run (guestfs_main_loop *mlv, guestfs_h *g) xset2 = ml->xset; r = select (ml->max_fd+1, &rset2, &wset2, &xset2, NULL); if (r == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; perrorf (g, "select"); ml->is_running = 0; return -1;