R_CONFIG(3)
π NAME π
r_config β the configuration management library for radare2, bestie. It's the whole vibe of "what are our settings rn" β¨
πΈ SYNOPSIS πΈ
#include <r_config.h>
π DESCRIPTION π
Okay so the r_config API is literally the configuration registry that radare2 and all its subcommands run on. It's the keeper of:
- named configuration keys (strings, integers and booleans, the whole trio π )
- metadata β that's the description, the options, and the read-only flags
- setters/getters so that components can react when config changes happen, which is the icon behaviour fr fr
What do people actually use it for? So many things, no cap:
- global defaults like architecture and bits
- binding config keys straight to internal variables
- temporary overrides for one single operation
- serializing configuration into project files so it survives
The main character container here is struct r_config_t π β it keeps a list and a hash table of struct r_config_node_t. Each one of those nodes is holding: the key name, the value, the type flags, optional callbacks, and a human-readable description. That's it, that's the node.
π¦ INITIALIZATION π¦
This is where you create and destroy configuration objects. An RConfig is usually attached to a higher-level context (for example an RCore instance keeps one RConfig around, they're basically a couple π«Ά). And cloning? Cloning produces a shallow copy of nodes, which is perfect for temporary manipulation.
/* prototypes */
RConfig *r_config_new(void *user);
RConfig *r_config_clone(RConfig *cfg);
void r_config_free(RConfig *cfg);
RConfig *cfg = r_config_new (core);
/* ... use cfg ... */
r_config_free (cfg);
β¨ SETTING VALUES β¨
Setters update nodes or create them if they're not there yet, and they may call a node setter callback. If you want a setter that propagates the change into internal state, use the _cb variants or the r_config_set_setter* helpers (peep libr/config/callback.c for that one π).
/* prototypes */
RConfigNode *r_config_set(RConfig *cfg, const char *name, const char *value);
RConfigNode *r_config_set_i(RConfig *cfg, const char *name, const ut64 i);
RConfigNode *r_config_set_b(RConfig *cfg, const char *name, bool b);
RConfigNode *r_config_set_cb(RConfig *cfg, const char *name, const char *value, RConfigCallback cb);
RConfigNode *r_config_set_i_cb(RConfig *cfg, const char *name, int ivalue, RConfigCallback cb);
RConfigNode *r_config_set_b_cb(RConfig *cfg, const char *name, bool bvalue, RConfigCallback cb);
bool r_config_set_setter(RConfig *cfg, const char *key, RConfigCallback cb);
Notes, and these ones actually matter bestie:
- When a node has a setter, it is invoked after the new value is parsed. And if that setter returns
false? The previous value gets restored. Like nothing ever happened π - The
r_config_set*helpers create nodes if they do not exist. But if you need to populate options or a long description first, user_config_node_newfor manual node construction.
/* example: CLI-style configuration used in many binaries */
r_config_set (cfg, "asm.arch", "x86");
r_config_set_i (cfg, "asm.bits", 64);
r_config_set_b (cfg, "scr.interactive", false);
π₯ GETTING VALUES π₯
Accessors hand you the current effective value for a key. And if a node has an attached getter callback, it may compute values on demand β on-demand computation is so main character.
/* prototypes */
const char *r_config_get(RConfig *cfg, const char *name);
ut64 r_config_get_i(RConfig *cfg, const char *name);
bool r_config_get_b(RConfig *cfg, const char *name);
bool r_config_toggle(RConfig *cfg, const char *name);
bool r_config_rm(RConfig *cfg, const char *name);
const char *arch = r_config_get (cfg, "asm.arch");
ut64 bits = r_config_get_i (cfg, "asm.bits");
bool pseudo = r_config_get_b (cfg, "asm.pseudo");
r_config_toggle (cfg, "asm.pseudo");
π ADVANCED FEATURES π
This section is the higher-level utilities used in real radare2 code: locks, short expression evaluation (the CLI option parsers are obsessed with this), bulk listing, and the hold/restore pattern that captures values so you can bring them back later π
/* prototypes */
void r_config_lock(RConfig *cfg, bool lock);
char *r_config_eval(RConfig *cfg, const char *str, bool many, bool *error);
RConfigHold *r_config_hold_new(RConfig *cfg);
bool r_config_hold(RConfigHold *h, ...);
void r_config_hold_restore(RConfigHold *h);
void r_config_hold_free(RConfigHold *h);
void r_config_bump(RConfig *cfg, const char *key);
char *r_config_list(RConfig *cfg, const char *str, int rad);
Behavior and tips, gather round:
r_config_locksorts nodes and flips a lock flag. Super useful before serializing a stable snapshot, iykyk.r_config_evalunderstandskey=valueto set keys,key.to list matching keys, and colon-separated lists whenmanyis true. This mirrors how CLI handlers parse compact config expressions β it's giving parser energy.r_config_hold_new+r_config_holdcapture current values for a list of keys, andr_config_hold_restorerestores them. This mirrors patterns used when temporarily switching architectures or analysis depth.
/* hold/restore example (used in several core components) */
RConfigHold *hold = r_config_hold_new (cfg);
r_config_hold (hold, "asm.arch", "asm.bits", NULL);
/* temporarily override */
r_config_set (cfg, "asm.arch", "arm");
r_config_set_i (cfg, "asm.bits", 32);
/* restore original */
r_config_hold_restore (hold);
r_config_hold_free (hold);
π§ NODE MANAGEMENT π§
Direct node manipulation is the move when you're registering a config key with a long description, a whole collection of valid options, or custom setter/getter callbacks. Most callers just use the r_config_set* helpers, which create nodes implicitly β but use the node API when you want more control π
/* prototypes */
RConfigNode *r_config_node_new(const char *name, const char *value);
RConfigNode *r_config_node_get(RConfig *cfg, const char *name);
RConfigNode *r_config_node_desc(RConfigNode *node, const char *desc);
void r_config_node_add_option(RConfigNode *node, const char *option);
void r_config_node_purge_options(RConfigNode *node);
char *r_config_node_tostring(RConfigNode *node);
void r_config_node_free(void *n);
/* create a node with options and description */
RConfigNode *node = r_config_node_new ("custom.var", "default");
r_config_node_desc (node, "Description for UI and help output");
r_config_node_add_option (node, "opt1");
r_config_node_add_option (node, "opt2");
/* attach node into cfg (internal helpers in libr/config do this) */
r_config_node_free (node);
π SERIALIZATION π
Serialize and unserialize your configuration into an Sdb database. These routines are what save/load projects use, so your configuration survives between sessions β literally the "come back to me" of settings π₯Ή
/* prototypes */
void r_config_serialize(RConfig * R_NONNULL config, Sdb * R_NONNULL db);
bool r_config_unserialize(RConfig * R_NONNULL config, Sdb * R_NONNULL db, char ** R_NULLABLE err);
Sdb *db = sdb_new0 ();
r_config_serialize (cfg, db);
/* write sdb to disk via sdb API */
sdb_free (db);
π EXAMPLES π
These examples demonstrate patterns taken from real radare2 code: binding config keys to variables, temporary overrides, and evaluating compact config expressions. Ate, honestly.
#include <r_config.h>
int main(void) {
RConfig *cfg = r_config_new (NULL);
/* common set/get pattern used in many binaries */
r_config_set (cfg, "asm.arch", "x86");
r_config_set_i (cfg, "asm.bits", 64);
printf ("Arch: %s0, r_config_get (cfg, "asm.arch"));
/* bind a config key to a C variable using setter helpers (see
* libr/config/callback.c for the implementation used in the tree): */
int term_color = 0;
r_config_set_setter_i (cfg, "scr.color", &term_color);
r_config_set_i (cfg, "scr.color", 2); /* updates term_color */
/* temporarily override and restore: */
RConfigHold *hold = r_config_hold_new (cfg);
r_config_hold (hold, "asm.arch", "asm.bits", NULL);
r_config_set (cfg, "asm.arch", "arm");
r_config_set_i (cfg, "asm.bits", 32);
r_config_hold_restore (hold);
r_config_hold_free (hold);
/* evaluate a compact CLI expression (used in CLI parsing code): */
char *res = r_config_eval (cfg, "asm.arch=arm:asm.bits=32", true, NULL);
free (res);
r_config_free (cfg);
return 0;
}
β SEE ALSO β
And that's the whole page, bestie π«Ά This one's from the Debian edition of the manual, dated September 21, 2025, filed under R_CONFIG(3). Go forth and configure responsibly π
π¦ where this page comes from
- alpine 3.24: radare2-doc 6.1.6-r0 Β· GPL-3.0-or-later AND LGPL-2.0-or-later AND LGPL-2.1-or-later
- arch rolling: radare2 6.2.2-1 Β· GPL-3.0-only AND LGPL-3.0-only
- nixos 26.05: radare2 6.1.8
- opensuse-leap 16.0: radare2 6.2.2-bp160.1.1 Β· GPL-3.0-only AND LGPL-3.0-only
- opensuse-leap 16.0: radare2 6.2.2-bp160.1.1 Β· GPL-3.0-only AND LGPL-3.0-only
- opensuse-leap 16.0: radare2 6.2.2-bp160.1.1 Β· GPL-3.0-only AND LGPL-3.0-only
- opensuse-leap 16.0: radare2 6.2.2-bp160.1.1 Β· GPL-3.0-only AND LGPL-3.0-only
- ubuntu resolute: radare2 6.0.7+ds-1 Β· LGPL-3.0-only
- ubuntu stonking: radare2 6.1.8+ds-1 Β· LGPL-3.0-only
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms