1 /*
2  * Copyright (C) 2010      Citrix Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; version 2.1 only. with the special
7  * exception on linking described in file LICENSE.
8  *
9  * This program 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
12  * GNU Lesser General Public License for more details.
13  */
14 
15 #include "libxl_osdeps.h" /* must come before any other headers */
16 
17 #include "libxl_internal.h"
18 
19 /*----- xswait -----*/
20 
21 static libxl__ev_xswatch_callback xswait_xswatch_callback;
22 static libxl__ev_time_callback xswait_timeout_callback;
23 static void xswait_report_error(libxl__egc*, libxl__xswait_state*, int rc);
24 
libxl__xswait_init(libxl__xswait_state * xswa)25 void libxl__xswait_init(libxl__xswait_state *xswa)
26 {
27     libxl__ev_time_init(&xswa->time_ev);
28     libxl__ev_xswatch_init(&xswa->watch_ev);
29 }
30 
libxl__xswait_stop(libxl__gc * gc,libxl__xswait_state * xswa)31 void libxl__xswait_stop(libxl__gc *gc, libxl__xswait_state *xswa)
32 {
33     libxl__ev_time_deregister(gc, &xswa->time_ev);
34     libxl__ev_xswatch_deregister(gc, &xswa->watch_ev);
35 }
36 
libxl__xswait_inuse(const libxl__xswait_state * xswa)37 bool libxl__xswait_inuse(const libxl__xswait_state *xswa)
38 {
39     bool time_inuse = libxl__ev_time_isregistered(&xswa->time_ev);
40     bool watch_inuse = libxl__ev_xswatch_isregistered(&xswa->watch_ev);
41     assert(time_inuse == watch_inuse);
42     return time_inuse;
43 }
44 
libxl__xswait_start(libxl__gc * gc,libxl__xswait_state * xswa)45 int libxl__xswait_start(libxl__gc *gc, libxl__xswait_state *xswa)
46 {
47     int rc;
48 
49     rc = libxl__ev_time_register_rel(xswa->ao, &xswa->time_ev,
50                                      xswait_timeout_callback, xswa->timeout_ms);
51     if (rc) goto err;
52 
53     rc = libxl__ev_xswatch_register(gc, &xswa->watch_ev,
54                                     xswait_xswatch_callback, xswa->path);
55     if (rc) goto err;
56 
57     return 0;
58 
59  err:
60     libxl__xswait_stop(gc, xswa);
61     return rc;
62 }
63 
xswait_xswatch_callback(libxl__egc * egc,libxl__ev_xswatch * xsw,const char * watch_path,const char * event_path)64 void xswait_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *xsw,
65                              const char *watch_path, const char *event_path)
66 {
67     EGC_GC;
68     libxl__xswait_state *xswa = CONTAINER_OF(xsw, *xswa, watch_ev);
69     int rc;
70     const char *data;
71 
72     if (xswa->path[0] == '@') {
73         data = 0;
74     } else {
75         rc = libxl__xs_read_checked(gc, XBT_NULL, xswa->path, &data);
76         if (rc) { xswait_report_error(egc, xswa, rc); return; }
77     }
78 
79     xswa->callback(egc, xswa, 0, data);
80 }
81 
xswait_timeout_callback(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)82 void xswait_timeout_callback(libxl__egc *egc, libxl__ev_time *ev,
83                              const struct timeval *requested_abs,
84                              int rc)
85 {
86     EGC_GC;
87     libxl__xswait_state *xswa = CONTAINER_OF(ev, *xswa, time_ev);
88     LOG(DEBUG, "%s: xswait timeout (path=%s)", xswa->what, xswa->path);
89     xswait_report_error(egc, xswa, rc);
90 }
91 
xswait_report_error(libxl__egc * egc,libxl__xswait_state * xswa,int rc)92 static void xswait_report_error(libxl__egc *egc, libxl__xswait_state *xswa,
93                                 int rc)
94 {
95     EGC_GC;
96     libxl__xswait_stop(gc, xswa);
97     xswa->callback(egc, xswa, rc, 0);
98 }
99 
100 
101 /*----- data copier -----*/
102 
libxl__datacopier_init(libxl__datacopier_state * dc)103 void libxl__datacopier_init(libxl__datacopier_state *dc)
104 {
105     assert(dc->ao);
106     libxl__ao_abortable_init(&dc->abrt);
107     libxl__ev_fd_init(&dc->toread);
108     libxl__ev_fd_init(&dc->towrite);
109     LIBXL_TAILQ_INIT(&dc->bufs);
110 }
111 
libxl__datacopier_kill(libxl__datacopier_state * dc)112 void libxl__datacopier_kill(libxl__datacopier_state *dc)
113 {
114     STATE_AO_GC(dc->ao);
115     libxl__datacopier_buf *buf, *tbuf;
116 
117     libxl__ao_abortable_deregister(&dc->abrt);
118     libxl__ev_fd_deregister(gc, &dc->toread);
119     libxl__ev_fd_deregister(gc, &dc->towrite);
120     LIBXL_TAILQ_FOREACH_SAFE(buf, &dc->bufs, entry, tbuf)
121         free(buf);
122     LIBXL_TAILQ_INIT(&dc->bufs);
123 }
124 
datacopier_callback(libxl__egc * egc,libxl__datacopier_state * dc,int rc,int onwrite,int errnoval)125 static void datacopier_callback(libxl__egc *egc, libxl__datacopier_state *dc,
126                                 int rc, int onwrite, int errnoval)
127 {
128     libxl__datacopier_kill(dc);
129     dc->callback(egc, dc, rc, onwrite, errnoval);
130 }
131 
132 static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
133                                 int fd, short events, short revents);
134 
datacopier_check_state(libxl__egc * egc,libxl__datacopier_state * dc)135 static void datacopier_check_state(libxl__egc *egc, libxl__datacopier_state *dc)
136 {
137     STATE_AO_GC(dc->ao);
138     int rc;
139 
140     if (dc->used && !dc->readbuf) {
141         if (!libxl__ev_fd_isregistered(&dc->towrite)) {
142             rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
143                                        dc->writefd, POLLOUT);
144             if (rc) {
145                 LOG(ERROR, "unable to establish write event on %s"
146                     " during copy of %s", dc->writewhat, dc->copywhat);
147                 datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
148                 return;
149             }
150         }
151     } else if (!libxl__ev_fd_isregistered(&dc->toread) ||
152                dc->bytes_to_read == 0) {
153         /* we have had eof */
154         datacopier_callback(egc, dc, 0, 0, 0);
155         return;
156     } else {
157         /* nothing buffered, but still reading */
158         libxl__ev_fd_deregister(gc, &dc->towrite);
159     }
160 }
161 
libxl__datacopier_prefixdata(libxl__egc * egc,libxl__datacopier_state * dc,const void * data,size_t len)162 void libxl__datacopier_prefixdata(libxl__egc *egc, libxl__datacopier_state *dc,
163                                   const void *data, size_t len)
164 {
165     EGC_GC;
166     libxl__datacopier_buf *buf;
167     const uint8_t *ptr;
168 
169     /*
170      * It is safe for this to be called immediately after _start, as
171      * is documented in the public comment.  _start's caller must have
172      * the ctx locked, so other threads don't get to mess with the
173      * contents, and the fd events cannot happen reentrantly.  So we
174      * are guaranteed to beat the first data from the read fd.
175      */
176 
177     assert(len < dc->maxsz - dc->used);
178 
179     for (ptr = data; len; len -= buf->used, ptr += buf->used) {
180         buf = libxl__malloc(NOGC, sizeof(*buf));
181         buf->used = min(len, sizeof(buf->buf));
182         memcpy(buf->buf, ptr, buf->used);
183 
184         dc->used += buf->used;
185         LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
186     }
187 }
188 
datacopier_pollhup_handled(libxl__egc * egc,libxl__datacopier_state * dc,int fd,short revents,int onwrite)189 static int datacopier_pollhup_handled(libxl__egc *egc,
190                                       libxl__datacopier_state *dc,
191                                       int fd, short revents, int onwrite)
192 {
193     STATE_AO_GC(dc->ao);
194 
195     if (dc->callback_pollhup && (revents & POLLHUP)) {
196         LOG(DEBUG, "received POLLHUP on fd %d: %s during copy of %s",
197             fd, onwrite ? dc->writewhat : dc->readwhat, dc->copywhat);
198         libxl__datacopier_kill(dc);
199         dc->callback_pollhup(egc, dc, ERROR_FAIL, onwrite, -1);
200         return 1;
201     }
202     return 0;
203 }
204 
datacopier_abort(libxl__egc * egc,libxl__ao_abortable * abrt,int rc)205 static void datacopier_abort(libxl__egc *egc, libxl__ao_abortable *abrt,
206                              int rc)
207 {
208     libxl__datacopier_state *dc = CONTAINER_OF(abrt, *dc, abrt);
209     STATE_AO_GC(dc->ao);
210 
211     datacopier_callback(egc, dc, rc, -1, 0);
212 }
213 
datacopier_readable(libxl__egc * egc,libxl__ev_fd * ev,int fd,short events,short revents)214 static void datacopier_readable(libxl__egc *egc, libxl__ev_fd *ev,
215                                 int fd, short events, short revents) {
216     libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, toread);
217     STATE_AO_GC(dc->ao);
218 
219     if (datacopier_pollhup_handled(egc, dc, fd, revents, 0))
220         return;
221 
222     if (revents & ~(POLLIN|POLLHUP)) {
223         LOG(ERROR, "unexpected poll event 0x%x on fd %d (expected POLLIN "
224             "and/or POLLHUP) reading %s during copy of %s",
225             revents, fd, dc->readwhat, dc->copywhat);
226         datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
227         return;
228     }
229     assert(revents & (POLLIN|POLLHUP));
230     for (;;) {
231         libxl__datacopier_buf *buf = NULL;
232         int r;
233 
234         if (dc->readbuf) {
235             r = read(ev->fd, dc->readbuf + dc->used, dc->bytes_to_read);
236         } else {
237             while (dc->used >= dc->maxsz) {
238                 libxl__datacopier_buf *rm = LIBXL_TAILQ_FIRST(&dc->bufs);
239                 dc->used -= rm->used;
240                 assert(dc->used >= 0);
241                 LIBXL_TAILQ_REMOVE(&dc->bufs, rm, entry);
242                 free(rm);
243             }
244 
245             buf = LIBXL_TAILQ_LAST(&dc->bufs, libxl__datacopier_bufs);
246             if (!buf || buf->used >= sizeof(buf->buf)) {
247                 buf = libxl__malloc(NOGC, sizeof(*buf));
248                 buf->used = 0;
249                 LIBXL_TAILQ_INSERT_TAIL(&dc->bufs, buf, entry);
250             }
251             r = read(ev->fd, buf->buf + buf->used,
252                      min_t(size_t, sizeof(buf->buf) - buf->used,
253                            (dc->bytes_to_read == -1) ? SIZE_MAX : dc->bytes_to_read));
254         }
255         if (r < 0) {
256             if (errno == EINTR) continue;
257             assert(errno);
258             if (errno == EWOULDBLOCK) {
259                 if (revents & POLLHUP) {
260                     LOG(ERROR,
261                         "poll reported HUP but fd read gave EWOULDBLOCK"
262                         " on %s during copy of %s",
263                         dc->readwhat, dc->copywhat);
264                     datacopier_callback(egc, dc, ERROR_FAIL, -1, 0);
265                     return;
266                 }
267                 break;
268             }
269             LOGE(ERROR, "error reading %s during copy of %s",
270                  dc->readwhat, dc->copywhat);
271             datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
272             return;
273         }
274         if (r == 0) {
275             if (dc->callback_pollhup) {
276                 /* It might be that this "eof" is actually a HUP.  If
277                  * the caller cares about the difference,
278                  * double-check using poll(2). */
279                 struct pollfd hupchk;
280                 hupchk.fd = ev->fd;
281                 hupchk.events = POLLIN;
282                 hupchk.revents = 0;
283                 r = poll(&hupchk, 1, 0);
284                 if (r < 0)
285                     LIBXL__EVENT_DISASTER(egc,
286      "unexpected failure polling fd for datacopier eof hup check",
287                                   errno, 0);
288                 if (datacopier_pollhup_handled(egc, dc, fd, hupchk.revents, 0))
289                     return;
290             }
291             libxl__ev_fd_deregister(gc, &dc->toread);
292             break;
293         }
294         if (dc->log) {
295             int wrote = fwrite(buf->buf + buf->used, 1, r, dc->log);
296             if (wrote != r) {
297                 assert(ferror(dc->log));
298                 assert(errno);
299                 LOGE(ERROR, "error logging %s", dc->copywhat);
300                 datacopier_callback(egc, dc, ERROR_FAIL, 0, errno);
301                 return;
302             }
303         }
304         if (!dc->readbuf) {
305             buf->used += r;
306             assert(buf->used <= sizeof(buf->buf));
307         }
308         dc->used += r;
309         if (dc->bytes_to_read > 0)
310             dc->bytes_to_read -= r;
311         if (dc->bytes_to_read == 0)
312             break;
313     }
314     datacopier_check_state(egc, dc);
315 }
316 
datacopier_writable(libxl__egc * egc,libxl__ev_fd * ev,int fd,short events,short revents)317 static void datacopier_writable(libxl__egc *egc, libxl__ev_fd *ev,
318                                 int fd, short events, short revents) {
319     libxl__datacopier_state *dc = CONTAINER_OF(ev, *dc, towrite);
320     STATE_AO_GC(dc->ao);
321 
322     if (datacopier_pollhup_handled(egc, dc, fd, revents, 1))
323         return;
324 
325     if (revents & ~POLLOUT) {
326         LOG(ERROR, "unexpected poll event 0x%x on fd %d (should be POLLOUT)"
327             " writing %s during copy of %s",
328             revents, fd, dc->writewhat, dc->copywhat);
329         datacopier_callback(egc, dc, ERROR_FAIL, -1, EIO);
330         return;
331     }
332     assert(revents & POLLOUT);
333     for (;;) {
334         libxl__datacopier_buf *buf = LIBXL_TAILQ_FIRST(&dc->bufs);
335         if (!buf)
336             break;
337         if (!buf->used) {
338             LIBXL_TAILQ_REMOVE(&dc->bufs, buf, entry);
339             free(buf);
340             continue;
341         }
342         int r = write(ev->fd, buf->buf, buf->used);
343         if (r < 0) {
344             if (errno == EINTR) continue;
345             if (errno == EWOULDBLOCK) break;
346             assert(errno);
347             LOGE(ERROR, "error writing to %s during copy of %s",
348                  dc->writewhat, dc->copywhat);
349             datacopier_callback(egc, dc, ERROR_FAIL, 1, errno);
350             return;
351         }
352         assert(r > 0);
353         assert(r <= buf->used);
354         buf->used -= r;
355         dc->used -= r;
356         assert(dc->used >= 0);
357         memmove(buf->buf, buf->buf+r, buf->used);
358     }
359     datacopier_check_state(egc, dc);
360 }
361 
libxl__datacopier_start(libxl__datacopier_state * dc)362 int libxl__datacopier_start(libxl__datacopier_state *dc)
363 {
364     int rc;
365     STATE_AO_GC(dc->ao);
366 
367     libxl__datacopier_init(dc);
368 
369     assert(dc->readfd >= 0 || dc->writefd >= 0);
370     assert(!(dc->readbuf && dc->bytes_to_read == -1));
371 
372     dc->abrt.ao = ao;
373     dc->abrt.callback = datacopier_abort;
374     rc = libxl__ao_abortable_register(&dc->abrt);
375     if (rc) goto out;
376 
377     if (dc->readfd >= 0) {
378         rc = libxl__ev_fd_register(gc, &dc->toread, datacopier_readable,
379                                    dc->readfd, POLLIN);
380         if (rc) goto out;
381     }
382 
383     if (dc->writefd >= 0) {
384         rc = libxl__ev_fd_register(gc, &dc->towrite, datacopier_writable,
385                                    dc->writefd, POLLOUT);
386         if (rc) goto out;
387     }
388 
389     return 0;
390 
391  out:
392     libxl__datacopier_kill(dc);
393     return rc;
394 }
395 
396 /*----- openpty -----*/
397 
398 /* implementation */
399 
openpty_cleanup(libxl__openpty_state * op)400 static void openpty_cleanup(libxl__openpty_state *op)
401 {
402     int i;
403 
404     for (i=0; i<op->count; i++) {
405         libxl__openpty_result *res = &op->results[i];
406         libxl__carefd_close(res->master);  res->master = 0;
407         libxl__carefd_close(res->slave);   res->slave = 0;
408     }
409 }
410 
openpty_exited(libxl__egc * egc,libxl__ev_child * child,pid_t pid,int status)411 static void openpty_exited(libxl__egc *egc, libxl__ev_child *child,
412                            pid_t pid, int status) {
413     libxl__openpty_state *op = CONTAINER_OF(child, *op, child);
414     STATE_AO_GC(op->ao);
415 
416     if (status) {
417         /* Perhaps the child gave us the fds and then exited nonzero.
418          * Well that would be odd but we don't really care. */
419         libxl_report_child_exitstatus(CTX, op->rc ? LIBXL__LOG_ERROR
420                                                   : LIBXL__LOG_WARNING,
421                                       "openpty child", pid, status);
422     }
423     if (op->rc)
424         openpty_cleanup(op);
425     op->callback(egc, op);
426 }
427 
libxl__openptys(libxl__openpty_state * op,struct termios * termp,struct winsize * winp)428 int libxl__openptys(libxl__openpty_state *op,
429                     struct termios *termp,
430                     struct winsize *winp) {
431     /*
432      * This is completely crazy.  openpty calls grantpt which the spec
433      * says may fork, and may not be called with a SIGCHLD handler.
434      * Now our application may have a SIGCHLD handler so that's bad.
435      * We could perhaps block it but we'd need to block it on all
436      * threads.  This is just Too Hard.
437      *
438      * So instead, we run openpty in a child process.  That child
439      * process then of course has only our own thread and our own
440      * signal handlers.  We pass the fds back.
441      *
442      * Since our only current caller actually wants two ptys, we
443      * support calling openpty multiple times for a single fork.
444      */
445     STATE_AO_GC(op->ao);
446     int count = op->count;
447     int r, i, rc, sockets[2], ptyfds[count][2];
448     libxl__carefd *for_child = 0;
449     pid_t pid = -1;
450 
451     for (i=0; i<count; i++) {
452         ptyfds[i][0] = ptyfds[i][1] = -1;
453         libxl__openpty_result *res = &op->results[i];
454         assert(!res->master);
455         assert(!res->slave);
456     }
457     sockets[0] = sockets[1] = -1; /* 0 is for us, 1 for our child */
458 
459     libxl__carefd_begin();
460     r = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
461     if (r) { sockets[0] = sockets[1] = -1; }
462     for_child = libxl__carefd_opened(CTX, sockets[1]);
463     if (r) { LOGE(ERROR,"socketpair failed"); rc = ERROR_FAIL; goto out; }
464 
465     pid = libxl__ev_child_fork(gc, &op->child, openpty_exited);
466     if (pid == -1) {
467         rc = ERROR_FAIL;
468         goto out;
469     }
470 
471     if (!pid) {
472         /* child */
473         close(sockets[0]);
474         signal(SIGCHLD, SIG_DFL);
475 
476         for (i=0; i<count; i++) {
477             r = openpty(&ptyfds[i][0], &ptyfds[i][1], NULL, termp, winp);
478             if (r) { LOGE(ERROR,"openpty failed"); _exit(-1); }
479         }
480         rc = libxl__sendmsg_fds(gc, sockets[1], "",1,
481                                 2*count, &ptyfds[0][0], "ptys");
482         if (rc) { LOGE(ERROR,"sendmsg to parent failed"); _exit(-1); }
483         _exit(0);
484     }
485 
486     libxl__carefd_close(for_child);
487     for_child = 0;
488 
489     /* this should be fast so do it synchronously */
490 
491     libxl__carefd_begin();
492     char buf[1];
493     rc = libxl__recvmsg_fds(gc, sockets[0], buf,1,
494                             2*count, &ptyfds[0][0], "ptys");
495     if (!rc) {
496         for (i=0; i<count; i++) {
497             libxl__openpty_result *res = &op->results[i];
498             res->master = libxl__carefd_record(CTX, ptyfds[i][0]);
499             res->slave =  libxl__carefd_record(CTX, ptyfds[i][1]);
500         }
501     }
502     /* now the pty fds are in the carefds, if they were ever open */
503     libxl__carefd_unlock();
504     if (rc)
505         goto out;
506 
507     rc = 0;
508 
509  out:
510     if (sockets[0] >= 0) close(sockets[0]);
511     libxl__carefd_close(for_child);
512     if (libxl__ev_child_inuse(&op->child)) {
513         op->rc = rc;
514         /* we will get a callback when the child dies */
515         return 0;
516     }
517 
518     assert(rc);
519     openpty_cleanup(op);
520     return rc;
521 }
522 
523 /*----- async exec -----*/
524 
async_exec_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)525 static void async_exec_timeout(libxl__egc *egc,
526                                libxl__ev_time *ev,
527                                const struct timeval *requested_abs,
528                                int rc)
529 {
530     libxl__async_exec_state *aes = CONTAINER_OF(ev, *aes, time);
531     STATE_AO_GC(aes->ao);
532 
533     if (!aes->rc)
534         aes->rc = rc;
535 
536     libxl__ev_time_deregister(gc, &aes->time);
537 
538     assert(libxl__ev_child_inuse(&aes->child));
539     LOG(ERROR, "killing execution of %s because of timeout", aes->what);
540 
541     if (kill(aes->child.pid, SIGKILL)) {
542         LOGEV(ERROR, errno, "unable to kill %s [%ld]",
543               aes->what, (unsigned long)aes->child.pid);
544     }
545 
546     return;
547 }
548 
async_exec_done(libxl__egc * egc,libxl__ev_child * child,pid_t pid,int status)549 static void async_exec_done(libxl__egc *egc,
550                             libxl__ev_child *child,
551                             pid_t pid, int status)
552 {
553     libxl__async_exec_state *aes = CONTAINER_OF(child, *aes, child);
554     STATE_AO_GC(aes->ao);
555 
556     libxl__ev_time_deregister(gc, &aes->time);
557 
558     if (status) {
559         if (!aes->rc)
560             libxl_report_child_exitstatus(CTX, LIBXL__LOG_ERROR,
561                                           aes->what, pid, status);
562     }
563 
564     aes->callback(egc, aes, aes->rc, status);
565 }
566 
libxl__async_exec_init(libxl__async_exec_state * aes)567 void libxl__async_exec_init(libxl__async_exec_state *aes)
568 {
569     libxl__ev_time_init(&aes->time);
570     libxl__ev_child_init(&aes->child);
571 }
572 
libxl__async_exec_start(libxl__async_exec_state * aes)573 int libxl__async_exec_start(libxl__async_exec_state *aes)
574 {
575     pid_t pid;
576 
577     /* Convenience aliases */
578     libxl__ao *ao = aes->ao;
579     AO_GC;
580     libxl__ev_child *const child = &aes->child;
581     char ** const args = aes->args;
582 
583     aes->rc = 0;
584 
585     /* Set execution timeout */
586     if (libxl__ev_time_register_rel(ao, &aes->time,
587                                     async_exec_timeout,
588                                     aes->timeout_ms)) {
589         LOG(ERROR, "unable to register timeout for executing: %s", aes->what);
590         goto out;
591     }
592 
593     LOG(DEBUG, "forking to execute: %s ", aes->what);
594 
595     /* Fork and exec */
596     pid = libxl__ev_child_fork(gc, child, async_exec_done);
597     if (pid == -1) {
598         LOG(ERROR, "unable to fork");
599         goto out;
600     }
601 
602     if (!pid) {
603         /* child */
604         libxl__exec(gc, aes->stdfds[0], aes->stdfds[1],
605                     aes->stdfds[2], args[0], args, aes->env);
606     }
607 
608     return 0;
609 
610 out:
611     return ERROR_FAIL;
612 }
613 
libxl__async_exec_inuse(const libxl__async_exec_state * aes)614 bool libxl__async_exec_inuse(const libxl__async_exec_state *aes)
615 {
616     bool time_inuse = libxl__ev_time_isregistered(&aes->time);
617     bool child_inuse = libxl__ev_child_inuse(&aes->child);
618     assert(time_inuse == child_inuse);
619     return child_inuse;
620 }
621 
libxl__kill(libxl__gc * gc,pid_t pid,int sig,const char * what)622 void libxl__kill(libxl__gc *gc, pid_t pid, int sig, const char *what)
623 {
624     int r = kill(pid, sig);
625     if (r) LOGE(WARN, "failed to kill() %s [%lu] (signal %d)",
626                 what, (unsigned long)pid, sig);
627 }
628 
629 /*
630  * Local variables:
631  * mode: C
632  * c-basic-offset: 4
633  * indent-tabs-mode: nil
634  * End:
635  */
636