diff options
Diffstat (limited to 'src/libmowgli/eventloop/helper.c')
-rw-r--r-- | src/libmowgli/eventloop/helper.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/libmowgli/eventloop/helper.c b/src/libmowgli/eventloop/helper.c new file mode 100644 index 0000000..b89e447 --- /dev/null +++ b/src/libmowgli/eventloop/helper.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mowgli.h" + +typedef struct { + mowgli_eventloop_helper_start_fn_t *start_fn; + void *userdata; + mowgli_descriptor_t fd; +} mowgli_helper_create_req_t; + +static void +mowgli_helper_trampoline(mowgli_helper_create_req_t *req) +{ + mowgli_eventloop_helper_proc_t *helper; +#ifndef _WIN32 + int i, x; +#endif + + return_if_fail(req != NULL); + return_if_fail(req->start_fn != NULL); + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->fd = req->fd; + +#ifndef _WIN32 + for (i = 0; i < 1024; i++) + { + if (i != req->fd) + close(i); + } + + x = open("/dev/null", O_RDWR); + + for (i = 0; i < 2; i++) + { + if (req->fd != i) + dup2(x, i); + } + + if (x > 2) + close(x); +#endif + + helper->eventloop = mowgli_eventloop_create(); + helper->pfd = mowgli_pollable_create(helper->eventloop, helper->fd, helper); + helper->userdata = req->userdata; + + mowgli_pollable_set_nonblocking(helper->pfd, true); + + req->start_fn(helper, helper->userdata); +} + +mowgli_eventloop_helper_proc_t * +mowgli_helper_create(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_start_fn_t *start_fn, const char *helpername, void *userdata) +{ + mowgli_eventloop_helper_proc_t *helper; + mowgli_helper_create_req_t child; + int io_fd[2]; + + return_val_if_fail(eventloop != NULL, NULL); + return_val_if_fail(start_fn != NULL, NULL); + + child.start_fn = start_fn; + child.userdata = userdata; + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->eventloop = eventloop; + + socketpair(AF_UNIX, SOCK_STREAM, 0, io_fd); + + /* set up helper/child fd mapping */ + helper->fd = io_fd[0]; + child.fd = io_fd[1]; + + /* make pollables and make them non-blocking */ + helper->pfd = mowgli_pollable_create(eventloop, helper->fd, helper); + mowgli_pollable_set_nonblocking(helper->pfd, true); + + /* spawn helper process using mowgli_process_clone() */ + helper->child = mowgli_process_clone((mowgli_process_start_fn_t) mowgli_helper_trampoline, helpername, &child); + + if (helper->child == NULL) + { + mowgli_pollable_destroy(eventloop, helper->pfd); + + close(io_fd[0]); + close(io_fd[1]); + + mowgli_free(helper); + return NULL; + } + + close(child.fd); + + return helper; +} + +mowgli_eventloop_helper_proc_t * +mowgli_helper_spawn(mowgli_eventloop_t *eventloop, const char *path, char *const argv[]) +{ + mowgli_eventloop_helper_proc_t *helper; + int io_fd[2]; + char buf[64]; + + return_val_if_fail(eventloop != NULL, NULL); + return_val_if_fail(path != NULL, NULL); + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->eventloop = eventloop; + + socketpair(AF_UNIX, SOCK_STREAM, 0, io_fd); + + /* set up helper/child fd mapping */ + helper->fd = io_fd[0]; + + /* make pollables and make them non-blocking */ + helper->pfd = mowgli_pollable_create(eventloop, helper->fd, helper); + + snprintf(buf, sizeof buf, "%d", io_fd[1]); + setenv("IO_FD", buf, 1); + + /* Spawn helper process using mowgli_process_spawn(), helper will get + * IO_FD mapping from getenv(). Ugly hack, but it works... + * --nenolod + */ + helper->child = mowgli_process_spawn(path, argv); + + if (helper->child == NULL) + { + mowgli_pollable_destroy(eventloop, helper->pfd); + + close(io_fd[0]); + close(io_fd[1]); + + mowgli_free(helper); + return NULL; + } + + close(io_fd[1]); + + return helper; +} + +/* Set up a helper connection to parent using getenv() */ +mowgli_eventloop_helper_proc_t * +mowgli_helper_setup(mowgli_eventloop_t *eventloop) +{ + mowgli_eventloop_helper_proc_t *helper; + const char *env_io_fd; + + env_io_fd = getenv("IO_FD"); + + /* this shouldn't be a hard-fail because some idiot may run the helper from + * the cmdline. allow the helper to error out gracefully if not spawned as + * a helper. + */ + if (env_io_fd == NULL) + return NULL; + + helper = mowgli_alloc(sizeof(mowgli_eventloop_helper_proc_t)); + helper->type.type = MOWGLI_EVENTLOOP_TYPE_HELPER; + helper->eventloop = eventloop; + helper->fd = atoi(env_io_fd); + helper->pfd = mowgli_pollable_create(helper->eventloop, helper->fd, helper); + + mowgli_pollable_set_nonblocking(helper->pfd, true); + + return helper; +} + +static void +mowgli_helper_io_trampoline(mowgli_eventloop_t *eventloop, mowgli_eventloop_io_t *io, mowgli_eventloop_io_dir_t dir, void *userdata) +{ + mowgli_eventloop_helper_proc_t *helper = userdata; + + switch (dir) { + case MOWGLI_EVENTLOOP_IO_READ: + if (helper->read_function != NULL) + return helper->read_function(eventloop, helper, MOWGLI_EVENTLOOP_IO_READ, helper->userdata); + default: + break; + } +} + +void +mowgli_helper_set_read_cb(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_proc_t *helper, mowgli_eventloop_io_cb_t *read_fn) +{ + return_if_fail(eventloop != NULL); + return_if_fail(helper != NULL); + + if (read_fn == NULL) + mowgli_pollable_setselect(eventloop, helper->pfd, MOWGLI_EVENTLOOP_IO_READ, NULL); + + helper->read_function = read_fn; + mowgli_pollable_setselect(eventloop, helper->pfd, MOWGLI_EVENTLOOP_IO_READ, mowgli_helper_io_trampoline); +} + +void +mowgli_helper_destroy(mowgli_eventloop_t *eventloop, mowgli_eventloop_helper_proc_t *helper) +{ + return_if_fail(eventloop != NULL); + return_if_fail(helper != NULL); + + mowgli_process_kill(helper->child); + mowgli_pollable_destroy(eventloop, helper->pfd); + close(helper->fd); + + mowgli_free(helper); +} |