#include <freeradius-devel/unlang/tmpl.h>
#include <freeradius-devel/server/exec.h>
#include "tmpl_priv.h"
+#include <signal.h>
-
-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];
* 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;
}
}
}
+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;
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;
}
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);
}
* @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;
}