From: Arran Cudbard-Bell Date: Sun, 19 Nov 2017 11:32:23 +0000 (+0000) Subject: Add registration functions and a new prototype for async xlats X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed5f8e4bb01f86ea5b06189cfaf900c77112ce14;p=thirdparty%2Ffreeradius-server.git Add registration functions and a new prototype for async xlats --- diff --git a/src/include/xlat.h b/src/include/xlat.h index 08586c5fb52..21315c79b6e 100644 --- a/src/include/xlat.h +++ b/src/include/xlat.h @@ -66,44 +66,80 @@ typedef ssize_t (*xlat_func_t)(TALLOC_CTX *ctx, char **out, size_t outlen, void const *mod_inst, void const *xlat_inst, REQUEST *request, char const *fmt); +/** Async xlat callback function + * + * Ingests a list of value boxes as arguments, with arguments delimited by spaces. + * + * @param[in] ctx to allocate any fr_value_box_t in. + * @param[out] out Where to append #fr_value_box_t containing the output of this function. + * @param[in] xlat_inst Global xlat instance. + * @param[in] xlat_thread_inst Thread specific xlat instance. + * @param[in] request The current request. + * @param[in] in Input arguments. + * @return + * - XLAT_ACTION_YIELD xlat function is waiting on an I/O event and + * has pushed a resumption function onto the stack. + * - XLAT_ACTION_DONE xlat function completed. This does not necessarily + * mean it turned a result. + * - XLAT_ACTION_FAIL the xlat function failed. + */ +typedef xlat_action_t (*xlat_func_async_t)(TALLOC_CTX *ctx, fr_cursor_t *out, + void const *xlat_inst, void *xlat_thread_inst, + REQUEST *request, fr_value_box_t const *in, + void *uctx); + /** Allocate new instance data for an xlat instance * - * @param[out] xlat_inst Structure to populate. Allocated by #map_proc_instantiate. - * @param[in] mod_inst Module instance that registered the #xlat_func_t. - * @param[in] fmt string to base instantiation around. + * @param[out] xlat_inst Structure to populate. Allocated by #map_proc_instantiate. + * @param[in] exp Tokenized expression to use in expansion. + * @param[in] uctx passed to the registration function. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*xlat_instantiate_t)(void *xlat_inst, xlat_exp_t const *exp, void *uctx); + +/** Allocate new tread instance data for an xlat instance + * + * @param[in] xlat_inst Previously instantiated xlat instance. + * @param[out] xlat_thread_inst Thread specific structure to populate. + * Allocated by #map_proc_instantiate. + * @param[in] exp Tokenized expression to use in expansion. + * @param[in] uctx passed to the registration function. * @return * - 0 on success. * - -1 on failure. */ -typedef int (*xlat_instantiate_t)(void *xlat_inst, void *mod_inst, char const *fmt); +typedef int (*xlat_thread_instantiate_t)(void *xlat_inst, void *xlat_thread_inst, + xlat_exp_t const *exp, void *uctx); -xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out, - xlat_exp_t const **child, bool *alternate, - REQUEST *request, xlat_exp_t const **in, - fr_value_box_t *result); +xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out, + xlat_exp_t const **child, bool *alternate, + REQUEST *request, xlat_exp_t const **in, + fr_value_box_t *result); -xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t const **child, - REQUEST *request, xlat_exp_t const **in); +xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t const **child, + REQUEST *request, xlat_exp_t const **in); -ssize_t xlat_eval(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, - void const *escape_ctx) - CC_HINT(nonnull (1 ,3 ,4)); +ssize_t xlat_eval(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, + void const *escape_ctx) + CC_HINT(nonnull (1 ,3 ,4)); -ssize_t xlat_eval_compiled(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat, - xlat_escape_t escape, void const *escape_ctx) - CC_HINT(nonnull (1 ,3 ,4)); +ssize_t xlat_eval_compiled(char *out, size_t outlen, REQUEST *request, xlat_exp_t const *xlat, + xlat_escape_t escape, void const *escape_ctx) + CC_HINT(nonnull (1 ,3 ,4)); -ssize_t xlat_aeval(TALLOC_CTX *ctx, char **out, REQUEST *request, - char const *fmt, xlat_escape_t escape, void const *escape_ctx) - CC_HINT(nonnull (2, 3, 4)); +ssize_t xlat_aeval(TALLOC_CTX *ctx, char **out, REQUEST *request, + char const *fmt, xlat_escape_t escape, void const *escape_ctx) + CC_HINT(nonnull (2, 3, 4)); -ssize_t xlat_aeval_compiled(TALLOC_CTX *ctx, char **out, REQUEST *request, - xlat_exp_t const *xlat, xlat_escape_t escape, void const *escape_ctx) - CC_HINT(nonnull (2, 3, 4)); +ssize_t xlat_aeval_compiled(TALLOC_CTX *ctx, char **out, REQUEST *request, + xlat_exp_t const *xlat, xlat_escape_t escape, void const *escape_ctx) + CC_HINT(nonnull (2, 3, 4)); -ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error); +ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error); -size_t xlat_snprint(char *buffer, size_t bufsize, xlat_exp_t const *node); +size_t xlat_snprint(char *buffer, size_t bufsize, xlat_exp_t const *node); #define XLAT_DEFAULT_BUF_LEN 2048 @@ -112,7 +148,13 @@ int xlat_register(void *mod_inst, char const *name, xlat_instantiate_t instantiate, size_t inst_size, size_t buf_len, bool async_safe); -void xlat_unregister(void *mod_inst, char const *name, xlat_func_t func); +int xlat_async_register(TALLOC_CTX *ctx, + char const *name, xlat_func_t func, + xlat_instantiate_t instantiate, size_t inst_size, + xlat_thread_instantiate_t thread_instantiate, size_t thread_inst_size, + void *uctx); + +void xlat_unregister(char const *name); void xlat_unregister_module(void *instance); int xlat_register_redundant(CONF_SECTION *cs); int xlat_init(void); diff --git a/src/main/modules.c b/src/main/modules.c index b41aa5a7fef..8036c920c17 100644 --- a/src/main/modules.c +++ b/src/main/modules.c @@ -765,7 +765,7 @@ static int _module_instance_free(module_instance_t *mod_inst) * Remove all xlat's registered to module instance. */ if (mod_inst->dl_inst && mod_inst->dl_inst->data) { - xlat_unregister(mod_inst->dl_inst->data, mod_inst->name, NULL); + xlat_unregister(mod_inst->name); /* * Remove any registered paircompares. */ diff --git a/src/main/unit_test_module.c b/src/main/unit_test_module.c index 97d987b0ef1..fadb74d1f62 100644 --- a/src/main/unit_test_module.c +++ b/src/main/unit_test_module.c @@ -995,7 +995,7 @@ finish: talloc_free(request); talloc_free(state); - xlat_unregister(NULL, "poke", xlat_poke); + xlat_unregister("poke"); /* * Free the event list. diff --git a/src/main/xlat.h b/src/main/xlat.h index 5231b63adc2..583e29a1b40 100644 --- a/src/main/xlat.h +++ b/src/main/xlat.h @@ -32,22 +32,38 @@ # define XLAT_DEBUG(...) #endif +/** Function types + * + */ +typedef enum { + XLAT_FUNC_STRING, //!< Ingests and excretes strings. + XLAT_FUNC_BOXED //!< Ingests and excretes value boxes. +} xlat_func_type_t; + typedef struct xlat_t { - char name[FR_MAX_STRING_LEN]; //!< Name of the xlat expansion. - int length; //!< Length of name. - void *mod_inst; //!< Module instance passed to xlat and escape functions. - xlat_func_t func; //!< xlat function. - xlat_escape_t escape; //!< Escape function to apply to dynamic input to func. - xlat_instantiate_t instantiate; //!< Instantiation function. - size_t inst_size; //!< Length of instance data to pre-allocate. - size_t buf_len; //!< Length of output buffer to pre-allocate. - bool internal; //!< If true, cannot be redefined. - bool async_safe; ///!< If true, is async safe + char const *name; //!< Name of xlat function. + xlat_func_t func; //!< xlat function. + xlat_func_type_t type; //!< Type of xlat function. + + xlat_instantiate_t instantiate; //!< Instantiation function. + xlat_thread_instantiate_t thread_instantiate; //!< Thread instantiation function. + + bool internal; //!< If true, cannot be redefined. + + size_t inst_size; //!< Size of instance data to pre-allocate. + size_t thread_inst_size; //!< Size of the thread instance data to pre-allocate. + + bool async_safe; //!< If true, is async safe + void *uctx; //!< uctx to pass to instantiation functions. + + size_t buf_len; //!< Length of output buffer to pre-allocate. + void *mod_inst; //!< Module instance passed to xlat + xlat_escape_t escape; //!< Escape function to apply to dynamic input to func. } xlat_t; typedef enum { XLAT_LITERAL, //!< Literal string - XLAT_ONE_LETTER, //!< Literal string with %v + XLAT_ONE_LETTER, //!< Literal string with %v XLAT_FUNC, //!< xlat module XLAT_VIRTUAL, //!< virtual attribute XLAT_ATTRIBUTE, //!< xlat attribute diff --git a/src/main/xlat_func.c b/src/main/xlat_func.c index 146da0d6f32..b3841e7b924 100644 --- a/src/main/xlat_func.c +++ b/src/main/xlat_func.c @@ -579,12 +579,16 @@ done: static int xlat_cmp(void const *one, void const *two) { xlat_t const *a = one, *b = two; + size_t a_len, b_len; int ret; - ret = (a->length > b->length) - (a->length < b->length); + a_len = strlen(a->name); + b_len = strlen(b->name); + + ret = (a_len > b_len) - (a_len < b_len); if (ret != 0) return ret; - return memcmp(a->name, b->name, a->length); + return memcmp(a->name, b->name, a_len); } @@ -593,27 +597,28 @@ static int xlat_cmp(void const *one, void const *two) */ xlat_t *xlat_find(char const *name) { - xlat_t my_xlat; + xlat_t find; + xlat_t *found; if (!xlat_root) return NULL; - strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); - my_xlat.length = strlen(my_xlat.name); + find.name = name; + found = rbtree_finddata(xlat_root, &find); - return rbtree_finddata(xlat_root, &my_xlat); + return found; } /** Register an xlat function. * - * @param[in] mod_inst Instance of module that's registering the xlat function. - * @param[in] name xlat name. - * @param[in] func xlat function to be called. - * @param[in] escape function to sanitize any sub expansions passed to the xlat function. - * @param[in] instantiate function to pre-parse any xlat specific data. - * @param[in] inst_size sizeof() this xlat's instance data. - * @param[in] buf_len Size of the output buffer to allocate when calling the function. - * May be 0 if the function allocates its own buffer. - * @param[in] async_safe whether or not the function is async-safe. + * @param[in] mod_inst Instance of module that's registering the xlat function. + * @param[in] name xlat name. + * @param[in] func xlat function to be called. + * @param[in] escape function to sanitize any sub expansions passed to the xlat function. + * @param[in] instantiate function to pre-parse any xlat specific data. + * @param[in] inst_size sizeof() this xlat's instance data. + * @param[in] buf_len Size of the output buffer to allocate when calling the function. + * May be 0 if the function allocates its own buffer. + * @param[in] async_safe whether or not the function is async-safe. * @return * - 0 on success. * - -1 on failure. @@ -624,7 +629,8 @@ int xlat_register(void *mod_inst, char const *name, size_t buf_len, bool async_safe) { xlat_t *c; - xlat_t my_xlat; + xlat_t find; + bool new = false; if (!xlat_root) xlat_init(); @@ -636,9 +642,8 @@ int xlat_register(void *mod_inst, char const *name, /* * If it already exists, replace the instance. */ - strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); - my_xlat.length = strlen(my_xlat.name); - c = rbtree_finddata(xlat_root, &my_xlat); + find.name = name; + c = rbtree_finddata(xlat_root, &find); if (c) { if (c->internal) { ERROR("%s: Cannot re-define internal expansion %s", __FUNCTION__, name); @@ -650,24 +655,19 @@ int xlat_register(void *mod_inst, char const *name, return -1; } - c->func = func; - c->buf_len = buf_len; - c->escape = escape; - c->mod_inst = mod_inst; - c->instantiate = instantiate; - c->inst_size = inst_size; - return 0; - } - /* * Doesn't exist. Create it. */ - c = talloc_zero(xlat_root, xlat_t); + } else { + c = talloc_zero(xlat_root, xlat_t); + c->name = talloc_typed_strdup(c, name); + new = true; + } + c->func = func; + c->type = XLAT_FUNC_BOXED; c->buf_len = buf_len; c->escape = escape; - strlcpy(c->name, name, sizeof(c->name)); - c->length = strlen(c->name); c->mod_inst = mod_inst; c->instantiate = instantiate; c->inst_size = inst_size; @@ -675,7 +675,118 @@ int xlat_register(void *mod_inst, char const *name, DEBUG3("%s: %s", c->name, __FUNCTION__); - if (!rbtree_insert(xlat_root, c)) { + if (new && !rbtree_insert(xlat_root, c)) { + ERROR("Failed inserting xlat registration for %s", + c->name); + talloc_free(c); + return -1; + } + + return 0; +} + +/** Remove an xlat function from the function tree + * + * @param[in] xlat to free. + * @return 0 + */ +static int _xlat_free(xlat_t *xlat) +{ + xlat_t *c; + + if (!xlat_root) return 0; + + c = rbtree_finddata(xlat_root, xlat); + if (!c) return 0; + + rbtree_deletebydata(xlat_root, c); + + if (rbtree_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root); + + return 0; +} + +/** Register an async xlat + * + * All functions registered must be async_safe. + * + * @param[in] ctx Used to automate deregistration of the xlat fnction. + * @param[in] name of the xlat. + * @param[in] func to register. + * @param[in] instantiate Instantiation function. Called whenever a xlat is + * compiled. + * @param[in] inst_size The size of the instance struct. + * Pre-allocated for use by the instantiate function. + * If 0, no memory will be allocated. + * @param[in] thread_instantiate thread_instantiation_function. Called whenever a + * a thread is started to create thread local instance + * data. + * @param[in] thread_inst_size The size of the thread instance struct. + * Pre-allocated for use by the thread instance function. + * If 0, no memory will be allocated. + * @param[in] uctx To pass to instantiate callbacks and the xlat function + * when it's called. Usually the module instance that + * registered the xlat. + * @return + * - 0 on success. + * - -1 on failure. + */ +int xlat_async_register(TALLOC_CTX *ctx, + char const *name, xlat_func_t func, + xlat_instantiate_t instantiate, size_t inst_size, + xlat_thread_instantiate_t thread_instantiate, size_t thread_inst_size, + void *uctx) +{ + xlat_t *c; + xlat_t find; + bool new = false; + + if (!xlat_root) xlat_init(); + + if (!name || !*name) { + ERROR("%s: Invalid xlat name", __FUNCTION__); + return -1; + } + + /* + * If it already exists, replace the instance. + */ + find.name = name; + c = rbtree_finddata(xlat_root, &find); + if (c) { + if (c->internal) { + ERROR("%s: Cannot re-define internal expansion %s", __FUNCTION__, name); + return -1; + } + + if (!c->async_safe) { + ERROR("%s: Cannot change async capability of %s", __FUNCTION__, name); + return -1; + } + + /* + * Doesn't exist. Create it. + */ + } else { + c = talloc_zero(ctx, xlat_t); + c->name = talloc_typed_strdup(c, name); + new = true; + } + + c->func = func; + c->type = XLAT_FUNC_BOXED; + c->instantiate = instantiate; + c->thread_instantiate = thread_instantiate; + c->inst_size = inst_size; + c->thread_inst_size = thread_inst_size; + c->async_safe = true; + c->uctx = uctx; + + talloc_set_destructor(c, _xlat_free); + + DEBUG3("%s: %s", c->name, __FUNCTION__); + + if (new && !rbtree_insert(xlat_root, c)) { ERROR("Failed inserting xlat registration for %s", c->name); talloc_free(c); @@ -690,31 +801,25 @@ int xlat_register(void *mod_inst, char const *name, * We can only have one function to call per name, so the passing of "func" * here is extraneous. * - * @param[in] mod_inst data. * @param[in] name xlat to unregister. - * @param[in] func unused. */ -void xlat_unregister(void *mod_inst, char const *name, UNUSED xlat_func_t func) +void xlat_unregister(char const *name) { xlat_t *c; - xlat_t my_xlat; + xlat_t find; if (!name || !xlat_root) return; - strlcpy(my_xlat.name, name, sizeof(my_xlat.name)); - my_xlat.length = strlen(my_xlat.name); - - c = rbtree_finddata(xlat_root, &my_xlat); + find.name = name; + c = rbtree_finddata(xlat_root, &find); if (!c) return; - if (c->mod_inst != mod_inst) return; - rbtree_deletebydata(xlat_root, c); if (rbtree_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root); } -static int xlat_unregister_callback(void *mod_inst, void *data) +static int _xlat_unregister_callback(void *mod_inst, void *data) { xlat_t *c = (xlat_t *) data; @@ -727,7 +832,7 @@ void xlat_unregister_module(void *instance) { if (!xlat_root) return; /* All xlats have already been freed */ - rbtree_walk(xlat_root, RBTREE_DELETE_ORDER, xlat_unregister_callback, instance); + rbtree_walk(xlat_root, RBTREE_DELETE_ORDER, _xlat_unregister_callback, instance); if (rbtree_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root); }