From: Alan T. DeKok Date: Mon, 13 Apr 2020 13:11:25 +0000 (-0400) Subject: use kevent to wait for the PID X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e15e08b871ce54a4080fffbd93b0807b816e64d9;p=thirdparty%2Ffreeradius-server.git use kevent to wait for the PID it should all be working. Next is to add tests --- diff --git a/src/lib/unlang/tmpl.c b/src/lib/unlang/tmpl.c index dad1533f350..c40da286f07 100644 --- a/src/lib/unlang/tmpl.c +++ b/src/lib/unlang/tmpl.c @@ -29,9 +29,15 @@ RCSID("$Id$") #include #include #include "tmpl_priv.h" +#include - -static void unlang_tmpl_exec_kill(REQUEST *request) +/* + * Clean up everything except the waitpid handler. + * + * If there is a waitpid handler, then this cleanup function MUST + * be called after setting the handler. + */ +static void unlang_tmpl_exec_cleanup(REQUEST *request) { unlang_stack_t *stack = request->stack; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; @@ -75,7 +81,7 @@ static void unlang_tmpl_signal(REQUEST *request, fr_state_signal_t action) * ignore future signals. */ if (action == FR_SIGNAL_CANCEL) { - if (state->buffer) unlang_tmpl_exec_kill(request); + if (state->buffer) unlang_tmpl_exec_cleanup(request); state->signal = NULL; } } @@ -127,6 +133,23 @@ void unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_t **out, REQUEST *request, v } +static void unlang_tmpl_exec_waitpid(UNUSED fr_event_list_t *el, UNUSED pid_t pid, int status, void *uctx) +{ + REQUEST *request = uctx; + unlang_stack_t *stack = request->stack; + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_frame_state_tmpl_t *state = talloc_get_type_abort(frame->state, + unlang_frame_state_tmpl_t); + + state->status = status; + + rad_assert(state->pid == 0); + rad_assert(state->fd < 0); + rad_assert(state->ev == NULL); + unlang_interpret_resumable(request); +} + + static void unlang_tmpl_exec_read(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx) { REQUEST *request = uctx; @@ -144,31 +167,44 @@ static void unlang_tmpl_exec_read(UNUSED fr_event_list_t *el, int fd, UNUSED int if (data_len < 0) { if (errno == EINTR) return; - unlang_tmpl_exec_kill(request); + unlang_tmpl_exec_cleanup(request); unlang_interpret_resumable(request); return; } + p += data_len; + /* * Done reading, close the pipe. */ - if (data_len == 0) { - state->status = 0; + if ((data_len == 0) || (p >= end)) { + /* + * Ran out of buffer space. Kill the process. + */ + if (p >= end) kill(state->pid, SIGKILL); - // @todo - convert buffer to value_box + /* + * This event will stick around until the process exits. + */ + if (fr_event_pid_wait(state, request->el, &state->ev_pid, state->pid, + unlang_tmpl_exec_waitpid, request) < 0) { + unlang_tmpl_exec_cleanup(request); + unlang_interpret_resumable(request); + return; + } + state->pid = 0; /* don't kill the process */ - resume: - unlang_tmpl_exec_kill(request); + /* + * Clean up the FD, reader, and timeouts. + */ + unlang_tmpl_exec_cleanup(request); - // @todo - only mark the request resumable when the process exits - // which is the definition of "wait"... - unlang_interpret_resumable(request); + /* + * Once the process exits, we will be notified, and + */ return; } - p += data_len; - if (p == end) goto resume; /* overflowed output buffer */ - rad_assert(p < end); state->ptr = p; } @@ -178,7 +214,7 @@ static void unlang_tmpl_exec_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_ REQUEST *request = uctx; REDEBUG("Timeout running program - kill it and failing the request"); - unlang_tmpl_exec_kill(request); + unlang_tmpl_exec_cleanup(request); unlang_interpret_resumable(request); } @@ -268,13 +304,13 @@ static unlang_action_t unlang_tmpl_exec_wait_resume(REQUEST *request, rlm_rcode_ * @todo - make the timeout configurable */ if (fr_event_timer_in(request, request->el, &state->ev, 10 * NSEC, unlang_tmpl_exec_timeout, request) < 0) { - unlang_tmpl_exec_kill(request); + unlang_tmpl_exec_cleanup(request); goto fail; } if (fr_event_fd_insert(state->ctx, request->el, state->fd, unlang_tmpl_exec_read, NULL, NULL, request) < 0) { REDEBUG("Failed adding event - %s", fr_strerror()); - unlang_tmpl_exec_kill(request); + unlang_tmpl_exec_cleanup(request); goto fail; } diff --git a/src/lib/unlang/tmpl_priv.h b/src/lib/unlang/tmpl_priv.h index f5815bd3c99..36aed2d4e6c 100644 --- a/src/lib/unlang/tmpl_priv.h +++ b/src/lib/unlang/tmpl_priv.h @@ -45,7 +45,9 @@ typedef struct { pid_t pid; //!< child PID int fd; //!< for reading from the child - fr_event_timer_t const *ev; //!< for cleaning up the child + fr_event_timer_t const *ev; //!< for timing out the child + fr_event_pid_t const *ev_pid; //!< for cleaning up the process + char *buffer; //!< for reading the answer char *ptr; //!< where in the buffer we are writing to int status; //!< return status from the program