config.md (5400B)
@page configuration Configuration options and persistent state
@tableofcontents
Introduction
Tor uses a shared, table-driven mechanism to handle its configuration (torrc) files and its state files. Each module can declare a set of named fields for these files, and get notified whenever the configuration changes, or when the state is about to be flushed to disk.
Declaring options
Most modules will only need to use the macros in confdecl.h to declare a configuration or state structure.
You'll write something like this:
// mymoduleconfig.inc BEGINCONFSTRUCT(moduleoptionst) CONF_VAR(FieldOne, INT, 0, "7") CONF_VAR(FieldTwo, STRING, 0, NULL) ENDCONFSTRUCT(moduleoptionst)
The above example will result in a structure called moduleconfigt with two fields: one an integer called FieldOne and one a string called FieldTwo. The integer gets a default value of 7; the string's default value is NULL.
After making a definition file like that, you include it twice: once in a header, after saying \#define CONF_CONTEXT STRUCT, and once in a C file, after saying \#define CONF_CONTEXT TABLE. The first time defines a moduleoptionst structure, and the second time defines a table that tells the configuration manager how to use it.
Using the table, you declare a const configformatt, which
associates the fields with a set of functions for validating and
normalizing them, a list of abbreviations and deprecations, and
other features.
See confdecl.h and conftypes.h for more information. For example usage, see cryptooptions.inc or mainloopstate.inc.
Getting notifications
After using those macros, you must tell the subsystem management code about your module's configuration/state.
If you're writing configuration code, you'll need a function that receives the configuration object, and acts upon it. This function needs to be safe to call multiple times, since Tor will reconfigure its subsystems whenever it re-reads the torrc, gets a configuration change from a controller, or restarts in process. This function goes in your subsystem's subsysfnst.set_options field.
If you're writing state code, you'll need a function that receives
state (subsysfnst.set_state), and a function that flushes the
application state into a state object (subsysfnst.flush_state).
The set_state function will be called once (@ref configonceper
"1") when Tor is starting, whereas the flush_state function will
be called whenever Tor is about to save the state to disk.
See subsysfnst for more information here, and \ref initialization for more information about initialization and subsystems in general.
@anchor config_once_per 1. Technically, state is set once _per startup_.
Remember that Tor can be stopped and started multiple times in
the same process. If this happens, then your set_state() function
is called once every time Tor starts.
How it works
The common logic used to handle configuration and state files lives in @refdir{lib/confmgt}. At the highest level, a configuration manager object (configmgrt) maintains a list of each module's configuration objects, and a list of all their fields. When the user specifies a configuration value, the manager finds out how to parse it, where to store it, and which configuration object is affected.
The top-level configuration module (config.c) and state module (statefile.c) use configmgrt to create, initialize, set, compare, and free a "top level configuration object". This object contains a list of sub-objects: one for each module that participates in the configuration/state system. This top-level code then invokes the subsystem manager code (subsysmgr.c) to pass the corresponding configuration or state objects to each module that has one.
Note that the top level code does not have easy access to the configuration objects used by the sub-modules. This is by design. A module may expose some or all of its configuration or state object via accessor functions, if it likes, but if it does not, that object should be considered module-local.
Adding new types
Configuration and state fields each have a "type". These types specify how the fields' values are represented in C; how they are stored in files; and how they are encoded back and forth.
There is a set of built-in types listed in conftypes.h, but higher-level code can define its own types. To do so, you make an instance of vartypefns_t that describes how to manage your type, and an instance of vartypedeft that wraps your vartypefnst with a name and optional parameters and flags.
For an example of how a higher-level type is defined, see
ROUTERSETtypedefn in routerset.c. Also see the typedef
config_decl_ROUTERSET. Together, these let the routerset type be
used with the macros in confdecl.h.
Legacy configuration and state
As of this writing (November 2019), most of the configuration and state is still handled directly in config.c and statefile.c, and stored in the monolithic structures oroptionst and orstatet respectively.
These top-level structures are accessed with get_options() and get_state(), and used throughout much of the code, at the level of @refdir{core} and higher.
With time we hope to refactor this configuration into more reasonable pieces, so that they are no longer (effectively) global variables used throughout the code.