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