R_CONFIG(3)

πŸ“¦
version 6.2.2 Β· 2 versions
  • 6.2.2 Β· alpine 3.24, arch rolling, nixos 26.05, opensuse-leap 16.0, ubuntu resolute, ubuntu stonking (latest)
  • 6.2.4 Β· nixpkgs unstable
, in alpine 3.24, arch rolling, nixos 26.05, opensuse-leap 16.0, ubuntu resolute, ubuntu stonking

πŸ’– 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, use r_config_node_new for 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_lock sorts nodes and flips a lock flag. Super useful before serializing a stable snapshot, iykyk.
  • r_config_eval understands key=value to set keys, key. to list matching keys, and colon-separated lists when many is true. This mirrors how CLI handlers parse compact config expressions β€” it's giving parser energy.
  • r_config_hold_new + r_config_hold capture current values for a list of keys, and r_config_hold_restore restores 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