child_common.c revision a9402e5d8e75f7f361a5754e91180ed4f4ee98e1
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff/*
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff SSSD
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff Common helper functions to be used in child processes
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff Authors:
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence Sumit Bose <sbose@redhat.com>
15a44745412679c30a6d022733925af70a38b715David Lawrence
15a44745412679c30a6d022733925af70a38b715David Lawrence Copyright (C) 2009 Red Hat
15a44745412679c30a6d022733925af70a38b715David Lawrence
15a44745412679c30a6d022733925af70a38b715David Lawrence This program is free software; you can redistribute it and/or modify
15a44745412679c30a6d022733925af70a38b715David Lawrence it under the terms of the GNU General Public License as published by
15a44745412679c30a6d022733925af70a38b715David Lawrence the Free Software Foundation; either version 3 of the License, or
15a44745412679c30a6d022733925af70a38b715David Lawrence (at your option) any later version.
15a44745412679c30a6d022733925af70a38b715David Lawrence
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff This program is distributed in the hope that it will be useful,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff but WITHOUT ANY WARRANTY; without even the implied warranty of
40f53fa8d9c6a4fc38c0014495e7a42b08f52481David Lawrence MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9c3531d72aeaad6c5f01efe6a1c82023e1379e4dDavid Lawrence GNU General Public License for more details.
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff You should have received a copy of the GNU General Public License
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff along with this program. If not, see <http://www.gnu.org/licenses/>.
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff*/
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include <sys/types.h>
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include <fcntl.h>
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include <tevent.h>
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include <sys/wait.h>
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include <errno.h>
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include "util/util.h"
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include "util/find_uid.h"
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include "db/sysdb.h"
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff#include "providers/child_common.h"
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff/* Async communication with the child process via a pipe */
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graffstruct write_pipe_state {
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff int fd;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff uint8_t *buf;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff size_t len;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff size_t written;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff};
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graffstatic void write_pipe_handler(struct tevent_context *ev,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct tevent_fd *fde,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff uint16_t flags, void *pvt);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graffstruct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct tevent_context *ev,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff uint8_t *buf, size_t len, int fd)
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff{
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct tevent_req *req;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct write_pipe_state *state;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct tevent_fd *fde;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff req = tevent_req_create(mem_ctx, &state, struct write_pipe_state);
aed3b8cb4e1e9274df734b61844845ea0e33e79bBrian Wellington if (req == NULL) return NULL;
aed3b8cb4e1e9274df734b61844845ea0e33e79bBrian Wellington
aed3b8cb4e1e9274df734b61844845ea0e33e79bBrian Wellington state->fd = fd;
aed3b8cb4e1e9274df734b61844845ea0e33e79bBrian Wellington state->buf = buf;
aed3b8cb4e1e9274df734b61844845ea0e33e79bBrian Wellington state->len = len;
aed3b8cb4e1e9274df734b61844845ea0e33e79bBrian Wellington state->written = 0;
aed3b8cb4e1e9274df734b61844845ea0e33e79bBrian Wellington
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff fde = tevent_add_fd(ev, state, fd, TEVENT_FD_WRITE,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff write_pipe_handler, req);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff if (fde == NULL) {
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff DEBUG(1, ("tevent_add_fd failed.\n"));
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff goto fail;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff }
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington return req;
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Grafffail:
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff talloc_zfree(req);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff return NULL;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff}
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graffstatic void write_pipe_handler(struct tevent_context *ev,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct tevent_fd *fde,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff uint16_t flags, void *pvt)
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff{
9f95b0199c3f1b0ef3d40c1854a7501d72112e5aMichael Graff struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct write_pipe_state *state = tevent_req_data(req,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff struct write_pipe_state);
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington ssize_t size;
9f95b0199c3f1b0ef3d40c1854a7501d72112e5aMichael Graff
9f95b0199c3f1b0ef3d40c1854a7501d72112e5aMichael Graff if (flags & TEVENT_FD_READ) {
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff DEBUG(1, ("write_pipe_done called with TEVENT_FD_READ,"
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff " this should not happen.\n"));
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff tevent_req_error(req, EINVAL);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff return;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff }
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff size = write(state->fd,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff state->buf + state->written,
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff state->len - state->written);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff if (size == -1) {
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff if (errno == EAGAIN || errno == EINTR) return;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff DEBUG(1, ("write failed [%d][%s].\n", errno, strerror(errno)));
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington tevent_req_error(req, errno);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff return;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff } else if (size >= 0) {
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff state->written += size;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff if (state->written > state->len) {
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington DEBUG(1, ("write to much, this should never happen.\n"));
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington tevent_req_error(req, EINVAL);
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington return;
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington }
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington } else {
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington DEBUG(1, ("unexpected return value of write [%d].\n", size));
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington tevent_req_error(req, EINVAL);
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington return;
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington }
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff if (state->len == state->written) {
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff DEBUG(6, ("All data has been sent!\n"));
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff tevent_req_done(req);
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff return;
890fb60939f93161ca0c63e19c7154eaf3fed156Michael Graff }
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington}
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellingtonint write_pipe_recv(struct tevent_req *req)
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington{
153d59973f4434d80adfa6097a31d1e349f2d3baBrian Wellington 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 = talloc_array(state, uint8_t, MAX_CHILD_MSG_SIZE);
state->len = 0;
if (state->buf == NULL) goto fail;
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;
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,
state->buf + state->len,
MAX_CHILD_MSG_SIZE - state->len);
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->len += size;
if (state->len > MAX_CHILD_MSG_SIZE) {
DEBUG(1, ("read to much, this should never happen.\n"));
tevent_req_error(req, EINVAL);
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;
}
void child_sig_handler(struct tevent_context *ev,
struct tevent_signal *sige, int signum,
int count, void *__siginfo, void *pvt)
{
int ret;
int child_status;
DEBUG(7, ("Waiting for [%d] childeren.\n", count));
do {
errno = 0;
ret = waitpid(-1, &child_status, WNOHANG);
if (ret == -1) {
DEBUG(1, ("waitpid failed [%d][%s].\n", errno, strerror(errno)));
} else if (ret == 0) {
DEBUG(1, ("waitpid did not found a child with changed status.\n"));
} else {
if (WEXITSTATUS(child_status) != 0) {
DEBUG(1, ("child [%d] failed with status [%d].\n", ret,
child_status));
} else {
DEBUG(4, ("child [%d] finished successful.\n", ret));
}
}
--count;
} while (count < 0);
return;
}
static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
int child_debug_fd,
const char *binary,
char ***_argv)
{
uint_t argc = 3; /* program name, debug_level and NULL */
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;
if (child_debug_to_file) argc++;
if (!child_debug_timestamps) argc++;
/* program name, debug_level,
* debug_to_file, debug_timestamps
* 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=%d",
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;
}
}
if (!child_debug_timestamps) {
argv[--argc] = talloc_strdup(argv, "--debug-timestamps=0");
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", errno, strerror(errno)));
}
}
if (writefd != -1) {
ret = close(writefd);
if (ret != EOK) {
ret = errno;
DEBUG(1, ("close failed [%d][%s].\n", errno, strerror(errno)));
}
}
}