[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH 22/31] libxl: event API: new facilities for waiting for subprocesses



On Tue, 2012-04-10 at 20:07 +0100, Ian Jackson wrote:
> The current arrangements in libxl for spawning subprocesses have two
> key problems: (i) they make unwarranted (and largely undocumented)
> assumptions about the caller's use of subprocesses, (ii) they aren't
> integrated into the event system and can't be made asynchronous etc.
> 
> So replace them with a new set of facilities.
> 
> Primarily, from the point of view of code inside libxl, this is
> libxl__ev_child_fork which is both (a) a version of fork() and (b) an
> event source which generates a callback when the child dies.
> 
> From the point of view of the application, we fully document our use
> of SIGCHLD.  The application can tell us whether it wants to own
> SIGCHLD or not; if it does, it has to tell us about deaths of our
> children.
> 
> Currently there are no callers in libxl which use these facilities.
> All code in libxl which forks needs to be converted and libxl_fork
> needse to be be abolished.
> 
> Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
> Cc: Roger Pau Monne <roger.pau@xxxxxxxxxxxxx>

Acked-by: Ian Campbell <ian.campbell@xxxxxxxxxx>

> ---
>  tools/libxl/libxl.c          |   17 +++-
>  tools/libxl/libxl.h          |    1 +
>  tools/libxl/libxl_event.c    |   53 +++++++--
>  tools/libxl/libxl_event.h    |  147 +++++++++++++++++++++++-
>  tools/libxl/libxl_fork.c     |  255 
> ++++++++++++++++++++++++++++++++++++++++++
>  tools/libxl/libxl_internal.h |   59 +++++++++-
>  6 files changed, 512 insertions(+), 20 deletions(-)
> 
> diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
> index 60dbfdc..42ac89f 100644
> --- a/tools/libxl/libxl.c
> +++ b/tools/libxl/libxl.c
> @@ -39,7 +39,7 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
>      memset(ctx, 0, sizeof(libxl_ctx));
>      ctx->lg = lg;
> 
> -    /* First initialise pointers (cannot fail) */
> +    /* First initialise pointers etc. (cannot fail) */
> 
>      LIBXL_TAILQ_INIT(&ctx->occurred);
> 
> @@ -58,6 +58,11 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
>      LIBXL_TAILQ_INIT(&ctx->death_list);
>      libxl__ev_xswatch_init(&ctx->death_watch);
> 
> +    ctx->childproc_hooks = &libxl__childproc_default_hooks;
> +    ctx->childproc_user = 0;
> +
> +    ctx->sigchld_selfpipe[0] = -1;
> +
>      /* The mutex is special because we can't idempotently destroy it */
> 
>      if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
> @@ -160,6 +165,16 @@ int libxl_ctx_free(libxl_ctx *ctx)
> 
>      discard_events(&ctx->occurred);
> 
> +    /* If we have outstanding children, then the application inherits
> +     * them; we wish the application good luck with understanding
> +     * this if and when it reaps them. */
> +    libxl__sigchld_removehandler(ctx);
> +
> +    if (ctx->sigchld_selfpipe[0] >= 0) {
> +        close(ctx->sigchld_selfpipe[0]);
> +        close(ctx->sigchld_selfpipe[1]);
> +    }
> +
>      pthread_mutex_destroy(&ctx->lock);
> 
>      GC_FREE;
> diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
> index edbca53..03e71f6 100644
> --- a/tools/libxl/libxl.h
> +++ b/tools/libxl/libxl.h
> @@ -380,6 +380,7 @@ enum {
>      ERROR_NOT_READY = -11,
>      ERROR_OSEVENT_REG_FAIL = -12,
>      ERROR_BUFFERFULL = -13,
> +    ERROR_UNKNOWN_CHILD = -14,
>  };
> 
> 
> diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c
> index 672e3fe..1b7495a 100644
> --- a/tools/libxl/libxl_event.c
> +++ b/tools/libxl/libxl_event.c
> @@ -623,6 +623,10 @@ static int beforepoll_internal(libxl__gc *gc, 
> libxl__poller *poller,
>                                                                         \
>          REQUIRE_FD(poller->wakeup_pipe[0], POLLIN, BODY);              \
>                                                                         \
> +        int selfpipe = libxl__fork_selfpipe_active(CTX);               \
> +        if (selfpipe >= 0)                                             \
> +            REQUIRE_FD(selfpipe, POLLIN, BODY);                        \
> +                                                                       \
>      }while(0)
> 
>  #define REQUIRE_FD(req_fd_, req_events_, BODY) do{      \
> @@ -762,10 +766,11 @@ static void afterpoll_internal(libxl__egc *egc, 
> libxl__poller *poller,
>                                 int nfds, const struct pollfd *fds,
>                                 struct timeval now)
>  {
> +    /* May make callbacks into the application for child processes.
> +     * ctx must be locked exactly once */
>      EGC_GC;
>      libxl__ev_fd *efd;
> 
> -
>      LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) {
>          if (!efd->events)
>              continue;
> @@ -776,11 +781,16 @@ static void afterpoll_internal(libxl__egc *egc, 
> libxl__poller *poller,
>      }
> 
>      if (afterpoll_check_fd(poller,fds,nfds, poller->wakeup_pipe[0],POLLIN)) {
> -        char buf[256];
> -        int r = read(poller->wakeup_pipe[0], buf, sizeof(buf));
> -        if (r < 0)
> -            if (errno != EINTR && errno != EWOULDBLOCK)
> -                LIBXL__EVENT_DISASTER(egc, "read wakeup", errno, 0);
> +        int e = libxl__self_pipe_eatall(poller->wakeup_pipe[0]);
> +        if (e) LIBXL__EVENT_DISASTER(egc, "read wakeup", e, 0);
> +    }
> +
> +    int selfpipe = libxl__fork_selfpipe_active(CTX);
> +    if (selfpipe >= 0 &&
> +        afterpoll_check_fd(poller,fds,nfds, selfpipe, POLLIN)) {
> +        int e = libxl__self_pipe_eatall(selfpipe);
> +        if (e) LIBXL__EVENT_DISASTER(egc, "read sigchld pipe", e, 0);
> +        libxl__fork_selfpipe_woken(egc);
>      }
> 
>      for (;;) {
> @@ -1078,16 +1088,37 @@ void libxl__poller_put(libxl_ctx *ctx, libxl__poller 
> *p)
> 
>  void libxl__poller_wakeup(libxl__egc *egc, libxl__poller *p)
>  {
> +    int e = libxl__self_pipe_wakeup(p->wakeup_pipe[1]);
> +    if (e) LIBXL__EVENT_DISASTER(egc, "cannot poke watch pipe", e, 0);
> +}
> +
> +int libxl__self_pipe_wakeup(int fd)
> +{
>      static const char buf[1] = "";
> 
>      for (;;) {
> -        int r = write(p->wakeup_pipe[1], buf, 1);
> -        if (r==1) return;
> +        int r = write(fd, buf, 1);
> +        if (r==1) return 0;
>          assert(r==-1);
>          if (errno == EINTR) continue;
> -        if (errno == EWOULDBLOCK) return;
> -        LIBXL__EVENT_DISASTER(egc, "cannot poke watch pipe", errno, 0);
> -        return;
> +        if (errno == EWOULDBLOCK) return 0;
> +        assert(errno);
> +        return errno;
> +    }
> +}
> +
> +int libxl__self_pipe_eatall(int fd)
> +{
> +    char buf[256];
> +    for (;;) {
> +        int r = read(fd, buf, sizeof(buf));
> +        if (r == sizeof(buf)) continue;
> +        if (r >= 0) return 0;
> +        assert(r == -1);
> +        if (errno == EINTR) continue;
> +        if (errno == EWOULDBLOCK) return 0;
> +        assert(errno);
> +        return errno;
>      }
>  }
> 
> diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h
> index 2d2196f..713d96d 100644
> --- a/tools/libxl/libxl_event.h
> +++ b/tools/libxl/libxl_event.h
> @@ -163,11 +163,6 @@ void libxl_event_register_callbacks(libxl_ctx *ctx,
>   * After libxl_ctx_free, all corresponding evgen handles become
>   * invalid and must no longer be passed to evdisable.
>   *
> - * Events enabled with evenable prior to a fork and libxl_ctx_postfork
> - * are no longer generated after the fork/postfork; however the evgen
> - * structures are still valid and must be passed to evdisable if the
> - * memory they use should not be leaked.
> - *
>   * Applications should ensure that they eventually retrieve every
>   * event using libxl_event_check or libxl_event_wait, since events
>   * which occur but are not retreived by the application will be queued
> @@ -372,6 +367,148 @@ void libxl_osevent_occurred_fd(libxl_ctx *ctx, void 
> *for_libxl,
>  void libxl_osevent_occurred_timeout(libxl_ctx *ctx, void *for_libxl);
> 
> 
> +/*======================================================================*/
> +
> +/*
> + * Subprocess handling.
> + *
> + * Unfortunately the POSIX interface makes this very awkward.
> + *
> + * There are two possible arrangements for collecting statuses from
> + * wait/waitpid.
> + *
> + * For naive programs:
> + *
> + *     libxl will keep a SIGCHLD handler installed whenever it has an
> + *     active (unreaped) child.  It will reap all children with
> + *     wait(); any children it does not recognise will be passed to
> + *     the application via an optional callback (and will result in
> + *     logged warnings if no callback is provided or the callback
> + *     denies responsibility for the child).
> + *
> + *     libxl may have children whenever:
> + *
> + *       - libxl is performing an operation which can be made
> + *         asynchronous; ie one taking a libxl_asyncop_how, even
> + *         if NULL is passed indicating that the operation is
> + *         synchronous; or
> + *
> + *       - events of any kind are being generated, as requested
> + *         by libxl_evenable_....
> + *
> + *     A multithreaded application which is naive in this sense may
> + *     block SIGCHLD on some of its threads, but there must be at
> + *     least one thread that has SIGCHLD unblocked.  libxl will not
> + *     modify the blocking flag for SIGCHLD (except that it may create
> + *     internal service threads with all signals blocked).
> + *
> + *     A naive program must only have at any one time only
> + *     one libxl context which might have children.
> + *
> + * For programs which run their own children alongside libxl's:
> + *
> + *     A program which does this must call libxl_childproc_setmode.
> + *     There are two options:
> + *
> + *     libxl_sigchld_owner_mainloop:
> + *       The application must install a SIGCHLD handler and reap (at
> + *       least) all of libxl's children and pass their exit status
> + *       to libxl by calling libxl_childproc_exited.
> + *
> + *     libxl_sigchld_owner_libxl_always:
> + *       The application expects libxl to reap all of its children,
> + *       and provides a callback to be notified of their exit
> + *       statues.
> + *
> + * An application which fails to call setmode, or which passes 0 for
> + * hooks, while it uses any libxl operation which might
> + * create or use child processes (see above):
> + *   - Must not have any child processes running.
> + *   - Must not install a SIGCHLD handler.
> + *   - Must not reap any children.
> + */
> +
> +
> +typedef enum {
> +    /* libxl owns SIGCHLD whenever it has a child. */
> +    libxl_sigchld_owner_libxl,
> +
> +    /* Application promises to call libxl_childproc_exited but NOT
> +     * from within a signal handler.  libxl will not itself arrange to
> +     * (un)block or catch SIGCHLD. */
> +    libxl_sigchld_owner_mainloop,
> +
> +    /* libxl owns SIGCHLD all the time, and the application is
> +     * relying on libxl's event loop for reaping its own children. */
> +    libxl_sigchld_owner_libxl_always,
> +} libxl_sigchld_owner;
> +
> +typedef struct {
> +    libxl_sigchld_owner chldowner;
> +
> +    /* All of these are optional: */
> +
> +    /* Called by libxl instead of fork.  Should behave exactly like
> +     * fork, including setting errno etc.  May NOT reenter into libxl.
> +     * Application may use this to discover pids of libxl's children,
> +     * for example.
> +     */
> +    pid_t (*fork_replacement)(void *user);
> +
> +    /* With libxl_sigchld_owner_libxl, called by libxl when it has
> +     * reaped a pid.  (Not permitted with _owner_mainloop.)
> +     *
> +     * Should return 0 if the child was recognised by the application
> +     * (or if the application does not keep those kind of records),
> +     * ERROR_UNKNOWN_CHILD if the application knows that the child is not
> +     * the application's; if it returns another error code it is a
> +     * disaster as described for libxl_event_register_callbacks.
> +     * (libxl will report unexpected children to its error log.)
> +     *
> +     * If not supplied, the application is assumed not to start
> +     * any children of its own.
> +     *
> +     * This function is NOT called from within the signal handler.
> +     * Rather it will be called from inside a libxl's event handling
> +     * code and thus only when libxl is running, for example from
> +     * within libxl_event_wait.  (libxl uses the self-pipe trick
> +     * to implement this.)
> +     *
> +     * childproc_exited_callback may call back into libxl, but it
> +     * is best to avoid making long-running libxl calls as that might
> +     * stall the calling event loop while the nested operation
> +     * completes.
> +     */
> +    int (*reaped_callback)(pid_t, int status, void *user);
> +} libxl_childproc_hooks;
> +
> +/* hooks may be 0 in which is equivalent to &{ libxl_sigchld_owner_libxl, 0, 
> 0 }
> + *
> + * May not be called when libxl might have any child processes, or the
> + * behaviour is undefined.  So it is best to call this at
> + * initialisation.
> + */
> +void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks 
> *hooks,
> +                             void *user);
> +
> +/*
> + * This function is for an application which owns SIGCHLD and which
> + * therefore reaps all of the process's children.
> + *
> + * May be called only by an application which has called setmode with
> + * chldowner == libxl_sigchld_owner_mainloop.  If pid was a process started
> + * by this instance of libxl, returns 0 after doing whatever
> + * processing is appropriate.  Otherwise silently returns
> + * ERROR_UNKNOWN_CHILD.  No other error returns are possible.
> + *
> + * May NOT be called from within a signal handler which might
> + * interrupt any libxl operation.  The application will almost
> + * certainly need to use the self-pipe trick (or a working pselect or
> + * ppoll) to implement this.
> + */
> +int libxl_childproc_reaped(libxl_ctx *ctx, pid_t, int status);
> +
> +
>  /*
>   * An application which initialises a libxl_ctx in a parent process
>   * and then forks a child which does not quickly exec, must
> diff --git a/tools/libxl/libxl_fork.c b/tools/libxl/libxl_fork.c
> index 4751ef4..aac9598 100644
> --- a/tools/libxl/libxl_fork.c
> +++ b/tools/libxl/libxl_fork.c
> @@ -46,6 +46,12 @@ static int atfork_registered;
>  static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
>      LIBXL_LIST_HEAD_INITIALIZER(carefds);
> 
> +/* non-null iff installed, protected by no_forking */
> +static libxl_ctx *sigchld_owner;
> +static struct sigaction sigchld_saved_action;
> +
> +static void sigchld_removehandler_core(void);
> +
>  static void atfork_lock(void)
>  {
>      int r = pthread_mutex_lock(&no_forking);
> @@ -104,6 +110,7 @@ void libxl_postfork_child_noexec(libxl_ctx *ctx)
>      int r;
> 
>      atfork_lock();
> +
>      LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) {
>          if (cf->fd >= 0) {
>              r = close(cf->fd);
> @@ -115,6 +122,10 @@ void libxl_postfork_child_noexec(libxl_ctx *ctx)
>          free(cf);
>      }
>      LIBXL_LIST_INIT(&carefds);
> +
> +    if (sigchld_owner)
> +        sigchld_removehandler_core();
> +
>      atfork_unlock();
>  }
> 
> @@ -138,6 +149,250 @@ int libxl__carefd_fd(const libxl__carefd *cf)
>  }
> 
>  /*
> + * Actual child process handling
> + */
> +
> +static void sigchld_handler(int signo)
> +{
> +    int e = libxl__self_pipe_wakeup(sigchld_owner->sigchld_selfpipe[1]);
> +    assert(!e); /* errors are probably EBADF, very bad */
> +}
> +
> +static void sigchld_removehandler_core(void)
> +{
> +    struct sigaction was;
> +    int r;
> +
> +    r = sigaction(SIGCHLD, &sigchld_saved_action, &was);
> +    assert(!r);
> +    assert(!(was.sa_flags & SA_SIGINFO));
> +    assert(was.sa_handler == sigchld_handler);
> +    sigchld_owner = 0;
> +}
> +
> +void libxl__sigchld_removehandler(libxl_ctx *ctx) /* non-reentrant */
> +{
> +    atfork_lock();
> +    if (sigchld_owner == ctx)
> +        sigchld_removehandler_core();
> +    atfork_unlock();
> +}
> +
> +int libxl__sigchld_installhandler(libxl_ctx *ctx) /* non-reentrant */
> +{
> +    int r, rc;
> +
> +    if (ctx->sigchld_selfpipe[0] < 0) {
> +        r = pipe(ctx->sigchld_selfpipe);
> +        if (r) {
> +            ctx->sigchld_selfpipe[0] = -1;
> +            LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR,
> +                             "failed to create sigchld pipe");
> +            rc = ERROR_FAIL;
> +            goto out;
> +        }
> +    }
> +
> +    atfork_lock();
> +    if (sigchld_owner != ctx) {
> +        struct sigaction ours;
> +
> +        assert(!sigchld_owner);
> +        sigchld_owner = ctx;
> +
> +        memset(&ours,0,sizeof(ours));
> +        ours.sa_handler = sigchld_handler;
> +        sigemptyset(&ours.sa_mask);
> +        ours.sa_flags = SA_NOCLDSTOP | SA_RESTART;
> +        r = sigaction(SIGCHLD, &ours, &sigchld_saved_action);
> +        assert(!r);
> +
> +        assert(((void)"application must negotiate with libxl about SIGCHLD",
> +                !(sigchld_saved_action.sa_flags & SA_SIGINFO) &&
> +                (sigchld_saved_action.sa_handler == SIG_DFL ||
> +                 sigchld_saved_action.sa_handler == SIG_IGN)));
> +    }
> +    atfork_unlock();
> +
> +    rc = 0;
> + out:
> +    return rc;
> +}
> +
> +static int chldmode_ours(libxl_ctx *ctx)
> +{
> +    return ctx->childproc_hooks->chldowner == libxl_sigchld_owner_libxl;
> +}
> +
> +int libxl__fork_selfpipe_active(libxl_ctx *ctx)
> +{
> +    /* Returns the fd to read, or -1 */
> +    if (!chldmode_ours(ctx))
> +        return -1;
> +
> +    if (LIBXL_LIST_EMPTY(&ctx->children))
> +        return -1;
> +
> +    return ctx->sigchld_selfpipe[0];
> +}
> +
> +static void perhaps_removehandler(libxl_ctx *ctx)
> +{
> +    if (LIBXL_LIST_EMPTY(&ctx->children) &&
> +        ctx->childproc_hooks->chldowner != libxl_sigchld_owner_libxl_always)
> +        libxl__sigchld_removehandler(ctx);
> +}
> +
> +static int childproc_reaped(libxl__egc *egc, pid_t pid, int status)
> +{
> +    EGC_GC;
> +    libxl__ev_child *ch;
> +
> +    LIBXL_LIST_FOREACH(ch, &CTX->children, entry)
> +        if (ch->pid == pid)
> +            goto found;
> +
> +    /* not found */
> +    return ERROR_UNKNOWN_CHILD;
> +
> + found:
> +    LIBXL_LIST_REMOVE(ch, entry);
> +    ch->pid = -1;
> +    ch->callback(egc, ch, pid, status);
> +
> +    perhaps_removehandler(CTX);
> +
> +    return 0;
> +}
> +
> +int libxl_childproc_reaped(libxl_ctx *ctx, pid_t pid, int status)
> +{
> +    EGC_INIT(ctx);
> +    CTX_LOCK;
> +    int rc = childproc_reaped(egc, pid, status);
> +    CTX_UNLOCK;
> +    EGC_FREE;
> +    return rc;
> +}
> +
> +void libxl__fork_selfpipe_woken(libxl__egc *egc)
> +{
> +    /* May make callbacks into the application for child processes.
> +     * ctx must be locked EXACTLY ONCE */
> +    EGC_GC;
> +
> +    while (chldmode_ours(CTX) /* in case the app changes the mode */) {
> +        int status;
> +        pid_t pid = waitpid(-1, &status, WNOHANG);
> +
> +        if (pid == 0) return;
> +
> +        if (pid == -1) {
> +            if (errno == ECHILD) return;
> +            if (errno == EINTR) continue;
> +            LIBXL__EVENT_DISASTER(egc, "waitpid() failed", errno, 0);
> +            return;
> +        }
> +
> +        int rc = childproc_reaped(egc, pid, status);
> +
> +        if (rc) {
> +            if (CTX->childproc_hooks->reaped_callback) {
> +                CTX_UNLOCK;
> +                rc = CTX->childproc_hooks->reaped_callback
> +                        (pid, status, CTX->childproc_user);
> +                CTX_LOCK;
> +                if (rc != 0 && rc != ERROR_UNKNOWN_CHILD) {
> +                    char disasterbuf[200];
> +                    snprintf(disasterbuf, sizeof(disasterbuf), " reported by"
> +                             " libxl_childproc_hooks->reaped_callback"
> +                             " (for pid=%lu, status=%d; error code %d)",
> +                             (unsigned long)pid, status, rc);
> +                    LIBXL__EVENT_DISASTER(egc, disasterbuf, 0, 0);
> +                    return;
> +                }
> +            } else {
> +                rc = ERROR_UNKNOWN_CHILD;
> +            }
> +            if (rc)
> +                libxl_report_child_exitstatus(CTX, XTL_WARN,
> +                                "unknown child", (long)pid, status);
> +        }
> +    }
> +}
> +
> +pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child *ch,
> +                           libxl__ev_child_callback *death)
> +{
> +    CTX_LOCK;
> +    int rc;
> +
> +    if (chldmode_ours(CTX)) {
> +        rc = libxl__sigchld_installhandler(CTX);
> +        if (rc) goto out;
> +    }
> +
> +    pid_t pid =
> +        CTX->childproc_hooks->fork_replacement
> +        ? CTX->childproc_hooks->fork_replacement(CTX->childproc_user)
> +        : fork();
> +    if (pid == -1) {
> +        LOGE(ERROR, "fork failed");
> +        rc = ERROR_FAIL;
> +        goto out;
> +    }
> +
> +    if (!pid) {
> +        /* woohoo! */
> +        return 0; /* Yes, CTX is left locked in the child. */
> +    }
> +
> +    ch->pid = pid;
> +    ch->callback = death;
> +    LIBXL_LIST_INSERT_HEAD(&CTX->children, ch, entry);
> +    rc = pid;
> +
> + out:
> +    perhaps_removehandler(CTX);
> +    CTX_UNLOCK;
> +    return rc;
> +}
> +
> +void libxl_childproc_setmode(libxl_ctx *ctx, const libxl_childproc_hooks 
> *hooks,
> +                             void *user)
> +{
> +    GC_INIT(ctx);
> +    CTX_LOCK;
> +
> +    assert(LIBXL_LIST_EMPTY(&CTX->children));
> +
> +    if (!hooks)
> +        hooks = &libxl__childproc_default_hooks;
> +
> +    ctx->childproc_hooks = hooks;
> +    ctx->childproc_user = user;
> +
> +    switch (ctx->childproc_hooks->chldowner) {
> +    case libxl_sigchld_owner_mainloop:
> +    case libxl_sigchld_owner_libxl:
> +        libxl__sigchld_removehandler(ctx);
> +        break;
> +    case libxl_sigchld_owner_libxl_always:
> +        libxl__sigchld_installhandler(ctx);
> +        break;
> +    default:
> +        abort();
> +    }
> +
> +    CTX_UNLOCK;
> +    GC_FREE;
> +}
> +
> +const libxl_childproc_hooks libxl__childproc_default_hooks = {
> +    libxl_sigchld_owner_libxl, 0, 0
> +};
> +
> +/*
>   * Local variables:
>   * mode: C
>   * c-basic-offset: 4
> diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
> index 1a2139c..a60171c 100644
> --- a/tools/libxl/libxl_internal.h
> +++ b/tools/libxl/libxl_internal.h
> @@ -196,6 +196,19 @@ typedef struct libxl__ev_watch_slot {
>  libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum);
> 
> 
> +typedef struct libxl__ev_child libxl__ev_child;
> +typedef void libxl__ev_child_callback(libxl__egc *egc, libxl__ev_child*,
> +                                      pid_t pid, int status);
> +struct libxl__ev_child {
> +    /* caller should include this in their own struct */
> +    /* read-only for caller: */
> +    pid_t pid; /* -1 means unused ("unregistered", ie Idle) */
> +    libxl__ev_child_callback *callback;
> +    /* remainder is private for libxl__ev_... */
> +    LIBXL_LIST_ENTRY(struct libxl__ev_child) entry;
> +};
> +
> +
>  /*
>   * evgen structures, which are the state we use for generating
>   * events for the caller.
> @@ -315,10 +328,14 @@ struct libxl__ctx {
> 
>      LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens;
> 
> -    /* for callers who reap children willy-nilly; caller must only
> -     * set this after libxl_init and before any other call - or
> -     * may leave them untouched */
> +    const libxl_childproc_hooks *childproc_hooks;
> +    void *childproc_user;
> +    int sigchld_selfpipe[2]; /* [0]==-1 means handler not installed */
> +    LIBXL_LIST_HEAD(, libxl__ev_child) children;
> +
> +    /* This is obsolete and must be removed: */
>      int (*waitpid_instead)(pid_t pid, int *status, int flags);
> +
>      libxl_version_info version_info;
>  };
> 
> @@ -566,6 +583,33 @@ static inline int libxl__ev_xswatch_isregistered(const 
> libxl__ev_xswatch *xw)
> 
> 
>  /*
> + * For making subprocesses.  This is the only permitted mechanism for
> + * code in libxl to do so.
> + *
> + * In the parent, returns the pid, filling in childw_out.
> + * In the child, returns 0.
> + * If it fails, returns a libxl error (all of which are -ve).
> + *
> + * The child should go on to exec (or exit) soon, and should not make
> + * any further libxl event calls in the meantime.
> + *
> + * The parent may signal the child but it must not reap it.  That will
> + * be done by the event machinery.  death may be NULL in which case
> + * the child is still reaped but its death is ignored.
> + *
> + * It is not possible to "deregister" the child death event source.
> + * It will generate exactly one event callback; until then the childw
> + * is Active and may not be reused.
> + */
> +_hidden pid_t libxl__ev_child_fork(libxl__gc *gc, libxl__ev_child 
> *childw_out,
> +                                 libxl__ev_child_callback *death);
> +static inline void libxl__ev_child_init(libxl__ev_child *childw_out)
> +                { childw_out->pid = -1; }
> +static inline int libxl__ev_child_inuse(libxl__ev_child *childw_out)
> +                { return childw_out->pid >= 0; }
> +
> +
> +/*
>   * Other event-handling support provided by the libxl event core to
>   * the rest of libxl.
>   */
> @@ -619,6 +663,15 @@ void libxl__poller_put(libxl_ctx *ctx, libxl__poller *p);
>   * ctx must be locked. */
>  void libxl__poller_wakeup(libxl__egc *egc, libxl__poller *p);
> 
> +/* Internal to fork and child reaping machinery */
> +extern const libxl_childproc_hooks libxl__childproc_default_hooks;
> +int libxl__sigchld_installhandler(libxl_ctx *ctx); /* non-reentrant;logs 
> errs */
> +void libxl__sigchld_removehandler(libxl_ctx *ctx); /* non-reentrant */
> +int libxl__fork_selfpipe_active(libxl_ctx *ctx); /* returns read fd or -1 */
> +void libxl__fork_selfpipe_woken(libxl__egc *egc);
> +int libxl__self_pipe_wakeup(int fd); /* returns 0 or -1 setting errno */
> +int libxl__self_pipe_eatall(int fd); /* returns 0 or -1 setting errno */
> +
> 
>  int libxl__atfork_init(libxl_ctx *ctx);
> 
> --
> 1.7.2.5
> 
> 
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxx
> http://lists.xen.org/xen-devel



_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.