On 1/9/20 12:12 PM, George Dunlap wrote:
> libxl needs to be able to know when processes it forks have completed.
> 
> At the moment, libxl has two basic mode (with some variations).  In
> one mode -- libxl_sigchld_owner_libxl* -- libxl sets up its own
> SIGCHLD signal handler, and also handles the event loop that allows
> libxl to safely block until the child in question is finished (using a
> self-pipe for the SIGCHLD handler to notify the waiters that it's time
> to look for reaped children).
> 
> In the other mode, libxl does not set up the SIGCHLD handler, nor does
> it do anything with processing the event loop; it expects the library
> caller to handle the event loop itself.
> 
> The golang runtime manages its own processes, and thus must use
> SIGCHLD itself; and it has an easy way for other users to get SIGCHLD
> notifications.  However, because its event loop is hidden away behind
> abstractions, it's not easy to hook into; and there's no need -- the
> golang runtime assumes that C function calls may block, and handles
> everything behind the scenes.

FWIW, attached is a C program that behaves as I think golang would
behave, which hangs in `mainloop` mode.

It's actually got two modes: Run it without any arguments, and it will
run in "default" mode (not setting up a SIGCHLD nor setting
childproc_mode);  run with at least one argument (whatever it is), it
will run in `mainloop` mode, and hang after the second SIGCHLD event.

 -George
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libxl.h>
#include <assert.h>
#include <pthread.h>
#include <poll.h>
#include <signal.h>
#include <fcntl.h>

char * malloc_copy(const char *src) {
  int len = strlen(src);
  char * dst = malloc(strlen(src)+1);

  strncpy(dst, src, len);

  return dst;
}

libxl_ctx *ctx;

int self_pipe_wakeup(int fd)
{
    /* Called from signal handlers, so needs to be async-signal-safe */
    static const char buf[1] = "";

    for (;;) {
        int r = write(fd, buf, 1);
        if (r==1) return 0;
        assert(r==-1);
        if (errno == EINTR) continue;
        if (errno == EWOULDBLOCK) return 0;
        if (!errno) abort();
        return errno;
    }
}

int 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;
    }
}

int sigchld_selfpipe[2];

static void *sigchld_helper(void *fd) {
    struct pollfd pfd;
    int r;

    pfd.fd = (int)(unsigned long)fd;
    pfd.events = POLLIN | POLLHUP;

    while (true) {
        // Wait for a sigchld on [0]
        fprintf(stderr, "Waiting for self-pipe\n");
        r = poll(&pfd, 1, -1);
        if (r < 0) {
            if (errno == EINTR)
                continue;
            perror("poll");
            return NULL;
        }
        if (pfd.revents == POLLHUP) {
            fprintf(stderr, "Received POLLHUP, closing helper\n");
            return NULL;
        }
        fprintf(stderr, "Self-pipe received, calling libxl_childproc_sigchld_occurred\n");
        self_pipe_eatall(pfd.fd);
        libxl_childproc_sigchld_occurred(ctx);
    }

    return NULL;
}

static void sigchld_handler(int signo) {
    // Signal on pipe [1]
    fprintf(stderr, "SIGCHLD received, self-signaling\n");
    self_pipe_wakeup(sigchld_selfpipe[1]);
}

void setup_sigchld(void) {
    struct sigaction ours, old;
    pthread_t t;
    int i, flags, r;

    // Get self-signal pipe
    if (pipe(sigchld_selfpipe) < 0) {
        perror("Failed to create a pipe");
        exit(1);
    }

    r = pthread_create(&t, NULL, sigchld_helper, (void *)(unsigned long)sigchld_selfpipe[0]);
    if (r < 0) {
        perror("Creating helper thread");
        exit(1);
    }

    // Make non-blocking
    for (i = 0; i < 2; i++) {
        int fd = sigchld_selfpipe[i];
        flags = fcntl(fd, F_GETFL);
        if (flags == -1) {
            perror("Getting block flags");
            exit(1);
        }

        flags |= O_NONBLOCK;

        r = fcntl(fd, F_SETFL, flags);
        if (flags == -1) {
            perror("Setting pipe non-blocking");
            exit(1);
        }
    }

    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, &old);
    assert(!r);
}

int main(int argc, char * argv[]) {
    int rc;
    xentoollog_logger_stdiostream * xtl;
    libxl_domain_config cconfig;
    uint32_t domid;
    bool mainloop = false;

    if (argc > 1)
        mainloop=true;

    if (mainloop)
        setup_sigchld();

    xtl = xtl_createlogger_stdiostream(stderr, XTL_DEBUG, 0);
    rc = libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)xtl);
    if (rc) {
        fprintf(stderr, "Getting libxl_ctx: %d", rc);
        exit(1);
    }

    if (mainloop) {
        libxl_childproc_hooks *cp = malloc(sizeof(*cp));

        cp->chldowner = libxl_sigchld_owner_mainloop;
        libxl_childproc_setmode(ctx, cp, NULL);
    }

    libxl_domain_config_init(&cconfig);
    libxl_domain_build_info_init_type(&cconfig.b_info, LIBXL_DOMAIN_TYPE_PV);

    cconfig.c_info.type = LIBXL_DOMAIN_TYPE_PV;
    cconfig.c_info.name = malloc_copy("c6-01");
    cconfig.b_info.max_vcpus = 4;
    cconfig.b_info.max_memkb = 2048*1024;
    cconfig.b_info.target_memkb = 2048*1024;
    cconfig.on_crash = LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
    cconfig.b_info.bootloader = malloc_copy("pygrub");

    cconfig.num_disks = 1;
    cconfig.disks = malloc(sizeof(libxl_device_disk));
    libxl_device_disk_init(cconfig.disks);
    cconfig.disks[0].vdev = malloc_copy("hda");
    cconfig.disks[0].format = LIBXL_DISK_FORMAT_RAW;
    cconfig.disks[0].pdev_path = malloc_copy("/images/c6-01.raw");
    cconfig.disks[0].readwrite = 1;

    cconfig.num_nics = 1;
    cconfig.nics = malloc(sizeof(libxl_device_nic));
    libxl_device_nic_init(cconfig.nics);

    rc = libxl_domain_create_new(ctx, &cconfig, &domid, NULL, NULL);
    if (rc) {
        fprintf(stderr, "Creating domain: %d", rc);
        exit(1);
    }

    printf("Created domain with id %d", domid);

    libxl_domain_config_dispose(&cconfig);

    if (mainloop) {
        close(sigchld_selfpipe[1]);
        close(sigchld_selfpipe[0]);
    }

    return 0;
}
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel

Reply via email to