/** Yield, spawning a child request, and resuming once the child request is complete * * @param[in] out Final rcode from when evaluation of the child request finishes. * @param[out] child - If not NULL, and points to a NULL pointer a pointer to the * child will be provided. * The caller can then manipulate the child, adding request data * and/or attributes. * The child pointer must be explicitly freed with * #unlang_subrequest_free once it is no longer needed. * - If not NULL, and points to a request, the request will be run * through the section passed as section_cs. The request must * have been allocated during a previous call to * unlang_module_yield_to_subrequest. * - If NULL the child will be automatically freed when the subrequest * completes. * @param[in] parent The current request. * @param[in] section_cs to execute. * @param[in] default_rcode The rcode the child starts executing its section with. * @param[in] resume function to call when the child has finished executing. * @param[in] signal function to call if a signal is received. * @param[in] rctx to pass to the resume() and signal() callbacks. * @return * - RLM_MODULE_YIELD. */ rlm_rcode_t unlang_module_yield_to_subrequest(rlm_rcode_t *out, REQUEST **child, REQUEST *parent, CONF_SECTION *section_cs, rlm_rcode_t default_rcode, fr_unlang_module_resume_t resume, fr_unlang_module_signal_t signal, void *rctx) { unlang_t *instruction = (unlang_t *)cf_data_value(cf_data_find(section_cs, unlang_group_t, NULL)); rad_assert(instruction); /* * Push the resumption point */ (void) unlang_module_yield(parent, resume, signal, rctx); if (!child || !*child) { CONF_SECTION *server_cs; fr_dict_t *dict; server_cs = virtual_server_by_child(section_cs); /* * We don't support executing orphaned sections. */ rad_assert(server_cs); /* * Work out the dictionary from the server section's cf_data */ dict = virtual_server_namespace(cf_section_name2(server_cs)); /* * If this asserts, fix the validation logic * don't just set a default value. * * *ALL* virtual servers should have a namespace. */ rad_assert(dict); /* * Push the subrequest frame. */ unlang_subrequest_push(out, child, parent, server_cs, instruction, dict, default_rcode, true); } else { unlang_subrequest_push_again(out, talloc_get_type_abort(*child, REQUEST), parent, instruction, default_rcode, true); } return RLM_MODULE_YIELD; /* This may allow us to do optimisations in future */ }
/** Find an existing module instance * * @param[in] modules section in the main config. * @param[in] asked_name The name of the module we're attempting to find. * May include '-' which indicates that it's ok for * the module not to be loaded. * @return * - Module instance matching name. * - NULL if not such module exists. */ module_instance_t *module_find(CONF_SECTION *modules, char const *asked_name) { char const *inst_name; void *inst; if (!modules) return NULL; /* * Look for the real name. Ignore the first character, * which tells the server "it's OK for this module to not * exist." */ inst_name = asked_name; if (inst_name[0] == '-') inst_name++; inst = cf_data_value(cf_data_find(modules, module_instance_t, inst_name)); if (!inst) return NULL; return talloc_get_type_abort(inst, module_instance_t); }
/** Wrapper around dl_instance * * @param[in] ctx to allocate data in (instance of proto_radius). * @param[out] out Where to write a dl_instance_t containing the module handle and instance. * @param[in] parent Base structure address. * @param[in] ci #CONF_PAIR specifying the name of the type module. * @param[in] rule unused. * @return * - 0 on success. * - -1 on failure. */ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) { char const *name = cf_pair_value(cf_item_to_pair(ci)); dl_instance_t *parent_inst; CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); CONF_SECTION *transport_cs; transport_cs = cf_section_find(cs, name, NULL); /* * Allocate an empty section if one doesn't exist * this is so defaults get parsed. */ if (!transport_cs) transport_cs = cf_section_alloc(cs, cs, name, NULL); parent_inst = cf_data_value(cf_data_find(cs, dl_instance_t, "rlm_radius")); rad_assert(parent_inst); return dl_instance(ctx, out, transport_cs, parent_inst, name, DL_TYPE_SUBMODULE); }
/** Initialise a module specific connection pool * * @see fr_pool_init * * @param[in] module section. * @param[in] opaque data pointer to pass to callbacks. * @param[in] c Callback to create new connections. * @param[in] a Callback to check the status of connections. * @param[in] log_prefix override, if NULL will be set automatically from the module CONF_SECTION. * @param[in] trigger_prefix if NULL will be set automatically from the module CONF_SECTION. * @param[in] trigger_args to make available in any triggers executed by the connection pool. * @return * - New connection pool. * - NULL on error. */ fr_pool_t *module_connection_pool_init(CONF_SECTION *module, void *opaque, fr_pool_connection_create_t c, fr_pool_connection_alive_t a, char const *log_prefix, char const *trigger_prefix, VALUE_PAIR *trigger_args) { CONF_SECTION *cs, *mycs; char log_prefix_buff[128]; char trigger_prefix_buff[128]; fr_pool_t *pool; char const *cs_name1, *cs_name2; int ret; #define parent_name(_x) cf_section_name(cf_item_to_section(cf_parent(_x))) cs_name1 = cf_section_name1(module); cs_name2 = cf_section_name2(module); if (!cs_name2) cs_name2 = cs_name1; if (!trigger_prefix) { snprintf(trigger_prefix_buff, sizeof(trigger_prefix_buff), "modules.%s.pool", cs_name1); trigger_prefix = trigger_prefix_buff; } if (!log_prefix) { snprintf(log_prefix_buff, sizeof(log_prefix_buff), "rlm_%s (%s)", cs_name1, cs_name2); log_prefix = log_prefix_buff; } /* * Get sibling's pool config section */ ret = module_sibling_section_find(&cs, module, "pool"); switch (ret) { case -1: return NULL; case 1: DEBUG4("%s: Using pool section from \"%s\"", log_prefix, parent_name(cs)); break; case 0: DEBUG4("%s: Using local pool section", log_prefix); break; } /* * Get our pool config section */ mycs = cf_section_find(module, "pool", NULL); if (!mycs) { DEBUG4("%s: Adding pool section to config item \"%s\" to store pool references", log_prefix, cf_section_name(module)); mycs = cf_section_alloc(module, module, "pool", NULL); } /* * Sibling didn't have a pool config section * Use our own local pool. */ if (!cs) { DEBUG4("%s: \"%s.pool\" section not found, using \"%s.pool\"", log_prefix, parent_name(cs), parent_name(mycs)); cs = mycs; } /* * If fr_pool_init has already been called * for this config section, reuse the previous instance. * * This allows modules to pass in the config sections * they would like to use the connection pool from. */ pool = cf_data_value(cf_data_find(cs, fr_pool_t, NULL)); if (!pool) { DEBUG4("%s: No pool reference found for config item \"%s.pool\"", log_prefix, parent_name(cs)); pool = fr_pool_init(cs, cs, opaque, c, a, log_prefix); if (!pool) return NULL; fr_pool_enable_triggers(pool, trigger_prefix, trigger_args); if (fr_pool_start(pool) < 0) { ERROR("%s: Starting initial connections failed", log_prefix); return NULL; } DEBUG4("%s: Adding pool reference %p to config item \"%s.pool\"", log_prefix, pool, parent_name(cs)); cf_data_add(cs, pool, NULL, false); return pool; } fr_pool_ref(pool); DEBUG4("%s: Found pool reference %p in config item \"%s.pool\"", log_prefix, pool, parent_name(cs)); /* * We're reusing pool data add it to our local config * section. This allows other modules to transitively * re-use a pool through this module. */ if (mycs != cs) { DEBUG4("%s: Copying pool reference %p from config item \"%s.pool\" to config item \"%s.pool\"", log_prefix, pool, parent_name(cs), parent_name(mycs)); cf_data_add(mycs, pool, NULL, false); } return pool; }
/** Allocate a #CONF_SECTION * * @param[in] ctx to allocate * @param[in] parent #CONF_SECTION to hang this #CONF_SECTION off of. * If parent is not NULL, the new section will be added as a child. * @param[in] name1 Primary name. * @param[in] name2 Secondary name. * @param[in] filename Caller file name for debugging. May be overridden later. * @param[in] lineno Caller line number for debugging. May be overridden later. * @return * - NULL on error. * - A new #CONF_SECTION parented by parent. */ CONF_SECTION *_cf_section_alloc(TALLOC_CTX *ctx, CONF_SECTION *parent, char const *name1, char const *name2, char const *filename, int lineno) { CONF_SECTION *cs; char buffer[1024]; if (!name1) return NULL; if (name2 && parent) { char const *p; p = strchr(name2, '$'); if (p && (p[1] != '{')) p = NULL; if (p) { name2 = cf_expand_variables(parent->item.filename, &parent->item.lineno, parent, buffer, sizeof(buffer), name2, NULL); if (!name2) { ERROR("Failed expanding section name"); return NULL; } } } cs = talloc_zero(ctx, CONF_SECTION); if (!cs) return NULL; cs->item.type = CONF_ITEM_SECTION; cs->item.parent = cf_section_to_item(parent); fr_cursor_init(&cs->item.cursor, &cs->item.child); MEM(cs->name1 = talloc_typed_strdup(cs, name1)); if (name2) { MEM(cs->name2 = talloc_typed_strdup(cs, name2)); cs->name2_quote = T_BARE_WORD; } talloc_set_destructor(cs, _cf_section_free); if (filename) cf_filename_set(cs, filename); if (lineno) cf_lineno_set(cs, lineno); if (parent) { CONF_DATA const *cd; CONF_PARSER *rule; cs->depth = parent->depth + 1; cf_item_add(parent, &(cs->item)); cd = cf_data_find(CF_TO_ITEM(parent), CONF_PARSER, cf_section_name1(parent)); if (cd) { rule = cf_data_value(cd); /* * The parent has an ON_READ rule. Check * if that rule has a child which matches * this section. */ if ((FR_BASE_TYPE(rule->type) == FR_TYPE_SUBSECTION) && ((rule->type & FR_TYPE_ON_READ) != 0) && rule->subcs) { CONF_PARSER const *rule_p; for (rule_p = rule->subcs; rule_p->name; rule_p++) { if ((FR_BASE_TYPE(rule_p->type) == FR_TYPE_SUBSECTION) && ((rule_p->type & FR_TYPE_ON_READ) != 0) && (strcmp(rule_p->name, name1) == 0)) { (void) _cf_section_rule_push(cs, rule_p, cd->item.filename, cd->item.lineno); (void) rule_p->func(ctx, NULL, NULL, cf_section_to_item(cs), rule_p); return cs; } } } } /* * Call any ON_READ callback. And push this rule * to the child section, so that the child can * pick it up. */ cd = cf_data_find(CF_TO_ITEM(parent), CONF_PARSER, name1); if (cd) { rule = cf_data_value(cd); if (rule->func && (FR_BASE_TYPE(rule->type) == FR_TYPE_SUBSECTION) && ((rule->type & FR_TYPE_ON_READ) != 0)) { (void) cf_section_rules_push(cs, rule); (void) rule->func(ctx, NULL, NULL, cf_section_to_item(cs), rule); } } } return cs; }