/** Create a client CONF_SECTION using a mapping section to map values from a result set to client attributes * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too. * * @note Caller should free CONF_SECTION passed in as out, on error. * Contents of that section will be in an undefined state. * * @param[in,out] out Section to perform mapping on. Either the root of the client config, or a parent section * (when this function is called recursively). * Should be alloced with cf_section_alloc, or if there's a separate template section, the * result of calling cf_section_dup on that section. * @param[in] map section. * @param[in] func to call to retrieve CONF_PAIR values. Must return a talloced buffer containing the value. * @param[in] data to pass to func, usually a result pointer. * @return 0 on success else -1 on error. */ int client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data) { CONF_ITEM const *ci; for (ci = cf_item_find_next(map, NULL); ci != NULL; ci = cf_item_find_next(map, ci)) { CONF_PAIR const *cp; CONF_PAIR *old; char *value; char const *attr; /* * Recursively process map subsection */ if (cf_item_is_section(ci)) { CONF_SECTION *cs, *cc; cs = cf_item_to_section(ci); /* * Use pre-existing section or alloc a new one */ cc = cf_section_sub_find_name2(out, cf_section_name1(cs), cf_section_name2(cs)); if (!cc) { cc = cf_section_alloc(out, cf_section_name1(cs), cf_section_name2(cs)); cf_section_add(out, cc); if (!cc) return -1; } if (client_map_section(cc, cs, func, data) < 0) return -1; continue; } cp = cf_item_to_pair(ci); attr = cf_pair_attr(cp); /* * The callback can return 0 (success) and not provide a value * in which case we skip the mapping pair. * * Or return -1 in which case we error out. */ if (func(&value, cp, data) < 0) { cf_log_err_cs(out, "Failed performing mapping \"%s\" = \"%s\"", attr, cf_pair_value(cp)); return -1; } if (!value) continue; /* * Replace an existing CONF_PAIR */ old = cf_pair_find(out, attr); if (old) { cf_pair_replace(out, old, value); talloc_free(value); continue; } /* * ...or add a new CONF_PAIR */ cp = cf_pair_alloc(out, attr, value, T_OP_SET, T_BARE_WORD, T_SINGLE_QUOTED_STRING); if (!cp) { cf_log_err_cs(out, "Failed allocing pair \"%s\" = \"%s\"", attr, value); talloc_free(value); return -1; } talloc_free(value); cf_item_add(out, cf_pair_to_item(cp)); } return 0; }
/* * %{poke:sql.foo=bar} */ static ssize_t xlat_poke(char **out, size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { int i; void *data, *base; char *p, *q; module_instance_t *mi; char *buffer; CONF_SECTION *modules; CONF_PAIR *cp; CONF_PARSER const *variables; size_t len; rad_assert(outlen > 1); rad_assert(request != NULL); rad_assert(fmt != NULL); rad_assert(out != NULL); rad_assert(*out); modules = cf_section_sub_find(request->root->config, "modules"); if (!modules) return 0; buffer = talloc_strdup(request, fmt); if (!buffer) return 0; p = strchr(buffer, '.'); if (!p) return 0; *(p++) = '\0'; mi = module_find(modules, buffer); if (!mi) { RDEBUG("Failed finding module '%s'", buffer); fail: talloc_free(buffer); return 0; } q = strchr(p, '='); if (!q) { RDEBUG("Failed finding '=' in string '%s'", fmt); goto fail; } *(q++) = '\0'; if (strchr(p, '.') != NULL) { RDEBUG("Can't do sub-sections right now"); goto fail; } cp = cf_pair_find(mi->cs, p); if (!cp) { RDEBUG("No such item '%s'", p); goto fail; } /* * Copy the old value to the output buffer, that way * tests can restore it later, if they need to. */ len = strlcpy(*out, cf_pair_value(cp), outlen); if (cf_pair_replace(mi->cs, cp, q) < 0) { RDEBUG("Failed replacing pair"); goto fail; } base = mi->insthandle; variables = mi->entry->module->config; /* * Handle the known configuration parameters. */ for (i = 0; variables[i].name != NULL; i++) { int ret; if (variables[i].type == PW_TYPE_SUBSECTION) continue; /* else it's a CONF_PAIR */ /* * Not the pair we want. Skip it. */ if (strcmp(variables[i].name, p) != 0) continue; if (variables[i].data) { data = variables[i].data; /* prefer this. */ } else if (base) { data = ((char *)base) + variables[i].offset; } else { DEBUG2("Internal sanity check 2 failed in cf_section_parse"); goto fail; } /* * Parse the pair we found, or a default value. */ ret = cf_pair_parse(mi->cs, variables[i].name, variables[i].type, data, variables[i].dflt, variables[i].quote); if (ret < 0) { DEBUG2("Failed inserting new value into module instance data"); goto fail; } break; /* we found it, don't do any more */ } talloc_free(buffer); return len; }