}
}
- trigger_exec(NULL, NULL, "server.start", false, NULL);
+ trigger_exec(NULL, NULL, NULL, "server.start", false, NULL);
/*
* Inform the parent (who should still be waiting) that the rest of
* Fire signal and stop triggers after ignoring SIGTERM, so handlers are
* not killed with the rest of the process group, below.
*/
- if (status == 2) trigger_exec(NULL, NULL, "server.signal.term", true, NULL);
- trigger_exec(NULL, NULL, "server.stop", false, NULL);
+ if (status == 2) trigger_exec(NULL, NULL, NULL, "server.signal.term", true, NULL);
+ trigger_exec(NULL, NULL, NULL, "server.stop", false, NULL);
/*
* Stop the scheduler, this signals the network and worker threads
uint32_t priority; //!< higher == higher priority
};
-#define request_is_external(_x) (_x->async->listen != NULL)
+#define request_is_external(_x) (_x->async && (_x->async->listen != NULL))
#define request_is_internal(_x) (!request_is_external(_x))
int fr_io_listen_free(fr_listen_t *li);
// WORKER_VERIFY;
+ /*
+ * Stop any new requests running with this interpreter
+ */
+ unlang_interpret_set_thread_default(NULL);
+
/*
* Destroy all of the active requests. These are ones
* which are still waiting for timers or file descriptor
#define _CONNECTION_PRIVATE 1
#include <freeradius-devel/server/connection.h>
-#include <freeradius-devel/server/log.h>
-#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/server/cond_eval.h>
+#include <freeradius-devel/server/log.h>
#include <freeradius-devel/server/trigger.h>
+#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/base.h>
#include <freeradius-devel/util/event.h>
#include <freeradius-devel/util/talloc.h>
#define CONN_TRIGGER(_state) do { \
if (conn->pub.triggers) { \
- trigger_exec(NULL, NULL, fr_table_str_by_value(fr_connection_trigger_names, _state, "<INVALID>"), true, NULL); \
+ trigger_exec(unlang_interpret_get_thread_default(), \
+ NULL, NULL, fr_table_str_by_value(fr_connection_trigger_names, _state, "<INVALID>"), true, NULL); \
} \
} while (0)
} connection_dsignal_t;
static fr_table_num_ordered_t const connection_dsignals[] = {
- { L("INIT"), CONNECTION_DSIGNAL_INIT },
+ { L("INIT"), CONNECTION_DSIGNAL_INIT },
{ L("CONNECTING"), CONNECTION_DSIGNAL_CONNECTED },
{ L("RECONNECT-FAILED"), CONNECTION_DSIGNAL_RECONNECT_FAILED },
{ L("RECONNECT-EXPIRED"), CONNECTION_DSIGNAL_RECONNECT_EXPIRED },
{ L("SHUTDOWN"), CONNECTION_DSIGNAL_SHUTDOWN },
- { L("HALT"), CONNECTION_DSIGNAL_HALT },
- { L("FREE"), CONNECTION_DSIGNAL_FREE }
+ { L("HALT"), CONNECTION_DSIGNAL_HALT },
+ { L("FREE"), CONNECTION_DSIGNAL_FREE }
};
static size_t connection_dsignals_len = NUM_ELEMENTS(connection_dsignals);
int fr_exec_wait_start(pid_t *pid_p, int *stdin_fd, int *stdout_fd, int *stderr_fd,
request_t *request, fr_value_box_list_t *vb_list, fr_pair_list_t *env_pairs);
-void fr_exec_waitpid(pid_t pid);
-
#ifdef __cplusplus
}
#endif
fr_dcursor_prepend(&cursor, vp);
snprintf(name, sizeof(name), "%s.%s", ef->trigger_prefix, name_suffix);
- trigger_exec(request, ef->conf, name, false, &args);
+ trigger_exec(unlang_interpret_get_thread_default(), request, ef->conf, name, false, &args);
fr_pair_list_free(&args);
}
last_hup = when;
- trigger_exec(NULL, NULL, "server.signal.hup", true, NULL);
+ trigger_exec(unlang_interpret_get_thread_default(), NULL, NULL, "server.signal.hup", true, NULL);
fr_event_loop_exit(event_list, 0x80);
}
}
if (!pool->triggers_enabled) return;
snprintf(name, sizeof(name), "%s.%s", pool->trigger_prefix, event);
- trigger_exec(request, pool->cs, name, true, &pool->trigger_args);
+ trigger_exec(unlang_interpret_get_thread_default(), request, pool->cs, name, true, &pool->trigger_args);
}
/** Find a connection handle in the connection list
char *name;
xlat_exp_t *xlat;
fr_value_box_list_t args;
+ bool synchronous;
+ pid_t pid; //!< for synchronous execution.
} fr_trigger_t;
}
/*
- * Execute the program without waiting for argss.
+ * Execute the program and wait for it to finish before
+ * continuing. This blocks the executing thread.
*/
- if (fr_exec_nowait(request, &trigger->args, NULL) < 0) {
- RPERROR("Failed running trigger %s", trigger->name);
- RETURN_MODULE_FAIL;
+ if (trigger->synchronous) {
+ if (fr_exec_wait_start(&trigger->pid, NULL, NULL, NULL, request, &trigger->args, NULL) < 0) {
+ RPERROR("Failed running trigger %s", trigger->name);
+ RETURN_MODULE_FAIL;
+ }
+ /*
+ * Wait for the trigger to finish
+ *
+ * FIXME - We really need to log stdout/stderr
+ */
+ waitpid(&trigger->pid, NULL, 0);
+ /*
+ * Execute the program without waiting for the result.
+ */
+ } else {
+ if (fr_exec_nowait(request, &trigger->args, NULL) < 0) {
+ RPERROR("Failed running trigger %s", trigger->name);
+ RETURN_MODULE_FAIL;
+ }
}
RETURN_MODULE_OK;
*
* @note Calls to this function will be ignored if #trigger_exec_init has not been called.
*
- * @param request The current request.
- * @param cs to search for triggers in.
- * If cs is not NULL, the portion after the last '.' in name is used for the trigger.
- * If cs is NULL, the entire name is used to find the trigger in the global trigger
- * section.
- * @param name the path relative to the global trigger section ending in the trigger name
- * e.g. module.ldap.pool.start.
- * @param rate_limit whether to rate limit triggers.
- * @param args to make available via the @verbatim %(trigger:<arg>) @endverbatim xlat.
- * @return - 0 on success.
- * - -1 on failure.
+ * @param[in] intp Interpreter to run the trigger with. If this is NULL the
+ * trigger will be executed synchronously.
+ *
+ * @param[in] request The current request.
+ * @param[in] cs to search for triggers in.
+ * If cs is not NULL, the portion after the last '.' in name is used for the trigger.
+ * If cs is NULL, the entire name is used to find the trigger in the global trigger
+ * section.
+ * @param[in] name the path relative to the global trigger section ending in the trigger name
+ * e.g. module.ldap.pool.start.
+ * @param[in] rate_limit whether to rate limit triggers.
+ * @param[in] args to make available via the @verbatim %(trigger:<arg>) @endverbatim xlat.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
*/
-int trigger_exec(request_t *request, CONF_SECTION const *cs, char const *name, bool rate_limit, fr_pair_list_t *args)
+int trigger_exec(unlang_interpret_t *intp, request_t *request,
+ CONF_SECTION const *cs, char const *name, bool rate_limit, fr_pair_list_t *args)
{
CONF_SECTION const *subcs;
}
/*
- * Always run triggers in the default interpreter
+ * If we're not running it locally use the default
+ * interpreter for the thread.
*/
- unlang_interpret_set(child, unlang_interpret_get_thread_default());
-
- if (unlang_subrequest_child_push_and_detach(child) < 0) goto error;
+ if (intp) {
+ unlang_interpret_set(child, intp);
+ if (unlang_subrequest_child_push_and_detach(child) < 0) {
+ error:
+ ROPTIONAL(RPEDEBUG, PERROR, "Running trigger failed");
+ talloc_free(child);
+ return -1;
+ }
+ }
if (unlang_interpret_push_function(child, trigger_run, trigger_resume,
- NULL, UNLANG_TOP_FRAME, trigger) < 0) {
- error:
- ROPTIONAL(RPEDEBUG, PERROR, "Running trigger failed");
+ NULL, UNLANG_TOP_FRAME, trigger) < 0) goto error;
+
+ if (!intp) {
+ /*
+ * Wait for the exec to finish too,
+ * so where there are global events
+ * the child processes don't race
+ * with something like the server
+ * shutting down.
+ */
+ trigger->synchronous = true;
+ unlang_interpret_synchronous(child);
talloc_free(child);
- return -1;
}
/*
#endif
#include <freeradius-devel/server/cf_util.h>
-#include <freeradius-devel/server/request.h>
#include <freeradius-devel/server/module.h>
+#include <freeradius-devel/server/request.h>
+#include <freeradius-devel/unlang/interpret.h>
#include <freeradius-devel/util/pair.h>
#include <freeradius-devel/util/talloc.h>
int trigger_exec_init(CONF_SECTION const *cs);
-int trigger_exec(request_t *request, CONF_SECTION const *cs,
+int trigger_exec(unlang_interpret_t *intp,
+ request_t *request, CONF_SECTION const *cs,
char const *name, bool quench, fr_pair_list_t *args)
- CC_HINT(nonnull (3));
+ CC_HINT(nonnull (4));
void trigger_exec_free(void);
#define CONN_TRIGGER(_state) do { \
if (trunk->pub.triggers) { \
- trigger_exec(NULL, NULL, fr_table_str_by_value(fr_trunk_conn_trigger_names, _state, \
+ trigger_exec(unlang_interpret_get_thread_default(), \
+ NULL, NULL, fr_table_str_by_value(fr_trunk_conn_trigger_names, _state, \
"<INVALID>"), true, NULL); \
} \
} while (0)
#define REQUEST_TRIGGER(_state) do { \
if (trunk->pub.triggers) { \
- trigger_exec(NULL, NULL, fr_table_str_by_value(fr_trunk_req_trigger_names, _state, \
+ trigger_exec(unlang_interpret_get_thread_default(), \
+ NULL, NULL, fr_table_str_by_value(fr_trunk_req_trigger_names, _state, \
"<INVALID>"), true, NULL); \
} \
} while (0)
* Some functions differ mainly in their parsing
*/
typedef struct {
+ bool done_func; //!< Set to true after func has been executed.
unlang_function_t func; //!< To call when going down the stack.
unlang_function_t repeat; //!< To call when going back up the stack.
unlang_function_signal_t signal; //!< Signal function to call.
*/
caller = request->module;
request->module = NULL;
- if (!is_repeatable(frame)) {
+ if (!state->done_func) {
ua = state->func(p_result, &frame->priority, request, state->uctx);
+ state->done_func = true;
} else {
ua = state->repeat(p_result, &frame->priority, request, state->uctx);
}
/** Set the default interpreter for this thread
*
-
*/
void unlang_interpret_set_thread_default(unlang_interpret_t *intp)
{
- intp_thread_default = talloc_get_type_abort(intp, unlang_interpret_t);
+ if (intp) (void)talloc_get_type_abort(intp, unlang_interpret_t);
+
+ intp_thread_default = intp;
}
/** Get the default interpreter for this thread
bfd_request(session, request, &packet);
- trigger_exec(request, NULL, buffer, false, NULL);
+ trigger_exec(unlang_interpret_get_thread_default(), request, NULL, buffer, false, NULL);
}
bfd_request(session, &request, &packet);
- trigger_exec(&request, NULL, "server.bfd.warn", false, NULL);
+ trigger_exec(unlang_interpret_get_thread_default(), &request, NULL, "server.bfd.warn", false, NULL);
}