return 0;
}
-static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, UNUSED void const *mod_inst, UNUSED void *proc_inst,
+static unlang_action_t mod_map_proc(unlang_result_t *p_result, UNUSED map_ctx_t const *mpctx,
UNUSED request_t *request, UNUSED fr_value_box_list_t *src,
UNUSED map_list_t const *maps)
{
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
static request_t *request_clone(request_t *old, int number, CONF_SECTION *server_cs)
return inst;
}
-
-/** Evaluate a set of maps using the specified map processor
- *
- * Evaluate the map processor src template, then call a map processor function to do
- * something with the expanded src template and map the result to attributes in the request.
- *
- * @param[out] p_result Result code of evaluating the map.
- * @param[in] request The current request.
- * @param[in] inst of a map processor.
- * @param[in,out] result Result of expanding the map input. May be consumed
- * by the map processor.
- * @return one of UNLANG_ACTION_*
- */
-unlang_action_t map_proc(rlm_rcode_t *p_result, request_t *request, map_proc_inst_t const *inst, fr_value_box_list_t *result)
-{
- return inst->proc->evaluate(p_result, inst->proc->mod_inst, inst->data, request, result, inst->maps);
-}
extern "C" {
#endif
+/** Temporary structure to hold arguments for map calls
+ *
+ */
+typedef struct {
+ void *rctx; //!< Resume ctx that a module previously set.
+ void const *moi; //!< Map module instance.
+ void const *mpi; //!< Map processor instance.
+} map_ctx_t;
+
/** Function to evaluate the src string and map the result to server attributes
*
* @param[out] p_result Result of applying the map:
* - #RLM_MODULE_NOOP - If no data available for given src, or no mappings matched available data.
* - #RLM_MODULE_UPDATED - If new pairs were added to the request.
* - #RLM_MODULE_FAIL - If an error occurred performing the mapping.
- * @param[in] mod_inst Instance of the module that registered the map_proc.
- * @param[in] proc_inst Map proc data created by #map_proc_instantiate_t.
+ * @param[in] mpctx Call ctx for the map processor.
* @param[in] request The current request.
* @param[in,out] result Input data for the map processor. May be consumed by the
* map processor.
* @param[in] maps Head of the list of maps to process.
* @return one of UNLANG_ACTION_*
*/
-typedef unlang_action_t (*map_proc_func_t)(rlm_rcode_t *p_result, void const *mod_inst, void *proc_inst, request_t *request,
+typedef unlang_action_t (*map_proc_func_t)(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
fr_value_box_list_t *result, map_list_t const *maps);
/** Allocate new instance data for a map processor
map_proc_inst_t *map_proc_instantiate(TALLOC_CTX *ctx, map_proc_t const *proc,
CONF_SECTION *cs, tmpl_t const *src, map_list_t const *maps);
-
-unlang_action_t map_proc(rlm_rcode_t *p_result, request_t *request, map_proc_inst_t const *inst, fr_value_box_list_t *src);
-
#ifdef __cplusplus
}
#endif
RCSID("$Id$")
#include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/map.h>
#include <freeradius-devel/unlang/tmpl.h>
-
+#include <freeradius-devel/unlang/map.h>
#include "map_priv.h"
*
*/
typedef struct {
- fr_dcursor_t maps; //!< Cursor of maps to evaluate.
+ fr_dcursor_t maps; //!< Cursor of maps to evaluate.
- fr_dlist_head_t vlm_head; //!< Head of list of VP List Mod.
+ fr_dlist_head_t vlm_head; //!< Head of list of VP List Mod.
- fr_value_box_list_t lhs_result; //!< Result of expanding the LHS
- fr_value_box_list_t rhs_result; //!< Result of expanding the RHS.
+ fr_value_box_list_t lhs_result; //!< Result of expanding the LHS
+ fr_value_box_list_t rhs_result; //!< Result of expanding the RHS.
- unlang_update_state_t state; //!< What we're currently doing.
+ unlang_update_state_t state; //!< What we're currently doing.
} unlang_frame_state_update_t;
/** State of a map block
*
*/
typedef struct {
- fr_value_box_list_t src_result; //!< Result of expanding the map source.
+ fr_value_box_list_t src_result; //!< Result of expanding the map source.
+
+ /** @name Resumption and signalling
+ * @{
+ */
+ void *rctx; //!< for resume / signal
+ map_proc_func_t resume; //!< resumption handler
+ unlang_map_signal_t signal; //!< for signal handlers
+ fr_signal_t sigmask; //!< Signals to block.
+
+ /** @} */
} unlang_frame_state_map_proc_t;
+/** Wrapper to create a map_ctx_t as a compound literal
+ *
+ * @param[in] _mod_inst of the module being called.
+ * @param[in] _map_inst of the map being called.
+ * @param[in] _rctx Resume ctx (if any).
+ */
+#define MAP_CTX(_mod_inst, _map_inst, _rctx) &(map_ctx_t){ .moi = _mod_inst, .mpi = _map_inst, .rctx = _rctx }
+
/** Apply a list of modifications on one or more fr_pair_t lists.
*
* @param[in] request The current request.
return list_mod_create(p_result, request, frame);
}
-static unlang_action_t map_proc_resume(UNUSED unlang_result_t *p_result, UNUSED request_t *request,
+static unlang_action_t map_proc_resume(unlang_result_t *p_result, request_t *request,
#ifdef WITH_VERIFY_PTR
unlang_stack_frame_t *frame
#else
#endif
)
{
-#ifdef WITH_VERIFY_PTR
+ unlang_frame_state_map_proc_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_map_proc_t);
unlang_frame_state_map_proc_t *map_proc_state = talloc_get_type_abort(frame->state, unlang_frame_state_map_proc_t);
+ map_proc_func_t resume;
+ unlang_group_t *g = unlang_generic_to_group(frame->instruction);
+ unlang_map_t *gext = unlang_group_to_map(g);
+ map_proc_inst_t *inst = gext->proc_inst;
+ unlang_action_t ua = UNLANG_ACTION_CALCULATE_RESULT;
+
+#ifdef WITH_VERIFY_PTR
VALUE_BOX_LIST_VERIFY(&map_proc_state->src_result);
#endif
- return UNLANG_ACTION_CALCULATE_RESULT;
+ resume = state->resume;
+ state->resume = NULL;
+
+ /*
+ * Call any map resume function
+ */
+ if (resume) ua = resume(p_result, MAP_CTX(inst->proc->mod_inst, inst->data, state->rctx),
+ request, &map_proc_state->src_result, inst->maps);
+ return ua;
+}
+
+/** Yield a request back to the interpreter from within a module
+ *
+ * This passes control of the request back to the unlang interpreter, setting
+ * callbacks to execute when the request is 'signalled' asynchronously, or whatever
+ * timer or I/O event the module was waiting for occurs.
+ *
+ * @note The module function which calls #unlang_module_yield should return control
+ * of the C stack to the unlang interpreter immediately after calling #unlang_module_yield.
+ * A common pattern is to use ``return unlang_module_yield(...)``.
+ *
+ * @param[in] request The current request.
+ * @param[in] resume Called on unlang_interpret_mark_runnable().
+ * @param[in] signal Called on unlang_action().
+ * @param[in] sigmask Set of signals to block.
+ * @param[in] rctx to pass to the callbacks.
+ * @return
+ * - UNLANG_ACTION_YIELD.
+ */
+unlang_action_t unlang_map_yield(request_t *request,
+ map_proc_func_t resume, unlang_map_signal_t signal, fr_signal_t sigmask, void *rctx)
+{
+ unlang_stack_t *stack = request->stack;
+ unlang_stack_frame_t *frame = &stack->frame[stack->depth];
+ unlang_frame_state_map_proc_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_map_proc_t);
+
+ REQUEST_VERIFY(request); /* Check the yielded request is sane */
+
+ state->rctx = rctx;
+ state->resume = resume;
+ state->signal = signal;
+ state->sigmask = sigmask;
+
+ /*
+ * We set the repeatable flag here,
+ * so that the resume function is always
+ * called going back up the stack.
+ */
+ frame_repeat(frame, map_proc_resume);
+
+ return UNLANG_ACTION_YIELD;
}
static unlang_action_t map_proc_apply(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
VALUE_BOX_LIST_VERIFY(&map_proc_state->src_result);
frame_repeat(frame, map_proc_resume);
- return map_proc(&p_result->rcode, request, gext->proc_inst, &map_proc_state->src_result);
+
+ return inst->proc->evaluate(p_result, MAP_CTX(inst->proc->mod_inst, inst->data, NULL),
+ request, &map_proc_state->src_result, inst->maps);
}
static unlang_action_t unlang_map_state_init(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
--- /dev/null
+#pragma once
+/*
+ * 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 2, 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * $Id$
+ *
+ * @file unlang/map.h
+ *
+ * @copyright 2025 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <freeradius-devel/server/signal.h>
+#include <freeradius-devel/server/request.h>
+#include <freeradius-devel/server/map.h>
+#include <freeradius-devel/server/map_proc.h>
+#include <freeradius-devel/unlang/interpret.h>
+
+/** A callback when the request gets a fr_signal_t.
+ *
+ * @note The callback is automatically removed on unlang_interpret_mark_runnable().
+ *
+ * @param[in] mpctx calling context for the map function.
+ * @param[in] request The current request.
+ * @param[in] action which is signalling the request.
+ */
+typedef void (*unlang_map_signal_t)(map_ctx_t const *mpctx, request_t *request, fr_signal_t action);
+
+unlang_action_t unlang_map_yield(request_t *request,
+ map_proc_func_t resume, unlang_map_signal_t signal, fr_signal_t sigmask, void *rctx);
+#ifdef __cplusplus
+}
+#endif
* - #RLM_MODULE_NOOP no rows were returned.
* - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
* - #RLM_MODULE_FAIL if an error occurred.
- * @param[in] mod_inst NULL.
- * @param[in] proc_inst NULL.
+ * @param[in] mpctx NULL
* @param[in] request The current request.
* @param[in] client_override If NULL, use the current client, else use the client matching
* the ip given.
* @param[in] maps Head of the map list.
* @return UNLANG_ACTION_CALCULATE_RESULT
*/
-static unlang_action_t map_proc_client(rlm_rcode_t *p_result, UNUSED void const *mod_inst, UNUSED void *proc_inst,
+static unlang_action_t map_proc_client(unlang_result_t *p_result, UNUSED map_ctx_t const *mpctx,
request_t *request, fr_value_box_list_t *client_override, map_list_t const *maps)
{
rlm_rcode_t rcode = RLM_MODULE_OK;
FR_VALUE_BOX_LIST_FREE, true,
SIZE_MAX) < 0) {
REDEBUG("Failed concatenating input data");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
client_str = client_override_head->vb_strvalue;
client = client_from_request(request);
if (!client) {
REDEBUG("No client associated with this request");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
}
uctx.cs = client->cs;
REXDENT();
finish:
- RETURN_MODULE_RCODE(rcode);
+ RETURN_UNLANG_RCODE(rcode);
}
static xlat_arg_parser_t const xlat_client_args[] = {
#include <freeradius-devel/server/map_proc.h>
-static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request,
+static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
fr_value_box_list_t *key, map_list_t const *maps);
/*
* - #RLM_MODULE_NOOP no rows were returned.
* - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
* - #RLM_MODULE_FAIL if an error occurred.
- * @param[in] mod_inst #rlm_csv_t.
- * @param[in] proc_inst mapping map entries to field numbers.
+ * @param[in] mpctx #map_ctx_t, which contains the module and map instances.
* @param[in,out] request The current request.
* @param[in] key key to look for
* @param[in] maps Head of the map list.
* @return UNLANG_ACTION_CALCULATE_RESULT
*/
-static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request,
+static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
fr_value_box_list_t *key, map_list_t const *maps)
{
- rlm_csv_t const *inst = talloc_get_type_abort_const(mod_inst, rlm_csv_t);
+ rlm_csv_t const *inst = talloc_get_type_abort_const(mpctx->moi, rlm_csv_t);
fr_value_box_t *key_head = fr_value_box_list_head(key);
if (!key_head) {
REDEBUG("CSV key cannot be (null)");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
if ((inst->key_data_type == FR_TYPE_OCTETS) || (inst->key_data_type == FR_TYPE_STRING)) {
FR_VALUE_BOX_LIST_FREE, true,
SIZE_MAX) < 0) {
REDEBUG("Failed parsing key");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
}
- RETURN_MODULE_RCODE(mod_map_apply(inst, request, key_head, maps));
+ RETURN_UNLANG_RCODE(mod_map_apply(inst, request, key_head, maps));
}
* - #RLM_MODULE_NOOP no rows were returned or columns matched.
* - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
* - #RLM_MODULE_FAIL if a fault occurred.
- * @param mod_inst unused.
- * @param proc_inst cached jpath sequences.
+ * @param mpctx Call context for the map processor, containing the jpath cache.
* @param request The current request.
* @param json JSON string to parse.
* @param maps Head of the map list.
* @return UNLANG_ACTION_CALCULATE_RESULT
*/
-static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, UNUSED void const *mod_inst, void *proc_inst, request_t *request,
+static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
fr_value_box_list_t *json, map_list_t const *maps)
{
rlm_rcode_t rcode = RLM_MODULE_UPDATED;
struct json_tokener *tok;
- rlm_json_jpath_cache_t *cache = proc_inst;
+ rlm_json_jpath_cache_t const *cache = mpctx->mpi;
map_t const *map = NULL;
rlm_json_jpath_to_eval_t to_eval;
if (!json_head) {
REDEBUG("JSON map input cannot be (null)");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
if (fr_value_box_list_concat_in_place(request,
FR_VALUE_BOX_LIST_FREE, true,
SIZE_MAX) < 0) {
REDEBUG("Failed concatenating input");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
json_str = json_head->vb_strvalue;
if ((talloc_array_length(json_str) - 1) == 0) {
REDEBUG("JSON map input length must be > 0");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
tok = json_tokener_new();
json_object_put(to_eval.root);
json_tokener_free(tok);
- RETURN_MODULE_RCODE(rcode);
+ RETURN_UNLANG_RCODE(rcode);
}
static int mod_instantiate(module_inst_ctx_t const *mctx)
* - #RLM_MODULE_NOOP no rows were returned.
* - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
* - #RLM_MODULE_FAIL if an error occurred.
- * @param[in] mod_inst #rlm_ldap_t
- * @param[in] proc_inst unused.
+ * @param[in] mpctx module map ctx.
* @param[in,out] request The current request.
* @param[in] url LDAP url specifying base DN and filter.
* @param[in] maps Head of the map list.
* @return UNLANG_ACTION_CALCULATE_RESULT
*/
-static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request,
+static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
fr_value_box_list_t *url, map_list_t const *maps)
{
- rlm_ldap_t const *inst = talloc_get_type_abort_const(mod_inst, rlm_ldap_t);
+ rlm_ldap_t const *inst = talloc_get_type_abort_const(mpctx->mpi, rlm_ldap_t);
fr_ldap_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, fr_ldap_thread_t);
LDAPURLDesc *ldap_url;
if (fr_uri_escape_list(url, ldap_uri_parts, NULL) < 0) {
RPERROR("Failed to escape LDAP map URI");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
url_head = fr_value_box_list_head(url);
if (!url_head) {
REDEBUG("LDAP URL cannot be empty");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
if (fr_value_box_list_concat_in_place(url_head, url_head, url, FR_TYPE_STRING,
FR_VALUE_BOX_LIST_FREE, true, SIZE_MAX) < 0) {
RPEDEBUG("Failed concatenating input");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
if (!ldap_is_ldap_url(url_head->vb_strvalue)) {
REDEBUG("Map query string does not look like a valid LDAP URI");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
MEM(map_ctx = talloc_zero(unlang_interpret_frame_talloc_ctx(request), ldap_map_ctx_t));
RPEDEBUG("Parsing LDAP URL failed - %s", fr_ldap_url_err_to_str(ldap_url_ret));
fail:
talloc_free(map_ctx);
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
ldap_url = map_ctx->ldap_url;
sql_group_ctx_t *group_ctx; //!< Context used for retrieving user group membership.
} sql_autz_ctx_t;
-/** Context for SQL maps
- *
- */
-typedef struct {
- rlm_sql_t const *inst;
- map_list_t const *maps;
- fr_sql_query_t *query_ctx;
-} sql_map_ctx_t;
-
typedef struct {
fr_value_box_t user; //!< Expansion of sql_user_name.
fr_value_box_t filename; //!< File name to write SQL logs to.
/** Process the results of an SQL map query
*
- * @param[out] p_result Result of applying the map.
- * @param[in] request Current request.
- * @param[in] uctx Map context.
- * @return One of UNLANG_ACTION_*
+ * @param p_result Result of map expansion:
+ * - #RLM_MODULE_NOOP no rows were returned or columns matched.
+ * - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
+ * - #RLM_MODULE_FAIL if a fault occurred.
+ * @param mpctx Map context, containing the module instance.
+ * @param request The current request.
+ * @param query string to execute.
+ * @param maps Head of the map list.
+ * @return UNLANG_ACTION_CALCULATE_RESULT
*/
-static unlang_action_t mod_map_resume(unlang_result_t *p_result, request_t *request, void *uctx)
+static unlang_action_t mod_map_resume(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
+ UNUSED fr_value_box_list_t *query, map_list_t const *maps)
{
- sql_map_ctx_t *map_ctx = talloc_get_type_abort(uctx, sql_map_ctx_t);
- fr_sql_query_t *query_ctx = map_ctx->query_ctx;
- map_list_t const *maps = map_ctx->maps;
- rlm_sql_t const *inst = map_ctx->inst;
+ fr_sql_query_t *query_ctx = talloc_get_type_abort(mpctx->rctx, fr_sql_query_t);
+ rlm_sql_t const *inst = mpctx->moi;
map_t const *map;
rlm_rcode_t rcode = RLM_MODULE_UPDATED;
sql_rcode_t ret;
finish:
talloc_free(fields);
- talloc_free(map_ctx);
+ talloc_free(query_ctx);
RETURN_UNLANG_RCODE(rcode);
}
* - #RLM_MODULE_NOOP no rows were returned or columns matched.
* - #RLM_MODULE_UPDATED if one or more #fr_pair_t were added to the #request_t.
* - #RLM_MODULE_FAIL if a fault occurred.
- * @param mod_inst #rlm_sql_t instance.
- * @param proc_inst Instance data for this specific mod_proc call (unused).
- * @param request The current request.
- * @param query string to execute.
- * @param maps Head of the map list.
+ * @param mpctx Map context, containing the module instance.
+ * @param request The current request.
+ * @param query string to execute.
+ * @param maps Head of the map list.
* @return UNLANG_ACTION_CALCULATE_RESULT
*/
-static unlang_action_t mod_map_proc(rlm_rcode_t *p_result, void const *mod_inst, UNUSED void *proc_inst, request_t *request,
- fr_value_box_list_t *query, map_list_t const *maps)
+static unlang_action_t mod_map_proc(unlang_result_t *p_result, map_ctx_t const *mpctx, request_t *request,
+ fr_value_box_list_t *query, UNUSED map_list_t const *maps)
{
- rlm_sql_t const *inst = talloc_get_type_abort_const(mod_inst, rlm_sql_t);
+ rlm_sql_t const *inst = talloc_get_type_abort_const(mpctx->moi, rlm_sql_t);
rlm_sql_thread_t *thread = talloc_get_type_abort(module_thread(inst->mi)->data, rlm_sql_thread_t);
fr_value_box_t *query_head = fr_value_box_list_head(query);
- sql_map_ctx_t *map_ctx;
+ fr_sql_query_t *query_ctx = NULL;
fr_value_box_t *vb = NULL;
rlm_sql_escape_uctx_t *escape_uctx = NULL;
if (!query_head) {
REDEBUG("Query cannot be (null)");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
while ((vb = fr_value_box_list_next(query, vb))) {
FR_VALUE_BOX_LIST_FREE, true,
SIZE_MAX) < 0) {
RPEDEBUG("Failed concatenating input string");
- RETURN_MODULE_FAIL;
+ RETURN_UNLANG_FAIL;
}
- MEM(map_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), sql_map_ctx_t));
- *map_ctx = (sql_map_ctx_t) {
- .inst = inst,
- .maps = maps,
- };
-
- MEM(map_ctx->query_ctx = fr_sql_query_alloc(map_ctx, inst, request,
- thread->trunk, query_head->vb_strvalue, SQL_QUERY_SELECT));
-
- if (unlang_function_push(NULL, request, NULL, mod_map_resume, NULL, 0,
- UNLANG_SUB_FRAME, map_ctx) != UNLANG_ACTION_PUSHED_CHILD) RETURN_MODULE_FAIL;
+ query_ctx = fr_sql_query_alloc(unlang_interpret_frame_talloc_ctx(request), inst, request,
+ thread->trunk, query_head->vb_strvalue, SQL_QUERY_SELECT);
- return unlang_function_push(NULL, request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, map_ctx->query_ctx);
+ if (unlang_map_yield(request, mod_map_resume, NULL, 0, query_ctx) != UNLANG_ACTION_YIELD) RETURN_UNLANG_FAIL;
+ return unlang_function_push(NULL, request, inst->select, NULL, NULL, 0, UNLANG_SUB_FRAME, query_ctx);
}
/** xlat escape function for drivers which do not provide their own