]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add registration functions and a new prototype for async xlats
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 19 Nov 2017 11:32:23 +0000 (11:32 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 19 Nov 2017 11:32:23 +0000 (11:32 +0000)
src/include/xlat.h
src/main/modules.c
src/main/unit_test_module.c
src/main/xlat.h
src/main/xlat_func.c

index 08586c5fb521494160454160de75a1f60f881933..21315c79b6ea7360c4a8ee3b9081a456b3edd4d4 100644 (file)
@@ -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);
index b41aa5a7fefce243edaf96a3841a15c525d50e27..8036c920c172776f34b5b8c0b2d146bacb8d0a5f 100644 (file)
@@ -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.
                 */
index 97d987b0ef11ac6a2f743a6fede2733796548244..fadb74d1f62db353227837fa66cf1e1364b15758 100644 (file)
@@ -995,7 +995,7 @@ finish:
        talloc_free(request);
        talloc_free(state);
 
-       xlat_unregister(NULL, "poke", xlat_poke);
+       xlat_unregister("poke");
 
        /*
         *      Free the event list.
index 5231b63adc2743393088658e904f6b79b286ae31..583e29a1b406c0d3df62f3ad73e326f25140ae31 100644 (file)
 #  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
index 146da0d6f32ff4725d519d835005c194a85f4880..b3841e7b924db280a68914218f89fd8d12c30857 100644 (file)
@@ -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);
 }