1 /*
2 * Copyright (C) 2012 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 * Internal child process machinery for use by other parts of libxl
16 */
17
18 #include "libxl_osdeps.h" /* must come before any other headers */
19
20 #include "libxl_internal.h"
21
22 /*
23 * carefd arrangements
24 *
25 * carefd_begin and _unlock take out the no_forking lock, which we
26 * also take and release in our pthread_atfork handlers. So when this
27 * lock is held the whole process cannot fork. We therefore protect
28 * our fds from leaking into children made by other threads.
29 *
30 * We maintain a list of all the carefds, so that if the application
31 * wants to fork a long-running but non-execing child, we can close
32 * them all.
33 *
34 * So the record function sets CLOEXEC for the benefit of execing
35 * children, and makes a note of the fd for the benefit of non-execing
36 * ones.
37 */
38
39 struct libxl__carefd {
40 LIBXL_LIST_ENTRY(libxl__carefd) entry;
41 int fd;
42 };
43
44 static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER;
45 static int atfork_registered;
46 static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
47 LIBXL_LIST_HEAD_INITIALIZER(carefds);
48
49 /* Protected against concurrency by no_forking. sigchld_users is
50 * protected against being interrupted by SIGCHLD (and thus read
51 * asynchronously by the signal handler) by sigchld_defer (see
52 * below). */
53 static bool sigchld_installed; /* 0 means not */
54 static pthread_mutex_t sigchld_defer_mutex = PTHREAD_MUTEX_INITIALIZER;
55 static LIBXL_LIST_HEAD(, libxl_ctx) sigchld_users =
56 LIBXL_LIST_HEAD_INITIALIZER(sigchld_users);
57 static struct sigaction sigchld_saved_action;
58
59 static void sigchld_removehandler_core(void); /* idempotent */
60 static void sigchld_user_remove(libxl_ctx *ctx); /* idempotent */
61 static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old);
62
63 static void defer_sigchld(void);
64 static void release_sigchld(void);
65
atfork_lock(void)66 static void atfork_lock(void)
67 {
68 int r = pthread_mutex_lock(&no_forking);
69 assert(!r);
70 }
71
atfork_unlock(void)72 static void atfork_unlock(void)
73 {
74 int r = pthread_mutex_unlock(&no_forking);
75 assert(!r);
76 }
77
libxl__atfork_init(libxl_ctx * ctx)78 int libxl__atfork_init(libxl_ctx *ctx)
79 {
80 int r, rc;
81
82 atfork_lock();
83 if (atfork_registered) { rc = 0; goto out; }
84
85 r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock);
86 if (r) {
87 assert(r == ENOMEM);
88 libxl__alloc_failed(ctx, __func__, 0,0);
89 }
90
91 atfork_registered = 1;
92 rc = 0;
93 out:
94 atfork_unlock();
95 return rc;
96 }
97
libxl__carefd_begin(void)98 void libxl__carefd_begin(void) { atfork_lock(); }
libxl__carefd_unlock(void)99 void libxl__carefd_unlock(void) { atfork_unlock(); }
100
libxl__carefd_record(libxl_ctx * ctx,int fd)101 libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd)
102 {
103 libxl__carefd *cf = 0;
104
105 libxl_fd_set_cloexec(ctx, fd, 1);
106 cf = libxl__zalloc(&ctx->nogc_gc, sizeof(*cf));
107 cf->fd = fd;
108 LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry);
109 return cf;
110 }
111
libxl__carefd_opened(libxl_ctx * ctx,int fd)112 libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd)
113 {
114 libxl__carefd *cf = 0;
115 int saved_errno = errno;
116
117 if (fd >= 0)
118 cf = libxl__carefd_record(ctx, fd);
119 libxl__carefd_unlock();
120 errno = saved_errno;
121 return cf;
122 }
123
libxl_postfork_child_noexec(libxl_ctx * ctx)124 void libxl_postfork_child_noexec(libxl_ctx *ctx)
125 {
126 /*
127 * Anything running without the no_forking lock (atfork_lock)
128 * might be interrupted by fork. But libxl functions other than
129 * this one are then forbidden to the child.
130 *
131 * Conversely, this function might interrupt any other libxl
132 * operation (even though that other operation has the libxl ctx
133 * lock). We don't take the lock ourselves, since we are running
134 * in the child and if the lock is held the thread that took it no
135 * longer exists. To prevent us being interrupted by another call
136 * to ourselves (whether in another thread or by virtue of another
137 * fork) we take the atfork lock ourselves.
138 */
139 libxl__carefd *cf, *cf_tmp;
140 int r;
141
142 atfork_lock();
143
144 LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) {
145 if (cf->fd >= 0) {
146 r = close(cf->fd);
147 if (r)
148 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING,
149 "failed to close fd=%d"
150 " (perhaps of another libxl ctx)", cf->fd);
151 }
152 free(cf);
153 }
154 LIBXL_LIST_INIT(&carefds);
155
156 if (sigchld_installed) {
157 /* We are in theory not at risk of concurrent execution of the
158 * SIGCHLD handler, because the application should call
159 * libxl_postfork_child_noexec before the child forks again.
160 * (If the SIGCHLD was in flight in the parent at the time of
161 * the fork, the thread it was delivered on exists only in the
162 * parent so is not our concern.)
163 *
164 * But in case the application violated this rule (and did so
165 * while multithreaded in the child), we use our deferral
166 * machinery. The result is that the SIGCHLD may then be lost
167 * (i.e. signaled to the now-defunct libxl ctx(s)). But at
168 * least we won't execute undefined behaviour (by examining
169 * the list in the signal handler concurrently with clearing
170 * it here), and since we won't actually reap the new children
171 * things will in fact go OK if the application doesn't try to
172 * use SIGCHLD, but instead just waits for the child(ren). */
173 defer_sigchld();
174
175 LIBXL_LIST_INIT(&sigchld_users);
176 /* After this the ->sigchld_user_registered entries in the
177 * now-obsolete contexts may be lies. But that's OK because
178 * no-one will look at them. */
179
180 release_sigchld();
181 sigchld_removehandler_core();
182 }
183
184 atfork_unlock();
185 }
186
libxl__carefd_close(libxl__carefd * cf)187 int libxl__carefd_close(libxl__carefd *cf)
188 {
189 if (!cf) return 0;
190 atfork_lock();
191 int r = cf->fd < 0 ? 0 : close(cf->fd);
192 int esave = errno;
193 LIBXL_LIST_REMOVE(cf, entry);
194 atfork_unlock();
195 free(cf);
196 errno = esave;
197 return r;
198 }
199
libxl__carefd_fd(const libxl__carefd * cf)200 int libxl__carefd_fd(const libxl__carefd *cf)
201 {
202 if (!cf) return -1;
203 return cf->fd;
204 }
205
206 /*
207 * Low-level functions for child process handling, including
208 * the main SIGCHLD handler.
209 */
210
211 /* Like waitpid(,,WNOHANG) but handles all errors except ECHILD. */
checked_waitpid(libxl__egc * egc,pid_t want,int * status)212 static pid_t checked_waitpid(libxl__egc *egc, pid_t want, int *status)
213 {
214 EGC_GC;
215 for (;;) {
216 pid_t got = waitpid(want, status, WNOHANG);
217 if (got != -1)
218 return got;
219 if (errno == ECHILD)
220 return got;
221 if (errno == EINTR)
222 continue;
223 LIBXL__EVENT_DISASTER(gc, "waitpid() failed", errno, 0);
224 return 0;
225 }
226 }
227
228 static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
229 int fd, short events, short revents);
230
sigchld_handler(int signo)231 static void sigchld_handler(int signo)
232 {
233 /* This function has to be reentrant! Luckily it is. */
234
235 libxl_ctx *notify;
236 int esave = errno;
237
238 int r = pthread_mutex_lock(&sigchld_defer_mutex);
239 assert(!r);
240
241 LIBXL_LIST_FOREACH(notify, &sigchld_users, sigchld_users_entry) {
242 int e = libxl__self_pipe_wakeup(notify->sigchld_selfpipe[1]);
243 if (e) abort(); /* errors are probably EBADF, very bad */
244 }
245
246 r = pthread_mutex_unlock(&sigchld_defer_mutex);
247 assert(!r);
248
249 errno = esave;
250 }
251
sigchld_sethandler_raw(void (* handler)(int),struct sigaction * old)252 static void sigchld_sethandler_raw(void (*handler)(int), struct sigaction *old)
253 {
254 struct sigaction ours;
255 int r;
256
257 memset(&ours,0,sizeof(ours));
258 ours.sa_handler = handler;
259 sigemptyset(&ours.sa_mask);
260 ours.sa_flags = SA_NOCLDSTOP | SA_RESTART;
261 r = sigaction(SIGCHLD, &ours, old);
262 assert(!r);
263 }
264
265 /*
266 * SIGCHLD deferral
267 *
268 * sigchld_defer and sigchld_release are a bit like using sigprocmask
269 * to block the signal only they work for the whole process. Sadly
270 * this has to be done by setting a special handler that records the
271 * "pendingness" of the signal here in the program. How tedious.
272 *
273 * A property of this approach is that the signal handler itself
274 * must be reentrant (see the comment in release_sigchld).
275 *
276 * Callers have the atfork_lock so there is no risk of concurrency
277 * within these functions, aside from the risk of being interrupted by
278 * the signal. We use sigchld_defer_mutex to guard against the
279 * possibility of the real signal handler being still running on
280 * another thread.
281 */
282
283 static volatile sig_atomic_t sigchld_occurred_while_deferred;
284
sigchld_handler_when_deferred(int signo)285 static void sigchld_handler_when_deferred(int signo)
286 {
287 sigchld_occurred_while_deferred = 1;
288 }
289
defer_sigchld(void)290 static void defer_sigchld(void)
291 {
292 assert(sigchld_installed);
293
294 sigchld_sethandler_raw(sigchld_handler_when_deferred, 0);
295
296 /* Now _this thread_ cannot any longer be interrupted by the
297 * signal, so we can take the mutex without risk of deadlock. If
298 * another thread is in the signal handler, either it or we will
299 * block and wait for the other. */
300
301 int r = pthread_mutex_lock(&sigchld_defer_mutex);
302 assert(!r);
303 }
304
release_sigchld(void)305 static void release_sigchld(void)
306 {
307 assert(sigchld_installed);
308
309 int r = pthread_mutex_unlock(&sigchld_defer_mutex);
310 assert(!r);
311
312 sigchld_sethandler_raw(sigchld_handler, 0);
313 if (sigchld_occurred_while_deferred) {
314 sigchld_occurred_while_deferred = 0;
315 /* We might get another SIGCHLD here, in which case
316 * sigchld_handler will be interrupted and re-entered.
317 * This is OK. */
318 sigchld_handler(SIGCHLD);
319 }
320 }
321
322 /*
323 * Meat of the child process handling.
324 */
325
sigchld_removehandler_core(void)326 static void sigchld_removehandler_core(void) /* idempotent */
327 {
328 struct sigaction was;
329 int r;
330
331 if (!sigchld_installed)
332 return;
333
334 r = sigaction(SIGCHLD, &sigchld_saved_action, &was);
335 assert(!r);
336 assert(!(was.sa_flags & SA_SIGINFO));
337 assert(was.sa_handler == sigchld_handler);
338
339 sigchld_installed = 0;
340 }
341
sigchld_installhandler_core(void)342 static void sigchld_installhandler_core(void) /* idempotent */
343 {
344 if (sigchld_installed)
345 return;
346
347 sigchld_installed = 1;
348
349 sigchld_sethandler_raw(sigchld_handler, &sigchld_saved_action);
350
351 assert(((void)"application must negotiate with libxl about SIGCHLD",
352 !(sigchld_saved_action.sa_flags & SA_SIGINFO) &&
353 (sigchld_saved_action.sa_handler == SIG_DFL ||
354 sigchld_saved_action.sa_handler == SIG_IGN)));
355 }
356
sigchld_user_remove(libxl_ctx * ctx)357 static void sigchld_user_remove(libxl_ctx *ctx) /* idempotent */
358 {
359 if (!ctx->sigchld_user_registered)
360 return;
361
362 atfork_lock();
363 defer_sigchld();
364
365 LIBXL_LIST_REMOVE(ctx, sigchld_users_entry);
366
367 release_sigchld();
368
369 if (LIBXL_LIST_EMPTY(&sigchld_users))
370 sigchld_removehandler_core();
371
372 atfork_unlock();
373
374 ctx->sigchld_user_registered = 0;
375 }
376
libxl__sigchld_notneeded(libxl__gc * gc)377 void libxl__sigchld_notneeded(libxl__gc *gc) /* non-reentrant, idempotent */
378 {
379 sigchld_user_remove(CTX);
380 libxl__ev_fd_deregister(gc, &CTX->sigchld_selfpipe_efd);
381 }
382
libxl__sigchld_needed(libxl__gc * gc)383 int libxl__sigchld_needed(libxl__gc *gc) /* non-reentrant, idempotent */
384 {
385 int rc;
386
387 if (CTX->sigchld_selfpipe[0] < 0) {
388 rc = libxl__pipe_nonblock(CTX, CTX->sigchld_selfpipe);
389 if (rc) goto out;
390 }
391 if (!libxl__ev_fd_isregistered(&CTX->sigchld_selfpipe_efd)) {
392 rc = libxl__ev_fd_register(gc, &CTX->sigchld_selfpipe_efd,
393 sigchld_selfpipe_handler,
394 CTX->sigchld_selfpipe[0], POLLIN);
395 if (rc) goto out;
396 } else {
397 rc = libxl__ev_fd_modify(gc, &CTX->sigchld_selfpipe_efd, POLLIN);
398 if (rc) goto out;
399 }
400 if (!CTX->sigchld_user_registered) {
401 atfork_lock();
402
403 sigchld_installhandler_core();
404
405 defer_sigchld();
406
407 LIBXL_LIST_INSERT_HEAD(&sigchld_users, CTX, sigchld_users_entry);
408
409 release_sigchld();
410 atfork_unlock();
411
412 CTX->sigchld_user_registered = 1;
413 }
414
415 rc = 0;
416 out:
417 return rc;
418 }
419
chldmode_ours(libxl_ctx * ctx,bool creating)420 static bool chldmode_ours(libxl_ctx *ctx, bool creating)
421 {
422 switch (ctx->childproc_hooks->chldowner) {
423 case libxl_sigchld_owner_libxl:
424 return creating || !LIBXL_LIST_EMPTY(&ctx->children);
425 case libxl_sigchld_owner_mainloop:
426 return 0;
427 case libxl_sigchld_owner_libxl_always:
428 case libxl_sigchld_owner_libxl_always_selective_reap:
429 return 1;
430 }
431 abort();
432 }
433
perhaps_sigchld_notneeded(libxl__gc * gc)434 static void perhaps_sigchld_notneeded(libxl__gc *gc)
435 {
436 if (!chldmode_ours(CTX, 0))
437 libxl__sigchld_notneeded(gc);
438 }
439
perhaps_sigchld_needed(libxl__gc * gc,bool creating)440 static int perhaps_sigchld_needed(libxl__gc *gc, bool creating)
441 {
442 int rc;
443
444 if (chldmode_ours(CTX, creating)) {
445 rc = libxl__sigchld_needed(gc);
446 if (rc) return rc;
447 }
448 return 0;
449 }
450
childproc_reaped_ours(libxl__egc * egc,libxl__ev_child * ch,int status)451 static void childproc_reaped_ours(libxl__egc *egc, libxl__ev_child *ch,
452 int status)
453 {
454 pid_t pid = ch->pid;
455 LIBXL_LIST_REMOVE(ch, entry);
456 ch->pid = -1;
457 ch->callback(egc, ch, pid, status);
458 }
459
childproc_reaped(libxl__egc * egc,pid_t pid,int status)460 static int childproc_reaped(libxl__egc *egc, pid_t pid, int status)
461 {
462 EGC_GC;
463 libxl__ev_child *ch;
464
465 LIBXL_LIST_FOREACH(ch, &CTX->children, entry)
466 if (ch->pid == pid)
467 goto found;
468
469 /* not found */
470 return ERROR_UNKNOWN_CHILD;
471
472 found:
473 childproc_reaped_ours(egc, ch, status);
474
475 perhaps_sigchld_notneeded(gc);
476
477 return 0;
478 }
479
libxl_childproc_reaped(libxl_ctx * ctx,pid_t pid,int status)480 int libxl_childproc_reaped(libxl_ctx *ctx, pid_t pid, int status)
481 {
482 EGC_INIT(ctx);
483 CTX_LOCK;
484 assert(CTX->childproc_hooks->chldowner
485 == libxl_sigchld_owner_mainloop);
486 int rc = childproc_reaped(egc, pid, status);
487 CTX_UNLOCK_EGC_FREE;
488 return rc;
489 }
490
childproc_checkall(libxl__egc * egc)491 static void childproc_checkall(libxl__egc *egc)
492 {
493 EGC_GC;
494 libxl__ev_child *ch;
495
496 for (;;) {
497 int status;
498 pid_t got;
499
500 LIBXL_LIST_FOREACH(ch, &CTX->children, entry) {
501 got = checked_waitpid(egc, ch->pid, &status);
502 if (got)
503 goto found;
504 }
505 /* not found */
506 return;
507
508 found:
509 if (got == -1) {
510 LIBXL__EVENT_DISASTER
511 (gc, "waitpid() gave ECHILD but we have a child",
512 ECHILD, 0);
513 /* it must have finished but we don't know its status */
514 status = 255<<8; /* no wait.h macro for this! */
515 assert(WIFEXITED(status));
516 assert(WEXITSTATUS(status)==255);
517 assert(!WIFSIGNALED(status));
518 assert(!WIFSTOPPED(status));
519 }
520 childproc_reaped_ours(egc, ch, status);
521 /* we need to restart the loop, as children may have been edited */
522 }
523 }
524
libxl_childproc_sigchld_occurred(libxl_ctx * ctx)525 void libxl_childproc_sigchld_occurred(libxl_ctx *ctx)
526 {
527 EGC_INIT(ctx);
528 CTX_LOCK;
529 assert(CTX->childproc_hooks->chldowner
530 == libxl_sigchld_owner_mainloop);
531 childproc_checkall(egc);
532 CTX_UNLOCK_EGC_FREE;
533 }
534
sigchld_selfpipe_handler(libxl__egc * egc,libxl__ev_fd * ev,int fd,short events,short revents)535 static void sigchld_selfpipe_handler(libxl__egc *egc, libxl__ev_fd *ev,
536 int fd, short events, short revents)
537 {
538 /* May make callbacks into the application for child processes.
539 * So, this function may unlock and relock the CTX. This is OK
540 * because event callback functions are always called with the CTX
541 * locked exactly once, and from code which copes with reentrancy.
542 * (See also the comment in afterpoll_internal.) */
543 EGC_GC;
544
545 int selfpipe = CTX->sigchld_selfpipe[0];
546
547 if (revents & ~POLLIN) {
548 LOG(ERROR, "unexpected poll event 0x%x on SIGCHLD self pipe", revents);
549 LIBXL__EVENT_DISASTER(gc,
550 "unexpected poll event on SIGCHLD self pipe",
551 0, 0);
552 }
553 assert(revents & POLLIN);
554
555 int e = libxl__self_pipe_eatall(selfpipe);
556 if (e) LIBXL__EVENT_DISASTER(gc, "read sigchld pipe", e, 0);
557
558 if (CTX->childproc_hooks->chldowner
559 == libxl_sigchld_owner_libxl_always_selective_reap) {
560 childproc_checkall(egc);
561 return;
562 }
563
564 while (chldmode_ours(CTX, 0) /* in case the app changes the mode */) {
565 int status;
566 pid_t pid = checked_waitpid(egc, -1, &status);
567
568 if (pid == 0 || pid == -1 /* ECHILD */)
569 return;
570
571 int rc = childproc_reaped(egc, pid, status);
572
573 if (rc) {
574 if (CTX->childproc_hooks->reaped_callback) {
575 CTX_UNLOCK;
576 rc = CTX->childproc_hooks->reaped_callback
577 (pid, status, CTX->childproc_user);
578 CTX_LOCK;
579 if (rc != 0 && rc != ERROR_UNKNOWN_CHILD) {
580 char disasterbuf[200];
581 snprintf(disasterbuf, sizeof(disasterbuf), " reported by"
582 " libxl_childproc_hooks->reaped_callback"
583 " (for pid=%lu, status=%d; error code %d)",
584 (unsigned long)pid, status, rc);
585 LIBXL__EVENT_DISASTER(gc, disasterbuf, 0, 0);
586 return;
587 }
588 } else {
589 rc = ERROR_UNKNOWN_CHILD;
590 }
591 if (rc)
592 libxl_report_child_exitstatus(CTX, XTL_WARN,
593 "unknown child", (long)pid, status);
594 }
595 }
596 }
597
libxl__ev_child_fork(libxl__gc * gc,libxl__ev_child * ch,libxl__ev_child_callback * death)598 pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch,
599 libxl__ev_child_callback *death)
600 {
601 CTX_LOCK;
602 int rc;
603
604 perhaps_sigchld_needed(gc, 1);
605
606 pid_t pid =
607 CTX->childproc_hooks->fork_replacement
608 ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user)
609 : fork();
610 if (pid == -1) {
611 LOGE(ERROR, "fork failed");
612 rc = ERROR_FAIL;
613 goto out;
614 }
615
616 if (!pid) {
617 /* woohoo! */
618 if (CTX->xsh) {
619 xs_daemon_destroy_postfork(CTX->xsh);
620 CTX->xsh = NULL; /* turns mistakes into crashes */
621 }
622 /* Yes, CTX is left locked in the child. */
623 return 0;
624 }
625
626 ch->pid = pid;
627 ch->callback = death;
628 LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry);
629 rc = pid;
630
631 out:
632 perhaps_sigchld_notneeded(gc);
633 CTX_UNLOCK;
634 return rc;
635 }
636
libxl_childproc_setmode(libxl_ctx * ctx,const libxl_childproc_hooks * hooks,void * user)637 void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks *hooks,
638 void *user)
639 {
640 GC_INIT(ctx);
641 CTX_LOCK;
642
643 assert(LIBXL_LIST_EMPTY(&CTX->children));
644
645 if (!hooks)
646 hooks = &libxl__childproc_default_hooks;
647
648 ctx->childproc_hooks = hooks;
649 ctx->childproc_user = user;
650
651 perhaps_sigchld_notneeded(gc);
652 perhaps_sigchld_needed(gc, 0); /* idempotent, ok to ignore errors for now */
653
654 CTX_UNLOCK;
655 GC_FREE;
656 }
657
658 const libxl_childproc_hooks libxl__childproc_default_hooks = {
659 libxl_sigchld_owner_libxl, 0, 0
660 };
661
libxl__ev_child_xenstore_reopen(libxl__gc * gc,const char * what)662 int libxl__ev_child_xenstore_reopen(libxl__gc *gc, const char *what) {
663 int rc;
664
665 assert(!CTX->xsh);
666 CTX->xsh = xs_daemon_open();
667 if (!CTX->xsh) {
668 LOGE(ERROR, "%s: xenstore reopen failed", what);
669 rc = ERROR_FAIL; goto out;
670 }
671
672 libxl_fd_set_cloexec(CTX, xs_fileno(CTX->xsh), 1);
673
674 return 0;
675
676 out:
677 return rc;
678 }
679
680 typedef struct ev_child_killed {
681 libxl__ao *ao;
682 libxl__ev_child ch;
683 } ev_child_killed;
684 static void deregistered_child_callback(libxl__egc *, libxl__ev_child *,
685 pid_t, int status);
686
libxl__ev_child_kill_deregister(libxl__ao * ao,libxl__ev_child * ch,int sig)687 void libxl__ev_child_kill_deregister(libxl__ao *ao, libxl__ev_child *ch,
688 int sig)
689 {
690 AO_GC;
691
692 if (!libxl__ev_child_inuse(ch))
693 return;
694
695 pid_t pid = ch->pid;
696
697 ev_child_killed *new_ch = GCNEW(new_ch);
698 new_ch->ao = ao;
699 new_ch->ch.pid = pid;
700 new_ch->ch.callback = deregistered_child_callback;
701 LIBXL_LIST_INSERT_HEAD(&CTX->children, &new_ch->ch, entry);
702 ao->outstanding_killed_child++;
703
704 LIBXL_LIST_REMOVE(ch, entry);
705 ch->pid = -1;
706 int r = kill(pid, sig);
707 if (r)
708 LOGED(ERROR, ao->domid,
709 "failed to kill child [%ld] with signal %d",
710 (unsigned long)pid, sig);
711 }
712
deregistered_child_callback(libxl__egc * egc,libxl__ev_child * ch,pid_t pid,int status)713 static void deregistered_child_callback(libxl__egc *egc,
714 libxl__ev_child *ch,
715 pid_t pid,
716 int status)
717 {
718 ev_child_killed *ck = CONTAINER_OF(ch, *ck, ch);
719 EGC_GC;
720
721 libxl_report_child_exitstatus(CTX, XTL_ERROR,
722 "killed fork (dying as expected)",
723 pid, status);
724 ck->ao->outstanding_killed_child--;
725 libxl__ao_complete_check_progress_reports(egc, ck->ao);
726 }
727
728 /*
729 * Local variables:
730 * mode: C
731 * c-basic-offset: 4
732 * indent-tabs-mode: nil
733 * End:
734 */
735