diff options
Diffstat (limited to 'src/util/child_common.c')
-rw-r--r-- | src/util/child_common.c | 955 |
1 files changed, 955 insertions, 0 deletions
diff --git a/src/util/child_common.c b/src/util/child_common.c new file mode 100644 index 0000000..1dbf95b --- /dev/null +++ b/src/util/child_common.c @@ -0,0 +1,955 @@ +/* + SSSD + + Common helper functions to be used in child processes + + Authors: + Sumit Bose <sbose@redhat.com> + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <sys/types.h> +#include <fcntl.h> +#include <signal.h> +#include <tevent.h> +#include <sys/wait.h> +#include <errno.h> +#include <sys/prctl.h> + +#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; +}; + +static errno_t child_debug_init(const char *logfile, int *debug_fd); + +static void sss_child_handler(struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data); + +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(SSSDBG_FATAL_FAILURE, + "fatal error initializing sss_sigchild_ctx\n"); + return ENOMEM; + } + sigchld_ctx->ev = ev; + + ret = sss_hash_create(sigchld_ctx, 0, &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); + } + + talloc_free(imm); +} + +static 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(child_ctx); + 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; +}; + +static void child_sig_handler(struct tevent_context *ev, + struct tevent_signal *sige, int signum, + int count, void *__siginfo, 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) +{ + struct sss_child_ctx_old *child_ctx; + + DEBUG(SSSDBG_TRACE_INTERNAL, + "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(SSSDBG_TRACE_INTERNAL, "Signal handler set up for pid [%d]\n", pid); + + if (_child_ctx != NULL) { + *_child_ctx = child_ctx; + } + + return EOK; +} + +void child_handler_destroy(struct sss_child_ctx_old *ctx) +{ + errno_t ret; + + /* We still want to wait for the child to finish, but the caller is not + * interested in the result anymore (e.g. timeout was reached). */ + ctx->cb = NULL; + ctx->pvt = NULL; + + ret = kill(ctx->pid, SIGKILL); + if (ret == -1) { + ret = errno; + DEBUG(SSSDBG_MINOR_FAILURE, "kill failed [%d][%s].\n", ret, strerror(ret)); + } +} + +/* Async communication with the child process via a pipe */ + +struct _write_pipe_state { + int fd; + uint8_t *buf; + size_t len; + bool safe; + ssize_t written; +}; + +static void _write_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *pvt); + +static struct tevent_req *_write_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + uint8_t *buf, + size_t len, + bool safe, + 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->safe = safe; + state->written = 0; + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE, + _write_pipe_handler, req); + if (fde == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "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; + errno_t ret; + + state = tevent_req_data(req, struct _write_pipe_state); + + if (flags & TEVENT_FD_READ) { + DEBUG(SSSDBG_CRIT_FAILURE, + "_write_pipe_done called with TEVENT_FD_READ," + " this should not happen.\n"); + tevent_req_error(req, EINVAL); + return; + } + + errno = 0; + if (state->safe) { + state->written = sss_atomic_write_safe_s(state->fd, state->buf, state->len); + } else { + state->written = sss_atomic_write_s(state->fd, state->buf, state->len); + } + if (state->written == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "write failed [%d][%s].\n", ret, strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (state->len != state->written) { + DEBUG(SSSDBG_CRIT_FAILURE, "Wrote %zd bytes, expected %zu\n", + state->written, state->len); + tevent_req_error(req, EIO); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, "All data has been sent!\n"); + tevent_req_done(req); + return; +} + +static int _write_pipe_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} + +struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + uint8_t *buf, + size_t len, + int fd) +{ + return _write_pipe_send(mem_ctx, ev, buf, len, false, fd); +} + +int write_pipe_recv(struct tevent_req *req) +{ + return _write_pipe_recv(req); +} + +struct tevent_req *write_pipe_safe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + uint8_t *buf, + size_t len, + int fd) +{ + return _write_pipe_send(mem_ctx, ev, buf, len, true, fd); +} + +int write_pipe_safe_recv(struct tevent_req *req) +{ + return _write_pipe_recv(req); +} + +struct _read_pipe_state { + int fd; + uint8_t *buf; + size_t len; + bool safe; +}; + +static void _read_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *pvt); + +static struct tevent_req *_read_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + bool safe, + 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; + state->safe = safe; + + fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ, + _read_pipe_handler, req); + if (fde == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "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; + ssize_t size; + errno_t err; + uint8_t *buf; + size_t len = 0; + + state = tevent_req_data(req, struct _read_pipe_state); + + if (flags & TEVENT_FD_WRITE) { + DEBUG(SSSDBG_CRIT_FAILURE, "_read_pipe_done called with " + "TEVENT_FD_WRITE, this should not happen.\n"); + tevent_req_error(req, EINVAL); + return; + } + + buf = talloc_array(state, uint8_t, CHILD_MSG_CHUNK); + if (buf == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + if (state->safe) { + size = sss_atomic_read_safe_s(state->fd, buf, CHILD_MSG_CHUNK, &len); + if (size == -1 && errno == ERANGE) { + buf = talloc_realloc(state, buf, uint8_t, len); + if(!buf) { + tevent_req_error(req, ENOMEM); + return; + } + + size = sss_atomic_read_s(state->fd, buf, len); + } + } else { + size = sss_atomic_read_s(state->fd, buf, CHILD_MSG_CHUNK); + } + if (size == -1) { + err = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "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); + + if (state->len == len) { + DEBUG(SSSDBG_TRACE_FUNC, "All data received\n"); + tevent_req_done(req); + } + return; + + } else if (size == 0) { + DEBUG(SSSDBG_TRACE_FUNC, "EOF received, client finished\n"); + tevent_req_done(req); + return; + + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + "unexpected return value of read [%zd].\n", size); + tevent_req_error(req, EINVAL); + return; + } +} + +static errno_t _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; +} + +struct tevent_req *read_pipe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd) +{ + return _read_pipe_send(mem_ctx, ev, false, fd); +} + +errno_t read_pipe_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t **_buf, + ssize_t *_len) +{ + return _read_pipe_recv(req, mem_ctx, _buf, _len); +} + +struct tevent_req *read_pipe_safe_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd) +{ + return _read_pipe_send(mem_ctx, ev, true, fd); +} + +errno_t read_pipe_safe_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t **_buf, + ssize_t *_len) +{ + return _read_pipe_recv(req, mem_ctx, _buf, _len); +} + +static void child_invoke_callback(struct tevent_context *ev, + struct tevent_immediate *imm, + void *pvt); +static 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(SSSDBG_FATAL_FAILURE, + "SIGCHLD handler called with invalid child count\n"); + return; + } + + child_ctx = talloc_get_type(pvt, struct sss_child_ctx_old); + DEBUG(SSSDBG_TRACE_LIBS, "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(SSSDBG_CRIT_FAILURE, + "waitpid failed [%d][%s].\n", err, strerror(err)); + } else if (ret == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "waitpid did not find a child with changed status.\n"); + } else { + if (WIFEXITED(child_ctx->child_status)) { + if (WEXITSTATUS(child_ctx->child_status) != 0) { + DEBUG(SSSDBG_CRIT_FAILURE, + "child [%d] failed with status [%d].\n", ret, + WEXITSTATUS(child_ctx->child_status)); + } else { + DEBUG(SSSDBG_CONF_SETTINGS, + "child [%d] finished successfully.\n", ret); + } + } else if (WIFSIGNALED(child_ctx->child_status)) { + DEBUG(SSSDBG_CRIT_FAILURE, + "child [%d] was terminated by signal [%d].\n", ret, + WTERMSIG(child_ctx->child_status)); + } else { + if (WIFSTOPPED(child_ctx->child_status)) { + DEBUG(SSSDBG_TRACE_LIBS, + "child [%d] was stopped by signal [%d].\n", ret, + WSTOPSIG(child_ctx->child_status)); + } + if (WIFCONTINUED(child_ctx->child_status) == true) { + DEBUG(SSSDBG_TRACE_LIBS, + "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(child_ctx); + if (imm == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, + "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, + const char *extra_argv[], + bool extra_args_only, + char ***_argv) +{ + /* + * program name, debug_level, debug_timestamps, + * debug_microseconds, PR_SET_DUMPABLE and NULL + */ + uint_t argc = 6; + char ** argv = NULL; + errno_t ret = EINVAL; + size_t i; + + if (extra_args_only) { + argc = 2; /* program name and NULL */ + } + + /* Save the current state in case an interrupt changes it */ + bool child_debug_timestamps = debug_timestamps; + bool child_debug_microseconds = debug_microseconds; + + if (!extra_args_only) { + argc++; + } + + if (extra_argv) { + for (i = 0; extra_argv[i]; i++) argc++; + } + + /* + * program name, debug_level, debug_timestamps, + * debug_microseconds and NULL + */ + argv = talloc_array(mem_ctx, char *, argc); + if (argv == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_array failed.\n"); + return ENOMEM; + } + + argv[--argc] = NULL; + + /* Add extra_attrs first */ + if (extra_argv) { + for (i = 0; extra_argv[i]; i++) { + argv[--argc] = talloc_strdup(argv, extra_argv[i]); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + } + } + + if (!extra_args_only) { + argv[--argc] = talloc_asprintf(argv, "--debug-level=%#.4x", + debug_level); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + + if (sss_logger == FILES_LOGGER) { + argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d", + child_debug_fd); + if (argv[argc] == NULL) { + ret = ENOMEM; + goto fail; + } + } else { + argv[--argc] = talloc_asprintf(argv, "--logger=%s", + sss_logger_str[sss_logger]); + 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_asprintf(argv, "--dumpable=%d", + prctl(PR_GET_DUMPABLE)); + 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; +} + +void exec_child_ex(TALLOC_CTX *mem_ctx, + int *pipefd_to_child, int *pipefd_from_child, + const char *binary, const char *logfile, + const char *extra_argv[], bool extra_args_only, + int child_in_fd, int child_out_fd) +{ + int ret; + errno_t err; + char **argv; + int debug_fd = -1; + + if (logfile) { + ret = child_debug_init(logfile, &debug_fd); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "child_debug_init() failed.\n"); + exit(EXIT_FAILURE); + } + } else { + debug_fd = STDERR_FILENO; + } + + close(pipefd_to_child[1]); + ret = dup2(pipefd_to_child[0], child_in_fd); + if (ret == -1) { + err = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "dup2 failed [%d][%s].\n", err, strerror(err)); + exit(EXIT_FAILURE); + } + + close(pipefd_from_child[0]); + ret = dup2(pipefd_from_child[1], child_out_fd); + if (ret == -1) { + err = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "dup2 failed [%d][%s].\n", err, strerror(err)); + exit(EXIT_FAILURE); + } + + ret = prepare_child_argv(mem_ctx, debug_fd, + binary, extra_argv, extra_args_only, + &argv); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "prepare_child_argv() failed.\n"); + exit(EXIT_FAILURE); + } + + execv(binary, argv); + err = errno; + DEBUG(SSSDBG_OP_FAILURE, "execv failed [%d][%s].\n", err, strerror(err)); + exit(EXIT_FAILURE); +} + +void exec_child(TALLOC_CTX *mem_ctx, + int *pipefd_to_child, int *pipefd_from_child, + const char *binary, const char *logfile) +{ + exec_child_ex(mem_ctx, pipefd_to_child, pipefd_from_child, + binary, logfile, NULL, false, + STDIN_FILENO, STDOUT_FILENO); +} + +int child_io_destructor(void *ptr) +{ + int ret; + struct child_io_fds *io = talloc_get_type(ptr, struct child_io_fds); + if (io == NULL) return EOK; + + if (io->write_to_child_fd != -1) { + ret = close(io->write_to_child_fd); + io->write_to_child_fd = -1; + if (ret != EOK) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "close failed [%d][%s].\n", ret, strerror(ret)); + } + } + + if (io->read_from_child_fd != -1) { + ret = close(io->read_from_child_fd); + io->read_from_child_fd = -1; + if (ret != EOK) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "close failed [%d][%s].\n", ret, strerror(ret)); + } + } + + return EOK; +} + +static errno_t child_debug_init(const char *logfile, int *debug_fd) +{ + int ret; + FILE *debug_filep; + + if (debug_fd == NULL) { + return EOK; + } + + if (sss_logger == FILES_LOGGER && *debug_fd == -1) { + ret = open_debug_file_ex(logfile, &debug_filep, false); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, "Error setting up logging (%d) [%s]\n", + ret, sss_strerror(ret)); + return ret; + } + + *debug_fd = fileno(debug_filep); + if (*debug_fd == -1) { + ret = errno; + DEBUG(SSSDBG_FATAL_FAILURE, + "fileno failed [%d][%s]\n", ret, strerror(ret)); + return ret; + } + } + + return EOK; +} |