return NULL;
}
- /*
- * If we call init(), make sure that we call free();
- */
- if (dict->proto && dict->proto->init) {
- if (dict->proto->init() < 0) return NULL;
-
- /*
- * Mark the *referencing* dictionary as autofree.
- *
- * Changing this to dict->autofree=true will break things. :(
- */
- (*dict_def)->autofree = true;
- }
-
/*
* The reference is to the root of the foreign protocol, we're done.
*/
bool in_protocol_by_num; //!< Whether the dictionary has been inserted into the
//!< protocol_by_num table.
- bool autoloaded; //!< manual vs autoload
-
bool string_based; //!< TACACS, etc.
- bool autofree; //!< from dict_fixup.
+ bool loading; //!< from fr_dict_protocol_afrom_file();
+
+ bool loaded; //!< from fr_dict_protocol_afrom_file();
fr_hash_table_t *vendors_by_name; //!< Lookup vendor by name.
fr_hash_table_t *vendors_by_num; //!< Lookup vendor by PEN.
*/
dict_fixup_init(NULL, &ctx->fixup);
- // check if there's a linked library for the
- // protocol. The values can be unknown (we
- // try to load one), or non-existent, or
- // known. For the last two, we don't try to
- // load anything.
+ /*
+ * We're in the middle of loading this dictionary. Tell
+ * fr_dict_protocol_afrom_file() to suppress recursive references.
+ */
+ found->loading = true;
ctx->dict = found;
* has already been loaded and return that.
*/
dict = dict_by_protocol_name(proto_name);
- if (dict && dict->autoloaded) {
- dict_dependent_add(dict, dependent);
- *out = dict;
- return 0;
+ if (dict) {
+ /*
+ * If we're in the middle of loading this dictionary, then the only way we get back here
+ * is via a circular reference. So we catch that, and drop the circular dependency.
+ *
+ * When we have A->B->A, it means that we don't need to track B->A, because we track
+ * A->B. And if A is freed, then B is freed.
+ */
+ if (!dict->loading) dict_dependent_add(dict, dependent);
+
+ /*
+ * But we only return a pre-existing dict if _this function_ has loaded it.
+ */
+ if (dict->loaded) {
+ *out = dict;
+ return 0;
+ }
+
+ /*
+ * Set the flag to true _before_ loading the file. That prevents recursion.
+ */
+ dict->loaded = true;
}
if (!proto_dir) {
*/
if (dict_from_file(dict_gctx->internal, dict_dir, FR_DICTIONARY_FILE, NULL, 0) < 0) {
error:
+ dict->loading = false;
talloc_free(dict_dir);
return -1;
}
goto error;
}
- talloc_free(dict_dir);
-
/*
- * If we're autoloading a previously defined dictionary,
- * then mark up the dictionary as now autoloaded.
+ * Initialize the library.
*/
- if (!dict->autoloaded) dict->autoloaded = true;
+ if (dict->proto && dict->proto->init) {
+ if (dict->proto->init() < 0) goto error;
+ }
+ dict->loading = false;
+
+ talloc_free(dict_dir);
dict_dependent_add(dict, dependent);