From 87c07559af5cfcd2752295ef7c425bd3205f426f Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Wed, 14 Dec 2011 14:32:05 -0500 Subject: Move child_common routines to util --- Makefile.am | 14 +- src/providers/child_common.c | 735 -------------------------------- src/providers/child_common.h | 109 ----- src/providers/data_provider_be.c | 2 +- src/providers/dp_backend.h | 2 +- src/providers/ipa/ipa_dyndns.c | 2 +- src/providers/ipa/ipa_init.c | 2 +- src/providers/krb5/krb5_auth.c | 2 +- src/providers/krb5/krb5_auth.h | 2 +- src/providers/krb5/krb5_child.c | 2 +- src/providers/krb5/krb5_child_handler.c | 2 +- src/providers/krb5/krb5_init.c | 2 +- src/providers/ldap/ldap_child.c | 2 +- src/providers/ldap/ldap_init.c | 2 +- src/providers/ldap/sdap_child_helpers.c | 2 +- src/util/child_common.c | 735 ++++++++++++++++++++++++++++++++ src/util/child_common.h | 109 +++++ 17 files changed, 863 insertions(+), 863 deletions(-) delete mode 100644 src/providers/child_common.c delete mode 100644 src/providers/child_common.h create mode 100644 src/util/child_common.c create mode 100644 src/util/child_common.h diff --git a/Makefile.am b/Makefile.am index 7ebf29f4..f6b340a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -327,7 +327,7 @@ dist_noinst_HEADERS = \ src/providers/dp_backend.h \ src/providers/fail_over.h \ src/providers/providers.h \ - src/providers/child_common.h \ + src/util/child_common.h \ src/providers/simple/simple_access.h \ src/providers/krb5/krb5_auth.h \ src/providers/krb5/krb5_common.h \ @@ -366,6 +366,9 @@ libsss_debug_la_SOURCES = \ src/util/debug.c \ src/util/sss_log.c +noinst_LTLIBRARIES += libsss_child.la +libsss_child_la_SOURCES = src/util/child_common.c + noinst_LTLIBRARIES += libsss_util.la libsss_util_la_SOURCES = \ src/confdb/confdb.c \ @@ -395,6 +398,7 @@ libsss_util_la_SOURCES = \ libsss_util_la_LIBADD = \ $(SSSD_LIBS) \ $(UNICODE_LIBS) \ + libsss_child.la \ libsss_crypt.la \ libsss_debug.la if BUILD_SUDO @@ -461,7 +465,6 @@ sssd_sudo_LDADD = \ endif sssd_be_SOURCES = \ - src/providers/child_common.c \ src/providers/data_provider_be.c \ src/providers/data_provider_fo.c \ src/providers/data_provider_opts.c \ @@ -870,7 +873,6 @@ dist_noinst_DATA += \ #################### libsss_ldap_la_SOURCES = \ src/util/find_uid.c \ - src/providers/child_common.c \ src/providers/ldap/ldap_id.c \ src/providers/ldap/ldap_id_enum.c \ src/providers/ldap/ldap_id_cleanup.c \ @@ -938,7 +940,6 @@ libsss_simple_la_LDFLAGS = \ libsss_krb5_la_SOURCES = \ src/util/find_uid.c \ - src/providers/child_common.c \ src/providers/krb5/krb5_utils.c \ src/providers/krb5/krb5_become_user.c \ src/providers/krb5/krb5_delayed_online_authentication.c \ @@ -963,7 +964,6 @@ libsss_krb5_la_LDFLAGS = \ -module libsss_ipa_la_SOURCES = \ - src/providers/child_common.c \ src/providers/ipa/ipa_init.c \ src/providers/ipa/ipa_common.c \ src/providers/ipa/ipa_utils.c \ @@ -1026,7 +1026,6 @@ libsss_ipa_la_LDFLAGS = \ krb5_child_SOURCES = \ src/providers/krb5/krb5_become_user.c \ src/providers/krb5/krb5_child.c \ - src/providers/child_common.c \ src/providers/dp_pam_data_util.c \ src/util/user_info_msg.c \ src/util/sss_krb5.c \ @@ -1038,6 +1037,7 @@ krb5_child_CFLAGS = \ $(KRB5_CFLAGS) krb5_child_LDADD = \ libsss_debug.la \ + libsss_child.la \ $(TALLOC_LIBS) \ $(TEVENT_LIBS) \ $(POPT_LIBS) \ @@ -1046,7 +1046,6 @@ krb5_child_LDADD = \ ldap_child_SOURCES = \ src/providers/ldap/ldap_child.c \ - src/providers/child_common.c \ src/util/sss_krb5.c \ src/util/util.c \ src/util/signal.c @@ -1056,6 +1055,7 @@ ldap_child_CFLAGS = \ $(KRB5_CFLAGS) ldap_child_LDADD = \ libsss_debug.la \ + libsss_child.la \ $(TALLOC_LIBS) \ $(TEVENT_LIBS) \ $(POPT_LIBS) \ diff --git a/src/providers/child_common.c b/src/providers/child_common.c deleted file mode 100644 index 34602a41..00000000 --- a/src/providers/child_common.c +++ /dev/null @@ -1,735 +0,0 @@ -/* - SSSD - - Common helper functions to be used in child processes - - Authors: - Sumit Bose - - Copyright (C) 2009 Red Hat - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include - -#include "util/util.h" -#include "util/find_uid.h" -#include "db/sysdb.h" -#include "providers/child_common.h" - -struct sss_sigchild_ctx { - struct tevent_context *ev; - hash_table_t *children; - int options; -}; - -struct sss_child_ctx { - pid_t pid; - sss_child_fn_t cb; - void *pvt; - struct sss_sigchild_ctx *sigchld_ctx; -}; - -errno_t sss_sigchld_init(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct sss_sigchild_ctx **child_ctx) -{ - errno_t ret; - struct sss_sigchild_ctx *sigchld_ctx; - struct tevent_signal *tes; - - sigchld_ctx = talloc_zero(mem_ctx, struct sss_sigchild_ctx); - if (!sigchld_ctx) { - DEBUG(0, ("fatal error initializing sss_sigchild_ctx\n")); - return ENOMEM; - } - sigchld_ctx->ev = ev; - - ret = sss_hash_create(sigchld_ctx, 10, &sigchld_ctx->children); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, - ("fatal error initializing children hash table: [%s]\n", - strerror(ret))); - talloc_free(sigchld_ctx); - return ret; - } - - BlockSignals(false, SIGCHLD); - tes = tevent_add_signal(ev, sigchld_ctx, SIGCHLD, SA_SIGINFO, - sss_child_handler, sigchld_ctx); - if (tes == NULL) { - talloc_free(sigchld_ctx); - return EIO; - } - - *child_ctx = sigchld_ctx; - return EOK; -} - -static int sss_child_destructor(void *ptr) -{ - struct sss_child_ctx *child_ctx; - hash_key_t key; - int error; - - child_ctx = talloc_get_type(ptr, struct sss_child_ctx); - key.type = HASH_KEY_ULONG; - key.ul = child_ctx->pid; - - error = hash_delete(child_ctx->sigchld_ctx->children, &key); - if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) { - DEBUG(SSSDBG_TRACE_INTERNAL, - ("failed to delete child_ctx from hash table [%d]: %s\n", - error, hash_error_string(error))); - } - - return 0; -} - -errno_t sss_child_register(TALLOC_CTX *mem_ctx, - struct sss_sigchild_ctx *sigchld_ctx, - pid_t pid, - sss_child_fn_t cb, - void *pvt, - struct sss_child_ctx **child_ctx) -{ - struct sss_child_ctx *child; - hash_key_t key; - hash_value_t value; - int error; - - child = talloc_zero(mem_ctx, struct sss_child_ctx); - if (child == NULL) { - return ENOMEM; - } - - child->pid = pid; - child->cb = cb; - child->pvt = pvt; - child->sigchld_ctx = sigchld_ctx; - - key.type = HASH_KEY_ULONG; - key.ul = pid; - - value.type = HASH_VALUE_PTR; - value.ptr = child; - - error = hash_enter(sigchld_ctx->children, &key, &value); - if (error != HASH_SUCCESS) { - talloc_free(child); - return ENOMEM; - } - - talloc_set_destructor((TALLOC_CTX *) child, sss_child_destructor); - - *child_ctx = child; - return EOK; -} - -struct sss_child_cb_pvt { - struct sss_child_ctx *child_ctx; - int wait_status; -}; - -static void sss_child_invoke_cb(struct tevent_context *ev, - struct tevent_immediate *imm, - void *pvt) -{ - struct sss_child_cb_pvt *cb_pvt; - struct sss_child_ctx *child_ctx; - hash_key_t key; - int error; - - cb_pvt = talloc_get_type(pvt, struct sss_child_cb_pvt); - child_ctx = cb_pvt->child_ctx; - - key.type = HASH_KEY_ULONG; - key.ul = child_ctx->pid; - - error = hash_delete(child_ctx->sigchld_ctx->children, &key); - if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) { - DEBUG(SSSDBG_OP_FAILURE, - ("failed to delete child_ctx from hash table [%d]: %s\n", - error, hash_error_string(error))); - } - - if (child_ctx->cb) { - child_ctx->cb(child_ctx->pid, cb_pvt->wait_status, child_ctx->pvt); - } -} - -void sss_child_handler(struct tevent_context *ev, - struct tevent_signal *se, - int signum, - int count, - void *siginfo, - void *private_data) -{ - struct sss_sigchild_ctx *sigchld_ctx; - struct tevent_immediate *imm; - struct sss_child_cb_pvt *invoke_pvt; - struct sss_child_ctx *child_ctx; - hash_key_t key; - hash_value_t value; - int error; - int wait_status; - pid_t pid; - - sigchld_ctx = talloc_get_type(private_data, struct sss_sigchild_ctx); - key.type = HASH_KEY_ULONG; - - do { - do { - errno = 0; - pid = waitpid(-1, &wait_status, WNOHANG | sigchld_ctx->options); - } while (pid == -1 && errno == EINTR); - - if (pid == -1) { - DEBUG(SSSDBG_TRACE_INTERNAL, - ("waitpid failed [%d]: %s\n", errno, strerror(errno))); - return; - } else if (pid == 0) continue; - - key.ul = pid; - error = hash_lookup(sigchld_ctx->children, &key, &value); - if (error == HASH_SUCCESS) { - child_ctx = talloc_get_type(value.ptr, struct sss_child_ctx); - - imm = tevent_create_immediate(sigchld_ctx->ev); - if (imm == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - ("Out of memory invoking SIGCHLD callback\n")); - return; - } - - invoke_pvt = talloc_zero(child_ctx, struct sss_child_cb_pvt); - if (invoke_pvt == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - ("out of memory invoking SIGCHLD callback\n")); - return; - } - invoke_pvt->child_ctx = child_ctx; - invoke_pvt->wait_status = wait_status; - - tevent_schedule_immediate(imm, sigchld_ctx->ev, - sss_child_invoke_cb, invoke_pvt); - } else if (error == HASH_ERROR_KEY_NOT_FOUND) { - DEBUG(SSSDBG_TRACE_LIBS, - ("BUG: waitpid() returned [%d] but it was not in the table. " - "This could be due to a linked library creating processes " - "without registering them with the sigchld handler\n", - pid)); - /* We will simply ignore this and return to the loop - * This will prevent a zombie, but may cause unexpected - * behavior in the code that was trying to handle this - * pid. - */ - } else { - DEBUG(SSSDBG_OP_FAILURE, - ("SIGCHLD hash table error [%d]: %s\n", - error, hash_error_string(error))); - /* This is bad, but we should try to check for other - * children anyway, to avoid potential zombies. - */ - } - } while (pid != 0); -} - -struct sss_child_ctx_old { - struct tevent_signal *sige; - pid_t pid; - int child_status; - sss_child_callback_t cb; - void *pvt; -}; - -int child_handler_setup(struct tevent_context *ev, int pid, - sss_child_callback_t cb, void *pvt) -{ - struct sss_child_ctx_old *child_ctx; - - DEBUG(8, ("Setting up signal handler up for pid [%d]\n", pid)); - - child_ctx = talloc_zero(ev, struct sss_child_ctx_old); - if (child_ctx == NULL) { - return ENOMEM; - } - - child_ctx->sige = tevent_add_signal(ev, child_ctx, SIGCHLD, SA_SIGINFO, - child_sig_handler, child_ctx); - if(!child_ctx->sige) { - /* Error setting up signal handler */ - talloc_free(child_ctx); - return ENOMEM; - } - - child_ctx->pid = pid; - child_ctx->cb = cb; - child_ctx->pvt = pvt; - - DEBUG(8, ("Signal handler set up for pid [%d]\n", pid)); - return EOK; -} - -/* Async communication with the child process via a pipe */ - -struct write_pipe_state { - int fd; - uint8_t *buf; - size_t len; - size_t written; -}; - -static void write_pipe_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *pvt); - -struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - uint8_t *buf, size_t len, int fd) -{ - struct tevent_req *req; - struct write_pipe_state *state; - struct tevent_fd *fde; - - req = tevent_req_create(mem_ctx, &state, struct write_pipe_state); - if (req == NULL) return NULL; - - state->fd = fd; - state->buf = buf; - state->len = len; - state->written = 0; - - fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE, - write_pipe_handler, req); - if (fde == NULL) { - DEBUG(1, ("tevent_add_fd failed.\n")); - goto fail; - } - - return req; - -fail: - talloc_zfree(req); - return NULL; -} - -static void write_pipe_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *pvt) -{ - struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); - struct write_pipe_state *state = tevent_req_data(req, - struct write_pipe_state); - ssize_t size; - - if (flags & TEVENT_FD_READ) { - DEBUG(1, ("write_pipe_done called with TEVENT_FD_READ," - " this should not happen.\n")); - tevent_req_error(req, EINVAL); - return; - } - - size = write(state->fd, - state->buf + state->written, - state->len - state->written); - if (size == -1) { - if (errno == EAGAIN || errno == EINTR) return; - DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); - tevent_req_error(req, errno); - return; - - } else if (size >= 0) { - state->written += size; - if (state->written > state->len) { - DEBUG(1, ("write to much, this should never happen.\n")); - tevent_req_error(req, EINVAL); - return; - } - } else { - DEBUG(1, ("unexpected return value of write [%d].\n", size)); - tevent_req_error(req, EINVAL); - return; - } - - if (state->len == state->written) { - DEBUG(6, ("All data has been sent!\n")); - tevent_req_done(req); - return; - } -} - -int write_pipe_recv(struct tevent_req *req) -{ - TEVENT_REQ_RETURN_ON_ERROR(req); - - return EOK; -} - -struct read_pipe_state { - int fd; - uint8_t *buf; - size_t len; -}; - -static void read_pipe_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *pvt); - -struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, int fd) -{ - struct tevent_req *req; - struct read_pipe_state *state; - struct tevent_fd *fde; - - req = tevent_req_create(mem_ctx, &state, struct read_pipe_state); - if (req == NULL) return NULL; - - state->fd = fd; - state->buf = NULL; - state->len = 0; - - fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ, - read_pipe_handler, req); - if (fde == NULL) { - DEBUG(1, ("tevent_add_fd failed.\n")); - goto fail; - } - - return req; - -fail: - talloc_zfree(req); - return NULL; -} - -static void read_pipe_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *pvt) -{ - struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); - struct read_pipe_state *state = tevent_req_data(req, - struct read_pipe_state); - ssize_t size; - errno_t err; - uint8_t buf[CHILD_MSG_CHUNK]; - - if (flags & TEVENT_FD_WRITE) { - DEBUG(1, ("read_pipe_done called with TEVENT_FD_WRITE," - " this should not happen.\n")); - tevent_req_error(req, EINVAL); - return; - } - - size = read(state->fd, - buf, - CHILD_MSG_CHUNK); - if (size == -1) { - err = errno; - if (err == EAGAIN || err == EINTR) { - return; - } - - DEBUG(1, ("read failed [%d][%s].\n", err, strerror(err))); - tevent_req_error(req, err); - return; - - } else if (size > 0) { - state->buf = talloc_realloc(state, state->buf, uint8_t, - state->len + size); - if(!state->buf) { - tevent_req_error(req, ENOMEM); - return; - } - - safealign_memcpy(&state->buf[state->len], buf, - size, &state->len); - return; - - } else if (size == 0) { - DEBUG(6, ("EOF received, client finished\n")); - tevent_req_done(req); - return; - - } else { - DEBUG(1, ("unexpected return value of read [%d].\n", size)); - tevent_req_error(req, EINVAL); - return; - } -} - -int read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - uint8_t **buf, ssize_t *len) -{ - struct read_pipe_state *state; - state = tevent_req_data(req, struct read_pipe_state); - - TEVENT_REQ_RETURN_ON_ERROR(req); - - *buf = talloc_steal(mem_ctx, state->buf); - *len = state->len; - - return EOK; -} - -/* The pipes to communicate with the child must be nonblocking */ -void fd_nonblocking(int fd) -{ - int flags; - int ret; - - flags = fcntl(fd, F_GETFL, 0); - if (flags == -1) { - ret = errno; - DEBUG(1, ("F_GETFL failed [%d][%s].\n", ret, strerror(ret))); - return; - } - - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { - ret = errno; - DEBUG(1, ("F_SETFL failed [%d][%s].\n", ret, strerror(ret))); - } - - return; -} - -static void child_invoke_callback(struct tevent_context *ev, - struct tevent_immediate *imm, - void *pvt); -void child_sig_handler(struct tevent_context *ev, - struct tevent_signal *sige, int signum, - int count, void *__siginfo, void *pvt) -{ - int ret, err; - struct sss_child_ctx_old *child_ctx; - struct tevent_immediate *imm; - - if (count <= 0) { - DEBUG(0, ("SIGCHLD handler called with invalid child count\n")); - return; - } - - child_ctx = talloc_get_type(pvt, struct sss_child_ctx_old); - DEBUG(7, ("Waiting for child [%d].\n", child_ctx->pid)); - - errno = 0; - ret = waitpid(child_ctx->pid, &child_ctx->child_status, WNOHANG); - - if (ret == -1) { - err = errno; - DEBUG(1, ("waitpid failed [%d][%s].\n", err, strerror(err))); - } else if (ret == 0) { - DEBUG(1, ("waitpid did not found a child with changed status.\n")); - } else { - if WIFEXITED(child_ctx->child_status) { - if (WEXITSTATUS(child_ctx->child_status) != 0) { - DEBUG(1, ("child [%d] failed with status [%d].\n", ret, - WEXITSTATUS(child_ctx->child_status))); - } else { - DEBUG(4, ("child [%d] finished successfully.\n", ret)); - } - } else if WIFSIGNALED(child_ctx->child_status) { - DEBUG(1, ("child [%d] was terminated by signal [%d].\n", ret, - WTERMSIG(child_ctx->child_status))); - } else { - if WIFSTOPPED(child_ctx->child_status) { - DEBUG(7, ("child [%d] was stopped by signal [%d].\n", ret, - WSTOPSIG(child_ctx->child_status))); - } - if WIFCONTINUED(child_ctx->child_status) { - DEBUG(7, ("child [%d] was resumed by delivery of SIGCONT.\n", - ret)); - } - - return; - } - - /* Invoke the callback in a tevent_immediate handler - * so that it is safe to free the tevent_signal * - */ - imm = tevent_create_immediate(ev); - if (imm == NULL) { - DEBUG(0, ("Out of memory invoking sig handler callback\n")); - return; - } - - tevent_schedule_immediate(imm, ev,child_invoke_callback, - child_ctx); - } - - return; -} - -static void child_invoke_callback(struct tevent_context *ev, - struct tevent_immediate *imm, - void *pvt) -{ - struct sss_child_ctx_old *child_ctx = - talloc_get_type(pvt, struct sss_child_ctx_old); - if (child_ctx->cb) { - child_ctx->cb(child_ctx->child_status, child_ctx->sige, child_ctx->pvt); - } - - /* Stop monitoring for this child */ - talloc_free(child_ctx); -} - -static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx, - int child_debug_fd, - const char *binary, - char ***_argv) -{ - /* - * program name, debug_level, debug_timestamps, - * debug_microseconds and NULL - */ - uint_t argc = 5; - char ** argv; - errno_t ret = EINVAL; - - /* Save the current state in case an interrupt changes it */ - bool child_debug_to_file = debug_to_file; - bool child_debug_timestamps = debug_timestamps; - bool child_debug_microseconds = debug_microseconds; - - if (child_debug_to_file) argc++; - - /* - * program name, debug_level, debug_to_file, debug_timestamps, - * debug_microseconds and NULL - */ - argv = talloc_array(mem_ctx, char *, argc); - if (argv == NULL) { - DEBUG(1, ("talloc_array failed.\n")); - return ENOMEM; - } - - argv[--argc] = NULL; - - argv[--argc] = talloc_asprintf(argv, "--debug-level=%#.4x", - debug_level); - if (argv[argc] == NULL) { - ret = ENOMEM; - goto fail; - } - - if (child_debug_to_file) { - argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d", - child_debug_fd); - if (argv[argc] == NULL) { - ret = ENOMEM; - goto fail; - } - } - - argv[--argc] = talloc_asprintf(argv, "--debug-timestamps=%d", - child_debug_timestamps); - if (argv[argc] == NULL) { - ret = ENOMEM; - goto fail; - } - - argv[--argc] = talloc_asprintf(argv, "--debug-microseconds=%d", - child_debug_microseconds); - if (argv[argc] == NULL) { - ret = ENOMEM; - goto fail; - } - - argv[--argc] = talloc_strdup(argv, binary); - if (argv[argc] == NULL) { - ret = ENOMEM; - goto fail; - } - - if (argc != 0) { - ret = EINVAL; - goto fail; - } - - *_argv = argv; - - return EOK; - -fail: - talloc_free(argv); - return ret; -} - -errno_t exec_child(TALLOC_CTX *mem_ctx, - int *pipefd_to_child, int *pipefd_from_child, - const char *binary, int debug_fd) -{ - int ret; - errno_t err; - char **argv; - - close(pipefd_to_child[1]); - ret = dup2(pipefd_to_child[0], STDIN_FILENO); - if (ret == -1) { - err = errno; - DEBUG(1, ("dup2 failed [%d][%s].\n", err, strerror(err))); - return err; - } - - close(pipefd_from_child[0]); - ret = dup2(pipefd_from_child[1], STDOUT_FILENO); - if (ret == -1) { - err = errno; - DEBUG(1, ("dup2 failed [%d][%s].\n", err, strerror(err))); - return err; - } - - ret = prepare_child_argv(mem_ctx, debug_fd, - binary, &argv); - if (ret != EOK) { - DEBUG(1, ("prepare_child_argv.\n")); - return ret; - } - - ret = execv(binary, argv); - if (ret == -1) { - err = errno; - DEBUG(1, ("execv failed [%d][%s].\n", err, strerror(err))); - return err; - } - - return EOK; -} - -void child_cleanup(int readfd, int writefd) -{ - int ret; - - if (readfd != -1) { - ret = close(readfd); - if (ret != EOK) { - ret = errno; - DEBUG(1, ("close failed [%d][%s].\n", ret, strerror(ret))); - } - } - if (writefd != -1) { - ret = close(writefd); - if (ret != EOK) { - ret = errno; - DEBUG(1, ("close failed [%d][%s].\n", ret, strerror(ret))); - } - } -} diff --git a/src/providers/child_common.h b/src/providers/child_common.h deleted file mode 100644 index 1e9f1b6c..00000000 --- a/src/providers/child_common.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - SSSD - - Common helper functions to be used in child processes - - Authors: - Sumit Bose - - Copyright (C) 2009 Red Hat - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __CHILD_COMMON_H__ -#define __CHILD_COMMON_H__ - -#include -#include -#include -#include - -#include "util/util.h" - -#define IN_BUF_SIZE 512 -#define CHILD_MSG_CHUNK 256 - -struct response { - uint8_t *buf; - size_t size; -}; - -struct io_buffer { - uint8_t *data; - size_t size; -}; - -/* COMMON SIGCHLD HANDLING */ -typedef void (*sss_child_fn_t)(int pid, int wait_status, void *pvt); - -struct sss_sigchild_ctx; -struct sss_child_ctx; - -/* Create a new child context to manage callbacks */ -errno_t sss_sigchld_init(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct sss_sigchild_ctx **child_ctx); - -errno_t sss_child_register(TALLOC_CTX *mem_ctx, - struct sss_sigchild_ctx *sigchld_ctx, - pid_t pid, - sss_child_fn_t cb, - void *pvt, - struct sss_child_ctx **child_ctx); - -void sss_child_handler(struct tevent_context *ev, - struct tevent_signal *se, - int signum, - int count, - void *siginfo, - void *private_data); - -/* Callback to be invoked when a sigchld handler is called. - * The tevent_signal * associated with the handler will be - * freed automatically when this function returns. - */ -typedef void (*sss_child_callback_t)(int child_status, - struct tevent_signal *sige, - void *pvt); - -/* Set up child termination signal handler */ -int child_handler_setup(struct tevent_context *ev, int pid, - sss_child_callback_t cb, void *pvt); - -/* Async communication with the child process via a pipe */ -struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - uint8_t *buf, size_t len, int fd); -int write_pipe_recv(struct tevent_req *req); - -struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, int fd); -int read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - uint8_t **buf, ssize_t *len); - -/* The pipes to communicate with the child must be nonblocking */ -void fd_nonblocking(int fd); - -void child_sig_handler(struct tevent_context *ev, - struct tevent_signal *sige, int signum, - int count, void *__siginfo, void *pvt); - -errno_t exec_child(TALLOC_CTX *mem_ctx, - int *pipefd_to_child, int *pipefd_from_child, - const char *binary, int debug_fd); - -void child_cleanup(int readfd, int writefd); - -#endif /* __CHILD_COMMON_H__ */ diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c index fb4da87c..bf77c5f6 100644 --- a/src/providers/data_provider_be.c +++ b/src/providers/data_provider_be.c @@ -42,7 +42,7 @@ #include "sbus/sssd_dbus.h" #include "providers/dp_backend.h" #include "providers/fail_over.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "resolv/async_resolv.h" #include "monitor/monitor_interfaces.h" diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h index 3d1c6bf2..f462b82d 100644 --- a/src/providers/dp_backend.h +++ b/src/providers/dp_backend.h @@ -24,7 +24,7 @@ #include "providers/data_provider.h" #include "providers/fail_over.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "db/sysdb.h" /* a special token, if used in place of the hostname, denotes that real diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c index e579e1d9..60bc6ec0 100644 --- a/src/providers/ipa/ipa_dyndns.c +++ b/src/providers/ipa/ipa_dyndns.c @@ -33,7 +33,7 @@ #include "confdb/confdb.h" #include "providers/ipa/ipa_common.h" #include "providers/ipa/ipa_dyndns.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/data_provider.h" #include "providers/ldap/ldap_common.h" #include "providers/ldap/sdap_async_private.h" diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c index 57b4180c..9acee7bf 100644 --- a/src/providers/ipa/ipa_init.c +++ b/src/providers/ipa/ipa_init.c @@ -27,7 +27,7 @@ #include #include -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/ipa/ipa_common.h" #include "providers/krb5/krb5_auth.h" #include "providers/ipa/ipa_id.h" diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c index f177be51..6aaf7fbe 100644 --- a/src/providers/krb5/krb5_auth.c +++ b/src/providers/krb5/krb5_auth.c @@ -35,7 +35,7 @@ #include "util/util.h" #include "util/find_uid.h" #include "db/sysdb.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_utils.h" diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h index 0d6318d1..89b77d36 100644 --- a/src/providers/krb5/krb5_auth.h +++ b/src/providers/krb5/krb5_auth.h @@ -30,7 +30,7 @@ #include "util/sss_krb5.h" #include "providers/dp_backend.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/krb5/krb5_common.h" #define CCACHE_ENV_NAME "KRB5CCNAME" diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index fe872109..01690cf4 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -32,7 +32,7 @@ #include "util/util.h" #include "util/sss_krb5.h" #include "util/user_info_msg.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/dp_backend.h" #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_utils.h" diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c index bafa0bbf..990a9ecc 100644 --- a/src/providers/krb5/krb5_child_handler.c +++ b/src/providers/krb5/krb5_child_handler.c @@ -23,7 +23,7 @@ */ #include "util/util.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/krb5/krb5_common.h" #include "providers/krb5/krb5_auth.h" #include "src/providers/krb5/krb5_utils.h" diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c index 6176dd18..3c39d847 100644 --- a/src/providers/krb5/krb5_init.c +++ b/src/providers/krb5/krb5_init.c @@ -26,7 +26,7 @@ #include #include #include -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_common.h" diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c index 02c7e557..160cc1ce 100644 --- a/src/providers/ldap/ldap_child.c +++ b/src/providers/ldap/ldap_child.c @@ -31,7 +31,7 @@ #include "util/util.h" #include "util/sss_krb5.h" -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/dp_backend.h" static krb5_context krb5_error_ctx; diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c index e7be90d9..8c4d3e68 100644 --- a/src/providers/ldap/ldap_init.c +++ b/src/providers/ldap/ldap_init.c @@ -22,7 +22,7 @@ along with this program. If not, see . */ -#include "providers/child_common.h" +#include "util/child_common.h" #include "providers/ldap/ldap_common.h" #include "providers/ldap/sdap_async_private.h" #include "providers/ldap/sdap_access.h" diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c index 5990fc3a..704c89ec 100644 --- a/src/providers/ldap/sdap_child_helpers.c +++ b/src/providers/ldap/sdap_child_helpers.c @@ -32,7 +32,7 @@ #include "util/sss_krb5.h" #include "providers/ldap/ldap_common.h" #include "providers/ldap/sdap_async_private.h" -#include "providers/child_common.h" +#include "util/child_common.h" #ifndef SSSD_LIBEXEC_PATH #error "SSSD_LIBEXEC_PATH not defined" diff --git a/src/util/child_common.c b/src/util/child_common.c new file mode 100644 index 00000000..6214c7cc --- /dev/null +++ b/src/util/child_common.c @@ -0,0 +1,735 @@ +/* + SSSD + + Common helper functions to be used in child processes + + Authors: + Sumit Bose + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include + +#include "util/util.h" +#include "util/find_uid.h" +#include "db/sysdb.h" +#include "util/child_common.h" + +struct sss_sigchild_ctx { + struct tevent_context *ev; + hash_table_t *children; + int options; +}; + +struct sss_child_ctx { + pid_t pid; + sss_child_fn_t cb; + void *pvt; + struct sss_sigchild_ctx *sigchld_ctx; +}; + +errno_t sss_sigchld_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_sigchild_ctx **child_ctx) +{ + errno_t ret; + struct sss_sigchild_ctx *sigchld_ctx; + struct tevent_signal *tes; + + sigchld_ctx = talloc_zero(mem_ctx, struct sss_sigchild_ctx); + if (!sigchld_ctx) { + DEBUG(0, ("fatal error initializing sss_sigchild_ctx\n")); + return ENOMEM; + } + sigchld_ctx->ev = ev; + + ret = sss_hash_create(sigchld_ctx, 10, &sigchld_ctx->children); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("fatal error initializing children hash table: [%s]\n", + strerror(ret))); + talloc_free(sigchld_ctx); + return ret; + } + + BlockSignals(false, SIGCHLD); + tes = tevent_add_signal(ev, sigchld_ctx, SIGCHLD, SA_SIGINFO, + sss_child_handler, sigchld_ctx); + if (tes == NULL) { + talloc_free(sigchld_ctx); + return EIO; + } + + *child_ctx = sigchld_ctx; + return EOK; +} + +static int sss_child_destructor(void *ptr) +{ + struct sss_child_ctx *child_ctx; + hash_key_t key; + int error; + + child_ctx = talloc_get_type(ptr, struct sss_child_ctx); + key.type = HASH_KEY_ULONG; + key.ul = child_ctx->pid; + + error = hash_delete(child_ctx->sigchld_ctx->children, &key); + if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("failed to delete child_ctx from hash table [%d]: %s\n", + error, hash_error_string(error))); + } + + return 0; +} + +errno_t sss_child_register(TALLOC_CTX *mem_ctx, + struct sss_sigchild_ctx *sigchld_ctx, + pid_t pid, + sss_child_fn_t cb, + void *pvt, + struct sss_child_ctx **child_ctx) +{ + struct sss_child_ctx *child; + hash_key_t key; + hash_value_t value; + int error; + + child = talloc_zero(mem_ctx, struct sss_child_ctx); + if (child == NULL) { + return ENOMEM; + } + + child->pid = pid; + child->cb = cb; + child->pvt = pvt; + child->sigchld_ctx = sigchld_ctx; + + key.type = HASH_KEY_ULONG; + key.ul = pid; + + value.type = HASH_VALUE_PTR; + value.ptr = child; + + error = hash_enter(sigchld_ctx->children, &key, &value); + if (error != HASH_SUCCESS) { + talloc_free(child); + return ENOMEM; + } + + talloc_set_destructor((TALLOC_CTX *) child, sss_child_destructor); + + *child_ctx = child; + return EOK; +} + +struct sss_child_cb_pvt { + struct sss_child_ctx *child_ctx; + int wait_status; +}; + +static void sss_child_invoke_cb(struct tevent_context *ev, + struct tevent_immediate *imm, + void *pvt) +{ + struct sss_child_cb_pvt *cb_pvt; + struct sss_child_ctx *child_ctx; + hash_key_t key; + int error; + + cb_pvt = talloc_get_type(pvt, struct sss_child_cb_pvt); + child_ctx = cb_pvt->child_ctx; + + key.type = HASH_KEY_ULONG; + key.ul = child_ctx->pid; + + error = hash_delete(child_ctx->sigchld_ctx->children, &key); + if (error != HASH_SUCCESS && error != HASH_ERROR_KEY_NOT_FOUND) { + DEBUG(SSSDBG_OP_FAILURE, + ("failed to delete child_ctx from hash table [%d]: %s\n", + error, hash_error_string(error))); + } + + if (child_ctx->cb) { + child_ctx->cb(child_ctx->pid, cb_pvt->wait_status, child_ctx->pvt); + } +} + +void sss_child_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + struct sss_sigchild_ctx *sigchld_ctx; + struct tevent_immediate *imm; + struct sss_child_cb_pvt *invoke_pvt; + struct sss_child_ctx *child_ctx; + hash_key_t key; + hash_value_t value; + int error; + int wait_status; + pid_t pid; + + sigchld_ctx = talloc_get_type(private_data, struct sss_sigchild_ctx); + key.type = HASH_KEY_ULONG; + + do { + do { + errno = 0; + pid = waitpid(-1, &wait_status, WNOHANG | sigchld_ctx->options); + } while (pid == -1 && errno == EINTR); + + if (pid == -1) { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("waitpid failed [%d]: %s\n", errno, strerror(errno))); + return; + } else if (pid == 0) continue; + + key.ul = pid; + error = hash_lookup(sigchld_ctx->children, &key, &value); + if (error == HASH_SUCCESS) { + child_ctx = talloc_get_type(value.ptr, struct sss_child_ctx); + + imm = tevent_create_immediate(sigchld_ctx->ev); + if (imm == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Out of memory invoking SIGCHLD callback\n")); + return; + } + + invoke_pvt = talloc_zero(child_ctx, struct sss_child_cb_pvt); + if (invoke_pvt == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("out of memory invoking SIGCHLD callback\n")); + return; + } + invoke_pvt->child_ctx = child_ctx; + invoke_pvt->wait_status = wait_status; + + tevent_schedule_immediate(imm, sigchld_ctx->ev, + sss_child_invoke_cb, invoke_pvt); + } else if (error == HASH_ERROR_KEY_NOT_FOUND) { + DEBUG(SSSDBG_TRACE_LIBS, + ("BUG: waitpid() returned [%d] but it was not in the table. " + "This could be due to a linked library creating processes " + "without registering them with the sigchld handler\n", + pid)); + /* We will simply ignore this and return to the loop + * This will prevent a zombie, but may cause unexpected + * behavior in the code that was trying to handle this + * pid. + */ + } else { + DEBUG(SSSDBG_OP_FAILURE, + ("SIGCHLD hash table error [%d]: %s\n", + error, hash_error_string(error))); + /* This is bad, but we should try to check for other + * children anyway, to avoid potential zombies. + */ + } + } while (pid != 0); +} + +struct sss_child_ctx_old { + struct tevent_signal *sige; + pid_t pid; + int child_status; + sss_child_callback_t cb; + void *pvt; +}; + +int child_handler_setup(struct tevent_context *ev, int pid, + sss_child_callback_t cb, void *pvt) +{ + struct sss_child_ctx_old *child_ctx; + + DEBUG(8, ("Setting up signal handler up for pid [%d]\n", pid)); + + child_ctx = talloc_zero(ev, struct sss_child_ctx_old); + if (child_ctx == NULL) { + return ENOMEM; + } + + child_ctx->sige = tevent_add_signal(ev, child_ctx, SIGCHLD, SA_SIGINFO, + child_sig_handler, child_ctx); + if(!child_ctx->sige) { + /* Error setting up signal handler */ + talloc_free(child_ctx); + return ENOMEM; + } + + child_ctx->pid = pid; + child_ctx->cb = cb; + child_ctx->pvt = pvt; + + DEBUG(8, ("Signal handler set up for pid [%d]\n", pid)); + return EOK; +} + +/* Async communication with the child process via a pipe */ + +struct write_pipe_state { + int fd; + uint8_t *buf; + size_t len; + size_t written; +}; + +static void write_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt); + +struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + uint8_t *buf, size_t len, int fd) +{ + struct tevent_req *req; + struct write_pipe_state *state; + struct tevent_fd *fde; + + req = tevent_req_create(mem_ctx, &state, struct write_pipe_state); + if (req == NULL) return NULL; + + state->fd = fd; + state->buf = buf; + state->len = len; + state->written = 0; + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE, + write_pipe_handler, req); + if (fde == NULL) { + DEBUG(1, ("tevent_add_fd failed.\n")); + goto fail; + } + + return req; + +fail: + talloc_zfree(req); + return NULL; +} + +static void write_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct write_pipe_state *state = tevent_req_data(req, + struct write_pipe_state); + ssize_t size; + + if (flags & TEVENT_FD_READ) { + DEBUG(1, ("write_pipe_done called with TEVENT_FD_READ," + " this should not happen.\n")); + tevent_req_error(req, EINVAL); + return; + } + + size = write(state->fd, + state->buf + state->written, + state->len - state->written); + if (size == -1) { + if (errno == EAGAIN || errno == EINTR) return; + DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno))); + tevent_req_error(req, errno); + return; + + } else if (size >= 0) { + state->written += size; + if (state->written > state->len) { + DEBUG(1, ("write to much, this should never happen.\n")); + tevent_req_error(req, EINVAL); + return; + } + } else { + DEBUG(1, ("unexpected return value of write [%d].\n", size)); + tevent_req_error(req, EINVAL); + return; + } + + if (state->len == state->written) { + DEBUG(6, ("All data has been sent!\n")); + tevent_req_done(req); + return; + } +} + +int write_pipe_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +struct read_pipe_state { + int fd; + uint8_t *buf; + size_t len; +}; + +static void read_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt); + +struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, int fd) +{ + struct tevent_req *req; + struct read_pipe_state *state; + struct tevent_fd *fde; + + req = tevent_req_create(mem_ctx, &state, struct read_pipe_state); + if (req == NULL) return NULL; + + state->fd = fd; + state->buf = NULL; + state->len = 0; + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ, + read_pipe_handler, req); + if (fde == NULL) { + DEBUG(1, ("tevent_add_fd failed.\n")); + goto fail; + } + + return req; + +fail: + talloc_zfree(req); + return NULL; +} + +static void read_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct read_pipe_state *state = tevent_req_data(req, + struct read_pipe_state); + ssize_t size; + errno_t err; + uint8_t buf[CHILD_MSG_CHUNK]; + + if (flags & TEVENT_FD_WRITE) { + DEBUG(1, ("read_pipe_done called with TEVENT_FD_WRITE," + " this should not happen.\n")); + tevent_req_error(req, EINVAL); + return; + } + + size = read(state->fd, + buf, + CHILD_MSG_CHUNK); + if (size == -1) { + err = errno; + if (err == EAGAIN || err == EINTR) { + return; + } + + DEBUG(1, ("read failed [%d][%s].\n", err, strerror(err))); + tevent_req_error(req, err); + return; + + } else if (size > 0) { + state->buf = talloc_realloc(state, state->buf, uint8_t, + state->len + size); + if(!state->buf) { + tevent_req_error(req, ENOMEM); + return; + } + + safealign_memcpy(&state->buf[state->len], buf, + size, &state->len); + return; + + } else if (size == 0) { + DEBUG(6, ("EOF received, client finished\n")); + tevent_req_done(req); + return; + + } else { + DEBUG(1, ("unexpected return value of read [%d].\n", size)); + tevent_req_error(req, EINVAL); + return; + } +} + +int read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf, ssize_t *len) +{ + struct read_pipe_state *state; + state = tevent_req_data(req, struct read_pipe_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *buf = talloc_steal(mem_ctx, state->buf); + *len = state->len; + + return EOK; +} + +/* The pipes to communicate with the child must be nonblocking */ +void fd_nonblocking(int fd) +{ + int flags; + int ret; + + flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + ret = errno; + DEBUG(1, ("F_GETFL failed [%d][%s].\n", ret, strerror(ret))); + return; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + ret = errno; + DEBUG(1, ("F_SETFL failed [%d][%s].\n", ret, strerror(ret))); + } + + return; +} + +static void child_invoke_callback(struct tevent_context *ev, + struct tevent_immediate *imm, + void *pvt); +void child_sig_handler(struct tevent_context *ev, + struct tevent_signal *sige, int signum, + int count, void *__siginfo, void *pvt) +{ + int ret, err; + struct sss_child_ctx_old *child_ctx; + struct tevent_immediate *imm; + + if (count <= 0) { + DEBUG(0, ("SIGCHLD handler called with invalid child count\n")); + return; + } + + child_ctx = talloc_get_type(pvt, struct sss_child_ctx_old); + DEBUG(7, ("Waiting for child [%d].\n", child_ctx->pid)); + + errno = 0; + ret = waitpid(child_ctx->pid, &child_ctx->child_status, WNOHANG); + + if (ret == -1) { + err = errno; + DEBUG(1, ("waitpid failed [%d][%s].\n", err, strerror(err))); + } else if (ret == 0) { + DEBUG(1, ("waitpid did not found a child with changed status.\n")); + } else { + if WIFEXITED(child_ctx->child_status) { + if (WEXITSTATUS(child_ctx->child_status) != 0) { + DEBUG(1, ("child [%d] failed with status [%d].\n", ret, + WEXITSTATUS(child_ctx->child_status))); + } else { + DEBUG(4, ("child [%d] finished successfully.\n", ret)); + } + } else if WIFSIGNALED(child_ctx->child_status) { + DEBUG(1, ("child [%d] was terminated by signal [%d].\n", ret, + WTERMSIG(child_ctx->child_status))); + } else { + if WIFSTOPPED(child_ctx->child_status) { + DEBUG(7, ("child [%d] was stopped by signal [%d].\n", ret, + WSTOPSIG(child_ctx->child_status))); + } + if WIFCONTINUED(child_ctx->child_status) { + DEBUG(7, ("child [%d] was resumed by delivery of SIGCONT.\n", + ret)); + } + + return; + } + + /* Invoke the callback in a tevent_immediate handler + * so that it is safe to free the tevent_signal * + */ + imm = tevent_create_immediate(ev); + if (imm == NULL) { + DEBUG(0, ("Out of memory invoking sig handler callback\n")); + return; + } + + tevent_schedule_immediate(imm, ev,child_invoke_callback, + child_ctx); + } + + return; +} + +static void child_invoke_callback(struct tevent_context *ev, + struct tevent_immediate *imm, + void *pvt) +{ + struct sss_child_ctx_old *child_ctx = + talloc_get_type(pvt, struct sss_child_ctx_old); + if (child_ctx->cb) { + child_ctx->cb(child_ctx->child_status, child_ctx->sige, child_ctx->pvt); + } + + /* Stop monitoring for this child */ + talloc_free(child_ctx); +} + +static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx, + int child_debug_fd, + const char *binary, + char ***_argv) +{ + /* + * program name, debug_level, debug_timestamps, + * debug_microseconds and NULL + */ + uint_t argc = 5; + char ** argv; + errno_t ret = EINVAL; + + /* Save the current state in case an interrupt changes it */ + bool child_debug_to_file = debug_to_file; + bool child_debug_timestamps = debug_timestamps; + bool child_debug_microseconds = debug_microseconds; + + if (child_debug_to_file) argc++; + + /* + * program name, debug_level, debug_to_file, debug_timestamps, + * debug_microseconds and NULL + */ + argv = talloc_array(mem_ctx, char *, argc); + if (argv == NULL) { + DEBUG(1, ("talloc_array failed.\n")); + return ENOMEM; + } + + argv[--argc] = NULL; + + argv[--argc] = talloc_asprintf(argv, "--debug-level=%#.4x", + debug_level); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + + if (child_debug_to_file) { + argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d", + child_debug_fd); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + } + + argv[--argc] = talloc_asprintf(argv, "--debug-timestamps=%d", + child_debug_timestamps); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + + argv[--argc] = talloc_asprintf(argv, "--debug-microseconds=%d", + child_debug_microseconds); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + + argv[--argc] = talloc_strdup(argv, binary); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + + if (argc != 0) { + ret = EINVAL; + goto fail; + } + + *_argv = argv; + + return EOK; + +fail: + talloc_free(argv); + return ret; +} + +errno_t exec_child(TALLOC_CTX *mem_ctx, + int *pipefd_to_child, int *pipefd_from_child, + const char *binary, int debug_fd) +{ + int ret; + errno_t err; + char **argv; + + close(pipefd_to_child[1]); + ret = dup2(pipefd_to_child[0], STDIN_FILENO); + if (ret == -1) { + err = errno; + DEBUG(1, ("dup2 failed [%d][%s].\n", err, strerror(err))); + return err; + } + + close(pipefd_from_child[0]); + ret = dup2(pipefd_from_child[1], STDOUT_FILENO); + if (ret == -1) { + err = errno; + DEBUG(1, ("dup2 failed [%d][%s].\n", err, strerror(err))); + return err; + } + + ret = prepare_child_argv(mem_ctx, debug_fd, + binary, &argv); + if (ret != EOK) { + DEBUG(1, ("prepare_child_argv.\n")); + return ret; + } + + ret = execv(binary, argv); + if (ret == -1) { + err = errno; + DEBUG(1, ("execv failed [%d][%s].\n", err, strerror(err))); + return err; + } + + return EOK; +} + +void child_cleanup(int readfd, int writefd) +{ + int ret; + + if (readfd != -1) { + ret = close(readfd); + if (ret != EOK) { + ret = errno; + DEBUG(1, ("close failed [%d][%s].\n", ret, strerror(ret))); + } + } + if (writefd != -1) { + ret = close(writefd); + if (ret != EOK) { + ret = errno; + DEBUG(1, ("close failed [%d][%s].\n", ret, strerror(ret))); + } + } +} diff --git a/src/util/child_common.h b/src/util/child_common.h new file mode 100644 index 00000000..1e9f1b6c --- /dev/null +++ b/src/util/child_common.h @@ -0,0 +1,109 @@ +/* + SSSD + + Common helper functions to be used in child processes + + Authors: + Sumit Bose + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CHILD_COMMON_H__ +#define __CHILD_COMMON_H__ + +#include +#include +#include +#include + +#include "util/util.h" + +#define IN_BUF_SIZE 512 +#define CHILD_MSG_CHUNK 256 + +struct response { + uint8_t *buf; + size_t size; +}; + +struct io_buffer { + uint8_t *data; + size_t size; +}; + +/* COMMON SIGCHLD HANDLING */ +typedef void (*sss_child_fn_t)(int pid, int wait_status, void *pvt); + +struct sss_sigchild_ctx; +struct sss_child_ctx; + +/* Create a new child context to manage callbacks */ +errno_t sss_sigchld_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sss_sigchild_ctx **child_ctx); + +errno_t sss_child_register(TALLOC_CTX *mem_ctx, + struct sss_sigchild_ctx *sigchld_ctx, + pid_t pid, + sss_child_fn_t cb, + void *pvt, + struct sss_child_ctx **child_ctx); + +void sss_child_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data); + +/* Callback to be invoked when a sigchld handler is called. + * The tevent_signal * associated with the handler will be + * freed automatically when this function returns. + */ +typedef void (*sss_child_callback_t)(int child_status, + struct tevent_signal *sige, + void *pvt); + +/* Set up child termination signal handler */ +int child_handler_setup(struct tevent_context *ev, int pid, + sss_child_callback_t cb, void *pvt); + +/* Async communication with the child process via a pipe */ +struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + uint8_t *buf, size_t len, int fd); +int write_pipe_recv(struct tevent_req *req); + +struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, int fd); +int read_pipe_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **buf, ssize_t *len); + +/* The pipes to communicate with the child must be nonblocking */ +void fd_nonblocking(int fd); + +void child_sig_handler(struct tevent_context *ev, + struct tevent_signal *sige, int signum, + int count, void *__siginfo, void *pvt); + +errno_t exec_child(TALLOC_CTX *mem_ctx, + int *pipefd_to_child, int *pipefd_from_child, + const char *binary, int debug_fd); + +void child_cleanup(int readfd, int writefd); + +#endif /* __CHILD_COMMON_H__ */ -- cgit