* ./build/make/jlibtool --mode=execute ./build/bin/local/fuzzer_radius -D share/dictionary /path/to/corpus/directory/
*/
-static bool init = false;
-static void * decode_ctx = NULL;
-static fr_test_point_proto_decode_t *tp = NULL;
-static fr_dict_t * dict = NULL;
+static bool init = false;
+static void *decode_ctx = NULL;
+static fr_test_point_proto_decode_t *tp = NULL;
+static dl_t *dl = NULL;
+static dl_loader_t *dl_loader;
-static dl_t * dl = NULL;
-static dl_loader_t *dl_loader;
+static fr_dict_t *dict = NULL;
+static fr_dict_gctx_t const *dict_gctx = NULL;
int LLVMFuzzerInitialize(int *argc, char ***argv);
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len);
-static void exitHandler()
+static void exitHandler(void)
{
+ fr_dict_free(&dict, __FILE__);
+
+ if (fr_dict_global_ctx_free(dict_gctx) < 0) fr_perror("fuzzer");
+
if (dl && dl->handle) {
dlclose(dl->handle);
dl->handle = NULL;
int LLVMFuzzerInitialize(int *argc, char ***argv)
{
- char const *lib_dir = getenv("FR_LIBRARY_PATH");
- char const *proto = getenv("FR_LIBRARY_FUZZ_PROTOCOL");
- char const *dict_dir = getenv("FR_DICTIONARY_DIR");
- char buffer[1024];
+ char const *lib_dir = getenv("FR_LIBRARY_PATH");
+ char const *proto = getenv("FR_LIBRARY_FUZZ_PROTOCOL");
+ char const *dict_dir = getenv("FR_DICTIONARY_DIR");
+ char buffer[1024];
if (!argc || !argv || !*argv) return -1; /* shut up clang scan */
fr_exit_now(EXIT_FAILURE);
}
- if (!fr_dict_global_ctx_init(NULL, dict_dir)) {
+ dict_gctx = fr_dict_global_ctx_init(NULL, dict_dir);
+ if (!dict_gctx) {
fr_perror("dict_global");
fr_exit_now(EXIT_FAILURE);
}
- if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("fuzzer: Failed initializing internal dictionary");
fr_exit_now(EXIT_FAILURE);
}
}
INFO("Loading dictionary: %s/dictionary", file_str);
- if (fr_dict_protocol_afrom_file(dict_end, dp->d_name, NULL) < 0) {
+ if (fr_dict_protocol_afrom_file(dict_end, dp->d_name, NULL, __FILE__) < 0) {
goto error;
}
dict_end++;
*/
int main(int argc, char *argv[])
{
- char const *dict_dir = DICTDIR;
- char c;
- int ret = 0;
- bool found = false;
- bool export = false;
+ char const *dict_dir = DICTDIR;
+ char c;
+ int ret = 0;
+ bool found = false;
+ bool export = false;
- TALLOC_CTX *autofree;
+ TALLOC_CTX *autofree;
+ fr_dict_gctx_t const *our_dict_gctx = NULL;
/*
* Must be called first, so the handler is called last
goto finish;
}
- if (!fr_dict_global_ctx_init(autofree, dict_dir)) {
+ our_dict_gctx = fr_dict_global_ctx_init(autofree, dict_dir);
+ if (!our_dict_gctx) {
fr_perror("radict");
ret = 1;
goto finish;
INFO("Loading dictionary: %s/%s", dict_dir, FR_DICTIONARY_FILE);
- if (fr_dict_internal_afrom_file(dict_end++, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(dict_end++, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("radict");
ret = 1;
goto finish;
}
finish:
- talloc_free(autofree);
+ /*
+ * Release our references on all the dicts
+ * we loaded.
+ */
+ {
+ fr_dict_t **dict_p = dicts;
+
+ do {
+ fr_dict_free(dict_p, __FILE__);
+ } while (++dict_p < dict_end);
+ }
+ if (fr_dict_global_ctx_free(our_dict_gctx) < 0) fr_perror("radict");
+ if (talloc_free(autofree) < 0) fr_perror("radict");
return found ? ret : 64;
}
*/
int main(int argc, char *argv[])
{
- int status;
- int c;
- bool display_version = false;
- bool radmin = false;
- int from_child[2] = {-1, -1};
- char *program;
- fr_schedule_t *sc = NULL;
- int ret = EXIT_SUCCESS;
+ int status;
+ int c;
+ bool display_version = false;
+ bool radmin = false;
+ int from_child[2] = {-1, -1};
+ char *program;
+ fr_schedule_t *sc = NULL;
+ int ret = EXIT_SUCCESS;
- TALLOC_CTX *global_ctx = NULL;
- main_config_t *config = NULL;
- bool talloc_memory_report = false;
+ TALLOC_CTX *global_ctx = NULL;
+ main_config_t *config = NULL;
+ bool talloc_memory_report = false;
- bool raddb_dir_set = false;
+ bool raddb_dir_set = false;
- size_t pool_size = 0;
- void *pool_page_start = NULL, *pool_page_end = NULL;
- bool do_mprotect;
+ size_t pool_size = 0;
+ void *pool_page_start = NULL, *pool_page_end = NULL;
+ bool do_mprotect;
+
+ fr_dict_gctx_t const *dict_gctx = NULL;
dl_module_loader_t *dl_modules = NULL;
* Initialise the top level dictionary hashes which hold
* the protocols.
*/
- if (!fr_dict_global_ctx_init(global_ctx, config->dict_dir)) {
+ dict_gctx = fr_dict_global_ctx_init(global_ctx, config->dict_dir);
+ if (!dict_gctx) {
fr_perror("%s", program);
EXIT_WITH_FAILURE;
}
*/
if (dl_modules) talloc_free(dl_modules);
+ /*
+ * Complain in debug builds about dictionaries
+ * that haven't been freed.
+ */
+ if (fr_dict_global_ctx_free(dict_gctx) < 0) {
+#ifndef NDEBUG
+ fr_perror("radiusd");
+ ret = EXIT_FAILURE;
+#endif
+ }
+
/*
* Cleanup everything else
*/
- talloc_free(global_ctx);
+ if (talloc_free(global_ctx) < 0) {
+#ifndef NDEBUG
+ fr_perror("radiusd");
+ ret = EXIT_FAILURE;
+#endif
+ }
/*
* Anything not cleaned up by the above is allocated in
*/
int main(int argc, char *argv[])
{
- fr_pcap_t *in = NULL, *in_p;
- fr_pcap_t **in_head = ∈
- fr_pcap_t *out = NULL;
+ fr_pcap_t *in = NULL, *in_p;
+ fr_pcap_t **in_head = ∈
+ fr_pcap_t *out = NULL;
- int ret = EXIT_SUCCESS; /* Exit status */
+ int ret = EXIT_SUCCESS; /* Exit status */
- char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */
- int port = FR_AUTH_UDP_PORT;
+ char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */
+ int port = FR_AUTH_UDP_PORT;
- int c;
- char const *raddb_dir = RADDBDIR;
- char const *dict_dir = DICTDIR;
- TALLOC_CTX *autofree;
+ int c;
+ char const *raddb_dir = RADDBDIR;
+ char const *dict_dir = DICTDIR;
+ TALLOC_CTX *autofree;
+ fr_dict_gctx_t const *dict_gctx = NULL;
- rs_stats_t *stats;
+ rs_stats_t *stats;
fr_debug_lvl = 1;
fr_log_fp = stdout;
conf->pcap_filter, conf->pcap_filter);
}
- if (!fr_dict_global_ctx_init(conf, dict_dir)) {
+ dict_gctx = fr_dict_global_ctx_init(conf, dict_dir);
+ if (!dict_gctx) {
fr_perror("radsniff");
fr_exit_now(EXIT_FAILURE);
}
fr_dict_autofree(radsniff_dict);
fr_radius_free();
+ if (fr_dict_global_ctx_free(dict_gctx) < 0) {
+ fr_perror("radsniff");
+ ret = EXIT_FAILURE;
+ }
+
return ret;
}
int zap = 0;
fr_dict_t *dict = NULL;
TALLOC_CTX *autofree;
+ fr_dict_gctx_t const *dict_gctx = NULL;
char const *p;
main_config_t *config;
+ int ret = EXIT_SUCCESS;
/*
* Must be called first, so the handler is called last
return 1;
}
- if (!fr_dict_global_ctx_init(autofree, config->dict_dir)) {
+ dict_gctx = fr_dict_global_ctx_init(autofree, config->dict_dir);
+ if (!dict_gctx) {
fr_perror("%s", main_config->name);
fr_exit_now(EXIT_FAILURE);
}
- if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("%s", config->name);
fr_exit_now(EXIT_FAILURE);
}
}
fclose(fp);
+ if (fr_dict_global_ctx_free(dict_gctx) < 0) {
+ fr_perror("radwho");
+ ret = EXIT_FAILURE;
+ }
main_config_free(&config);
- return 0;
+ return ret;
}
/*
* Decrease ref count if we're loading in a new dictionary
*/
- if (cc->tmpl_rules.dict_def) fr_dict_const_free(&cc->tmpl_rules.dict_def);
+ if (cc->tmpl_rules.dict_def) {
+ if (fr_dict_const_free(&cc->tmpl_rules.dict_def, __FILE__) < 0) return -1;
+ }
q = strchr(in, ' ');
if (q) {
dir = default_subdir;
}
- ret = fr_dict_protocol_afrom_file(&dict, name, dir);
+ ret = fr_dict_protocol_afrom_file(&dict, name, dir, __FILE__);
talloc_free(tmp);
if (ret < 0) RETURN_COMMAND_ERROR();
static int _command_ctx_free(command_file_ctx_t *cc)
{
- fr_dict_free(&cc->test_internal_dict);
+ if (fr_dict_free(&cc->test_internal_dict, __FILE__) < 0) {
+ fr_perror("unit_test_attribute");
+ return -1;
+ }
return 0;
}
}
fr_dict_global_ctx_set(cc->test_gctx);
- if (fr_dict_internal_afrom_file(&cc->test_internal_dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&cc->test_internal_dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("Failed loading test dict_gctx internal dictionary");
return NULL;
}
/*
* Free any residual resources we loaded.
*/
- if (cc) fr_dict_const_free(&cc->tmpl_rules.dict_def);
+ if (cc && (fr_dict_const_free(&cc->tmpl_rules.dict_def, __FILE__) < 0)) {
+ fr_perror("unit_test_attribute");
+ ret = -1;
+ }
+
fr_dict_global_ctx_set(config->dict_gctx); /* Switch back to the main dict ctx */
unload_proto_library();
talloc_free(cc);
EXIT_WITH_FAILURE;
}
- if (fr_dict_internal_afrom_file(&config.dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&config.dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("unit_test_attribute");
EXIT_WITH_FAILURE;
}
if (talloc_free(dl_loader) < 0) {
fr_perror("unit_test_attribute - dl_loader - "); /* Print free order issues */
}
- fr_dict_free(&config.dict);
+ if (fr_dict_free(&config.dict, __FILE__) < 0) {
+ fr_perror("unit_test_attribute");
+ ret = EXIT_FAILURE;
+ }
unlang_free_global();
*/
if (fr_dict_global_ctx_free(config.dict_gctx) < 0) {
fr_perror("unit_test_attribute"); /* Print free order issues */
+ ret = EXIT_FAILURE;
}
if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
* Explicitly free the autofree context
* to make errors less confusing.
*/
- talloc_free(autofree);
+ if (talloc_free(autofree) < 0) {
+ fr_perror("unit_test_attribute");
+ ret = EXIT_FAILURE;
+ }
return ret;
}
char const *receipt_file = NULL;
TALLOC_CTX *autofree;
+ fr_dict_gctx_t const *dict_gctx = NULL;
/*
* Must be called first, so the handler is called last
EXIT_WITH_FAILURE;
}
- if (!fr_dict_global_ctx_init(autofree, dict_dir)) {
+ dict_gctx = fr_dict_global_ctx_init(autofree, dict_dir);
+ if (!dict_gctx) {
fr_perror("unit_test_map");
EXIT_WITH_FAILURE;
}
- if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("unit_test_map");
EXIT_WITH_FAILURE;
}
/*
* Free any autoload dictionaries
*/
- fr_dict_autofree(unit_test_module_dict);
+ if (fr_dict_autofree(unit_test_module_dict) < 0) {
+ fr_perror("unit_test_map");
+ ret = EXIT_FAILURE;
+ }
+
+ if (fr_dict_free(&dict, __FILE__) < 0) {
+ fr_perror("unit_test_map");
+ ret = EXIT_FAILURE;
+ }
- fr_dict_free(&dict);
+ if (fr_dict_global_ctx_free(dict_gctx) < 0) {
+ fr_perror("unit_test_map");
+ ret = EXIT_FAILURE;
+ }
if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
fr_perror("unit_test_map");
const char *output_file = NULL;
const char *filter_file = NULL;
FILE *fp;
- request_t *request = NULL;
+ request_t *request = NULL;
fr_pair_t *vp;
fr_pair_list_t filter_vps;
bool xlat_only = false;
char const *receipt_file = NULL;
TALLOC_CTX *autofree;
+ fr_dict_gctx_t const *dict_gctx = NULL;
TALLOC_CTX *thread_ctx;
char *p;
EXIT_WITH_FAILURE;
}
- if (!fr_dict_global_ctx_init(autofree, config->dict_dir)) {
+ dict_gctx = fr_dict_global_ctx_init(autofree, config->dict_dir);
+ if (!dict_gctx) {
fr_perror("%s", config->name);
EXIT_WITH_FAILURE;
}
- if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("%s", config->name);
EXIT_WITH_FAILURE;
}
/*
* Free our explicitly loaded internal dictionary
*/
- fr_dict_free(&dict);
+ if (fr_dict_free(&dict, __FILE__) < 0) {
+ fr_perror("unit_test_module");
+ ret = EXIT_FAILURE;
+ }
if (dl_modules) talloc_free(dl_modules);
+ /*
+ * Free any openssl resources and the TLS dictionary
+ */
+ fr_openssl_free();
+
+ /*
+ * Free all the dictionaries, and complain/fail if
+ * they still have dependents.
+ */
+ if (fr_dict_global_ctx_free(dict_gctx) < 0) {
+ fr_perror("unit_test_module");
+ ret = EXIT_FAILURE;
+ }
+
if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
fr_perror("unit_test_module");
ret = EXIT_FAILURE;
}
+ if (talloc_free(autofree) < 0) {
+ fr_perror("unit_test_module");
+ ret = EXIT_FAILURE;
+ }
+
return ret;
}
*/
config->talloc_pool_size = 8 * 1024; /* default */
- if (fr_dict_internal_afrom_file(&config->dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&config->dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
PERROR("Failed reading internal dictionaries");
goto failure;
}
* Frees current config and any previous configs.
*/
TALLOC_FREE((*config)->root_cs);
- talloc_decrease_ref_count((*config)->dict);
+ fr_dict_free(&(*config)->dict, __FILE__);
TALLOC_FREE(*config);
return 0;
};
typedef struct {
- bool do_free;
- fr_dict_t const *dict;
+ fr_dict_t const *dict;
+ char const *server;
} virtual_server_dict_t;
/** Decrement references on dictionaries as the config sections are freed
*/
static int _virtual_server_dict_free(virtual_server_dict_t *cd)
{
- if (cd->do_free) {
- fr_dict_t *dict;
- memcpy(&dict, &cd->dict, sizeof(dict));
- fr_dict_free(&dict);
- }
+ fr_dict_const_free(&cd->dict, cd->server);
return 0;
}
-void virtual_server_dict_set(CONF_SECTION *server_cs, fr_dict_t const *dict, bool do_free)
+void virtual_server_dict_set(CONF_SECTION *server_cs, fr_dict_t const *dict, bool reference)
{
virtual_server_dict_t *p;
CONF_DATA const *cd;
return;
}
- fr_dict_reference(fr_dict_unconst(dict));
p = talloc_zero(server_cs, virtual_server_dict_t);
- p->do_free = do_free;
p->dict = dict;
+ p->server = talloc_strdup(p, cf_section_name2(server_cs));
talloc_set_destructor(p, _virtual_server_dict_free);
+ if (reference) fr_dict_dependent_add(fr_dict_unconst(dict), p->server);
+
cf_data_add(server_cs, p, "dictionary", true);
}
/*
* @todo - print out the entire error stack?
*/
- if (fr_dict_protocol_afrom_file(&dict, file, dir) < 0) {
+ if (fr_dict_protocol_afrom_file(&dict, file, dir, cf_section_name2(server_cs)) < 0) {
cf_log_perr(ci, "Failed loading namespace '%s'", namespace);
return -1;
}
set:
- virtual_server_dict_set(server_cs, dict, true);
+ virtual_server_dict_set(server_cs, dict, false);
module_name = talloc_strdup(ctx, namespace);
if (!set_dict) return 0;
app = (fr_app_t const *) module->common;
- if (app->dict) virtual_server_dict_set(server_cs, *app->dict, false);
+ if (app->dict) virtual_server_dict_set(server_cs, *app->dict, true);
return 0;
}
*
* @{
*/
-int fr_dict_internal_afrom_file(fr_dict_t **out, char const *internal_name);
+int fr_dict_internal_afrom_file(fr_dict_t **out, char const *internal_name,
+ char const *dependent);
-int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir);
+int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir,
+ char const *dependent);
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename);
/** @} */
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load);
-int fr_dict_autoload(fr_dict_autoload_t const *to_load);
+#define fr_dict_autoload(_to_load) _fr_dict_autoload(_to_load, __FILE__)
+int _fr_dict_autoload(fr_dict_autoload_t const *to_load, char const *dependent);
-void fr_dict_autofree(fr_dict_autoload_t const *to_free);
+#define fr_dict_autofree(_to_free) _fr_dict_autofree(_to_free, __FILE__)
+int _fr_dict_autofree(fr_dict_autoload_t const *to_free, char const *dependent);
int fr_dl_dict_enum_autoload(dl_t const *module, void *symbol, void *user_ctx);
*/
fr_dict_t *fr_dict_alloc(char const *proto_name, unsigned int proto_number) CC_HINT(nonnull);
-void fr_dict_reference(fr_dict_t *dict);
+void fr_dict_dependent_add(fr_dict_t *dict, char const *dependent) CC_HINT(nonnull);
-int fr_dict_free(fr_dict_t **dict);
+int fr_dict_free(fr_dict_t **dict, char const *dependent) CC_HINT(nonnull);
-int fr_dict_const_free(fr_dict_t const **dict);
+int fr_dict_const_free(fr_dict_t const **dict, char const *dependent) CC_HINT(nonnull);
/** @} */
/** @name Global dictionary management
if (p) *p = '\0';
- if (fr_dict_protocol_afrom_file(&other, ref, NULL) < 0) {
+ if (fr_dict_protocol_afrom_file(&other, ref, NULL, filename) < 0) {
return NULL;
}
} \
} while (0)
+/** Entry recording dictionary reference holders by file
+ */
+typedef struct {
+ fr_rb_node_t node;
+ int count; //!< How many references are held by this file.
+ ///< Signed to help figure out when things go wrong...
+ char const *dependent; //!< File holding the reference.
+} fr_dict_dependent_t;
+
/** Vendors and attribute names
*
* It's very likely that the same vendors will operate in multiple
fr_dict_attr_valid_func_t attr_valid; //!< validation function for new attributes
fr_dict_attr_t **fixups; //!< Attributes that need fixing up.
+
+ rbtree_t *dependents; //!< Which files are using this dictionary.
};
struct fr_dict_gctx_s {
extern fr_table_num_ordered_t const date_precision_table[];
extern size_t date_precision_table_len;
+bool dict_has_dependents(fr_dict_t *dict);
+
+int dict_dependent_add(fr_dict_t *dict, char const *dependent);
+
+int dict_dependent_remove(fr_dict_t *dict, char const *dependent);
+
fr_dict_t *dict_alloc(TALLOC_CTX *ctx);
int dict_dlopen(fr_dict_t *dict, char const *name);
return 0;
}
- dict = dict_alloc(NULL);
+ dict = dict_alloc(dict_gctx);
/*
* Try to load protocol-specific validation routines.
*
* @param[out] out Where to write pointer to the internal dictionary.
* @param[in] dict_subdir name of the internal dictionary dir (may be NULL).
+ * @param[in] dependent Either C src file, or another dictionary.
* @return
* - 0 on success.
* - -1 on failure.
*/
-int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir)
+int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent)
{
fr_dict_t *dict;
char *dict_path = NULL;
* Increase the reference count of the internal dictionary.
*/
if (dict_gctx->internal) {
- talloc_increase_ref_count(dict_gctx->internal);
+ dict_dependent_add(dict_gctx->internal, dependent);
*out = dict_gctx->internal;
return 0;
}
talloc_free(dict_path);
+ dict_dependent_add(dict, dependent);
+
+ if (!dict_gctx->internal) {
+ dict_gctx->internal = dict;
+ dict_dependent_add(dict, "global");
+ }
+
*out = dict;
- if (!dict_gctx->internal) dict_gctx->internal = dict;
return 0;
}
* dictionary if files have changed and *out is not NULL.
* @param[in] proto_name that we're loading the dictionary for.
* @param[in] proto_dir Explicitly set where to hunt for the dictionary files. May be NULL.
+ * @param[in] dependent Either C src file, or another dictionary.
* @return
* - 0 on success.
* - -1 on failure.
*/
-int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir)
+int fr_dict_protocol_afrom_file(fr_dict_t **out, char const *proto_name, char const *proto_dir, char const *dependent)
{
char *dict_dir = NULL;
fr_dict_t *dict;
*/
dict = dict_by_protocol_name(proto_name);
if (dict && dict->autoloaded) {
- talloc_increase_ref_count(dict);
+ dict_dependent_add(dict, dependent);
*out = dict;
return 0;
}
* If we're autoloading a previously defined dictionary,
* then mark up the dictionary as now autoloaded.
*/
- if (!dict->autoloaded) {
-// talloc_increase_ref_count(dict);
- dict->autoloaded = true;
- }
+ if (!dict->autoloaded) dict->autoloaded = true;
+
+ dict_dependent_add(dict, dependent);
*out = dict;
}
dict->in_protocol_by_num = true;
+ dict_dependent_add(dict, "global");
+
return 0;
}
return 0;
}
-static int _dict_free_autoref(void *data, UNUSED void *uctx)
+static int _dict_free_autoref(void *data, void *uctx)
{
+ fr_dict_t *referer = talloc_get_type_abort(uctx, fr_dict_t);
fr_dict_t *dict = talloc_get_type_abort(data, fr_dict_t);
- (void)fr_dict_free(&dict);
+ if (fr_dict_free(&dict, referer->root->name) < 0) return -1;
return 0;
}
+/** Find a dependent in the tree of dependents
+ *
+ */
+static int _dict_dependent_cmp(void const *a, void const *b)
+{
+ fr_dict_dependent_t const *dep_a = a;
+ fr_dict_dependent_t const *dep_b = b;
+
+ return strcmp(dep_a->dependent, dep_b->dependent);
+}
+
+/** Record a new dependency on a dictionary
+ *
+ * These are used to determine what is currently depending on a dictionary.
+ *
+ * @param[in] dict to record dependency on.
+ * @param[in] dependent Either C src file, or another dictionary.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int dict_dependent_add(fr_dict_t *dict, char const *dependent)
+{
+ fr_dict_dependent_t *found;
+
+ found = rbtree_find(dict->dependents, &(fr_dict_dependent_t){ .dependent = dependent } );
+ if (!found) {
+ fr_dict_dependent_t *new;
+
+ new = talloc_zero(dict->dependents, fr_dict_dependent_t);
+ if (unlikely(!new)) return -1;
+
+ /*
+ * If the dependent is in a module that gets
+ * unloaded, any strings in the text area also
+ * get unloaded (including dependent locations).
+ *
+ * Strdup the string here so we don't get
+ * random segfaults if a module forgets to unload
+ * a dictionary.
+ */
+ new->dependent = talloc_typed_strdup(new, dependent);
+ rbtree_insert(dict->dependents, new);
+
+ new->count = 1;
+
+ return 0;
+ }
+
+ found->count++; /* Increase ref count */
+
+ return 0;
+}
+
+/** Decrement ref count for a dependent in a dictionary
+ *
+ * @param[in] dict to remove dependency from.
+ * @param[in] dependent Either C src, or another dictionary dependent.
+ * What depends on this dictionary.
+ */
+int dict_dependent_remove(fr_dict_t *dict, char const *dependent)
+{
+ fr_dict_dependent_t *found;
+
+ found = rbtree_find(dict->dependents, &(fr_dict_dependent_t){ .dependent = dependent } );
+ if (!found) {
+ fr_strerror_printf("Dependent \"%s\" not found in dictionary \"%s\"", dependent, dict->root->name);
+ return -1;
+ }
+
+ if (found->count == 0) {
+ fr_strerror_printf("Zero ref count invalid for dependent \"%s\", dictionary \"%s\"",
+ dependent, dict->root->name);
+ return -1;
+ }
+
+ if (--found->count == 0) {
+ rbtree_delete(dict->dependents, found);
+ talloc_free(found);
+ return 0;
+ }
+
+ return 1;
+}
+
+/** Check if a dictionary still has dependents
+ *
+ * @param[in] dict to check
+ * @return
+ * - true if there's still at least one dependent.
+ * - false if there are no dependents.
+ */
+bool dict_has_dependents(fr_dict_t *dict)
+{
+ return (rbtree_num_elements(dict->dependents) > 0);
+}
+
static int _dict_free(fr_dict_t *dict)
{
if (!fr_cond_assert(!dict->in_protocol_by_name || fr_hash_table_delete(dict->gctx->protocol_by_name, dict))) {
fr_strerror_printf("Failed removing dictionary from protocol hash \"%s\"", dict->root->name);
return -1;
}
+ dict->in_protocol_by_name = false;
+
if (!fr_cond_assert(!dict->in_protocol_by_num || fr_hash_table_delete(dict->gctx->protocol_by_num, dict))) {
fr_strerror_printf("Failed removing dictionary from protocol number_hash \"%s\"", dict->root->name);
return -1;
}
+ dict->in_protocol_by_num = false;
+
+ if (dict_has_dependents(dict)) {
+ fr_rb_tree_iter_inorder_t iter;
+ fr_dict_dependent_t *dep;
+
+ fr_strerror_printf("Refusing to free dictionary \"%s\", still has dependents", dict->root->name);
+
+ for (dep = rbtree_iter_init_inorder(&iter, dict->dependents);
+ dep;
+ dep = rbtree_iter_next_inorder(&iter)) {
+ fr_strerror_printf_push("%s (%u)", dep->dependent, dep->count);
+ }
+ return -1;
+ }
/*
* Free the hash tables with free functions first
talloc_free(dict->vendors_by_name);
if (dict->autoref &&
- (fr_hash_table_walk(dict->autoref, _dict_free_autoref, NULL) < 0)) {
+ (fr_hash_table_walk(dict->autoref, _dict_free_autoref, dict) < 0)) {
return -1;
}
goto error;
}
+ /*
+ * Who/what depends on this dictionary
+ */
+ dict->dependents = rbtree_alloc(dict, fr_dict_dependent_t, node, _dict_dependent_cmp, NULL, 0);
+
/*
* Set default type size and length.
*/
*
* @param[in] dict to increase the reference count for.
*/
-void fr_dict_reference(fr_dict_t *dict)
+void fr_dict_dependent_add(fr_dict_t *dict, char const *dependent)
{
- talloc_increase_ref_count(dict);
+ dict_dependent_add(dict, dependent);
}
/** Decrement the reference count on a previously loaded dictionary
*
* @param[in] dict to free.
- * @return how many references to the dictionary remain.
+ * @param[in] dependent that originally allocated this dictionary.
+ * @return
+ * - 0 on success (dictionary freed).
+ * - 1 if other things still depend on the dictionary.
+ * - -1 on error (dependent doesn't exist)
*/
-int fr_dict_free(fr_dict_t **dict)
+int fr_dict_const_free(fr_dict_t const **dict, char const *dependent)
{
- int ret;
+ fr_dict_t **our_dict = UNCONST(fr_dict_t **, dict);
- if (!*dict) return 0;
+ if (!*our_dict) return 0;
- ret = talloc_decrease_ref_count(*dict);
- *dict = NULL;
+ switch (dict_dependent_remove(*our_dict, dependent)) {
+ case 0: /* dependent has no more refs */
+ if (!dict_has_dependents(*our_dict)) {
+ talloc_free(*our_dict);
+ return 0;
+ }
+ FALL_THROUGH;
- return ret;
+ case 1: /* dependent has more refs */
+ return 1;
+
+ default: /* error */
+ return -1;
+ }
}
/** Decrement the reference count on a previously loaded dictionary
*
* @param[in] dict to free.
- * @return how many references to the dictionary remain.
+ * @param[in] dependent that originally allocated this dictionary.
+ * @return
+ * - 0 on success (dictionary freed).
+ * - 1 if other things still depend on the dictionary.
+ * - -1 on error (dependent doesn't exist)
*/
-int fr_dict_const_free(fr_dict_t const **dict)
+int fr_dict_free(fr_dict_t **dict, char const *dependent)
{
- int ret;
- fr_dict_t *our_dict;
-
if (!*dict) return 0;
- memcpy(&our_dict, dict, sizeof(our_dict));
+ switch (dict_dependent_remove(*dict, dependent)) {
+ case 0: /* dependent has no more refs */
+ if (!dict_has_dependents(*dict)) {
+ talloc_free(*dict);
+ return 0;
+ }
+ FALL_THROUGH;
- ret = talloc_decrease_ref_count(our_dict);
- *dict = NULL;
+ case 1: /* dependent has more refs */
+ return 1;
- return ret;
+ default: /* error */
+ return -1;
+ }
}
/** Process a dict_attr_autoload element to load/verify a dictionary attribute
* - 0 on success.
* - -1 on failure.
*/
-int fr_dict_autoload(fr_dict_autoload_t const *to_load)
+int _fr_dict_autoload(fr_dict_autoload_t const *to_load, char const *dependent)
{
fr_dict_autoload_t const *p;
* Load the internal dictionary
*/
if (strcmp(p->proto, "freeradius") == 0) {
- if (fr_dict_internal_afrom_file(&dict, p->proto) < 0) return -1;
+ if (fr_dict_internal_afrom_file(&dict, p->proto, dependent) < 0) return -1;
} else {
- if (fr_dict_protocol_afrom_file(&dict, p->proto, p->base_dir) < 0) return -1;
+ if (fr_dict_protocol_afrom_file(&dict, p->proto, p->base_dir, dependent) < 0) return -1;
}
*(p->out) = dict;
*
* @param[in] to_free previously loaded dictionary to free.
*/
-void fr_dict_autofree(fr_dict_autoload_t const *to_free)
+int _fr_dict_autofree(fr_dict_autoload_t const *to_free, char const *dependent)
{
- fr_dict_t **dict;
- fr_dict_autoload_t const *p;
+ fr_dict_autoload_t const *p;
for (p = to_free; p->out; p++) {
- memcpy(&dict, &p->out, sizeof(dict)); /* const issues */
- if (!*dict) continue;
+ int ret;
- if (fr_dict_free(dict) == 0) *dict = NULL;
+ if (!*p->out) continue;
+ ret = fr_dict_const_free(p->out, dependent);
+
+ if (ret == 0) *p->out = NULL;
+ if (ret < 0) return -1;
}
+
+ return 0;
}
/** Callback to automatically resolve enum values
bool still_loaded = false;
if (gctx->internal) {
- fr_strerror_const("Refusing to free dict gctx. Internal dictionary is still loaded");
- still_loaded = true;
+ dict_dependent_remove(gctx->internal, "global"); /* remove our dependency */
+
+ if (talloc_free(gctx->internal) < 0) still_loaded = true;
}
for (dict = fr_hash_table_iter_init(gctx->protocol_by_name, &iter);
dict;
dict = fr_hash_table_iter_next(gctx->protocol_by_name, &iter)) {
(void)talloc_get_type_abort(dict, fr_dict_t);
- fr_strerror_printf_push("Refusing to free dict gctx. %s protocol dictionary is still loaded",
- dict->root->name);
- still_loaded = true;
+ dict_dependent_remove(dict, "global"); /* remove our dependency */
+
+ if (talloc_free(dict) < 0) still_loaded = true;
}
if (still_loaded) return -1;
*/
void fr_dict_global_ctx_debug(void)
{
- fr_hash_iter_t iter;
- fr_dict_t *dict;
+ fr_hash_iter_t dict_iter;
+ fr_dict_t *dict;
+ fr_rb_tree_iter_inorder_t dep_iter;
+ fr_dict_dependent_t *dep;
if (!dict_gctx) {
FR_FAULT_LOG("gctx not initialised");
}
FR_FAULT_LOG("gctx %p report", dict_gctx);
- for (dict = fr_hash_table_iter_init(dict_gctx->protocol_by_num, &iter);
+ for (dict = fr_hash_table_iter_init(dict_gctx->protocol_by_num, &dict_iter);
dict;
- dict = fr_hash_table_iter_next(dict_gctx->protocol_by_num, &iter)) {
- FR_FAULT_LOG("\t%s refs %zu", dict->root->name, talloc_reference_count(dict));
+ dict = fr_hash_table_iter_next(dict_gctx->protocol_by_num, &dict_iter)) {
+ for (dep = rbtree_iter_init_inorder(&dep_iter, dict->dependents);
+ dep;
+ dep = rbtree_iter_next_inorder(&dep_iter)) {
+ FR_FAULT_LOG("\t%s refs %s (%u)", dict->root->name, dep->dependent, dep->count);
+ }
}
- if (dict_gctx->internal) FR_FAULT_LOG("\t%s refs %zu", dict_gctx->internal->root->name,
- talloc_reference_count(dict_gctx->internal));
+ if (dict_gctx->internal) {
+ for (dep = rbtree_iter_init_inorder(&dep_iter, dict_gctx->internal->dependents);
+ dep;
+ dep = rbtree_iter_next_inorder(&dep_iter)) {
+ FR_FAULT_LOG("\t%s refs %s (%u)", dict_gctx->internal->root->name, dep->dependent, dep->count);
+ }
+ }
}
/** Coerce to non-const
if (!fr_dict_global_ctx_init(autofree, dict_dir)) goto error;
- if (fr_dict_internal_afrom_file(&dict_internal, FR_DICTIONARY_INTERNAL_DIR) < 0) goto error;
+ if (fr_dict_internal_afrom_file(&dict_internal, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) goto error;
/*
* Set the root name of the dictionary
int main(int argc, char **argv)
{
- int c;
- bool quiet = false;
- char *line = NULL;
- ssize_t result;
- char const *file = NULL;
- char const *name = "radiusd";
- char const *input_file = NULL;
- FILE *inputfp = stdin;
- char const *server = NULL;
- fr_dict_t *dict = NULL;
-
- char const *raddb_dir = RADIUS_DIR;
- char const *dict_dir = DICTDIR;
+ int c;
+ bool quiet = false;
+ char *line = NULL;
+ ssize_t result;
+ char const *file = NULL;
+ char const *name = "radiusd";
+ char const *input_file = NULL;
+ FILE *inputfp = stdin;
+ char const *server = NULL;
+ fr_dict_t *dict = NULL;
+
+ char const *raddb_dir = RADIUS_DIR;
+ char const *dict_dir = DICTDIR;
#ifdef USE_READLINE_HISTORY
- char history_file[PATH_MAX];
+ char history_file[PATH_MAX];
#endif
- TALLOC_CTX *autofree;
+ TALLOC_CTX *autofree;
+ fr_dict_gctx_t const *dict_gctx = NULL;
char *commands[MAX_COMMANDS];
int num_commands = -1;
* Need to read in the dictionaries, else we may get
* validation errors when we try and parse the config.
*/
- if (!fr_dict_global_ctx_init(autofree, dict_dir)) {
+ dict_gctx = fr_dict_global_ctx_init(autofree, dict_dir);
+ if (!dict_gctx) {
fr_perror("radmin");
fr_exit_now(64);
}
- if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR) < 0) {
+ if (fr_dict_internal_afrom_file(&dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
fr_perror("radmin");
fr_exit_now(64);
}
}
exit:
+ fr_dict_free(&dict, __FILE__);
+
+ if (fr_dict_global_ctx_free(dict_gctx) < 0) fr_perror("radmin");
+
if (inputfp != stdin) fclose(inputfp);
if (radmin_log.dst == L_DST_FILES) close(radmin_log.fd);
fr_exit_now(EXIT_FAILURE);
}
- if (fr_dict_internal_afrom_file(&conf->dict, FR_DICTIONARY_FILE) < 0) {
+ if (fr_dict_internal_afrom_file(&conf->dict, FR_DICTIONARY_FILE, __FILE__) < 0) {
fr_perror("sync_touch");
fr_exit_now(EXIT_FAILURE);
}