Bump copyright date to 2019
[tor.git] / src / app / config / statefile.c
blob9681f6f8b3e50b2161b92473ec58fcff8a5396ba
1 /* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2019, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
7 /**
8 * \file statefile.c
10 * \brief Handles parsing and encoding the persistent 'state' file that carries
11 * miscellaneous persistent state between Tor invocations.
13 * This 'state' file is a typed key-value store that allows multiple
14 * entries for the same key. It follows the same metaformat as described
15 * in confparse.c, and uses the same code to read and write itself.
17 * The state file is most suitable for small values that don't change too
18 * frequently. For values that become very large, we typically use a separate
19 * file -- for example, see how we handle microdescriptors, by storing them in
20 * a separate file with a journal.
22 * The current state is accessed via get_or_state(), which returns a singleton
23 * or_state_t object. Functions that change it should call
24 * or_state_mark_dirty() to ensure that it will get written to disk.
26 * The or_state_save() function additionally calls various functioens
27 * throughout Tor that might want to flush more state to the the disk,
28 * including some in rephist.c, entrynodes.c, circuitstats.c, hibernate.c.
31 #define STATEFILE_PRIVATE
32 #include "core/or/or.h"
33 #include "core/or/circuitstats.h"
34 #include "app/config/config.h"
35 #include "app/config/confparse.h"
36 #include "core/mainloop/mainloop.h"
37 #include "core/mainloop/netstatus.h"
38 #include "core/mainloop/connection.h"
39 #include "feature/control/control.h"
40 #include "feature/client/entrynodes.h"
41 #include "feature/hibernate/hibernate.h"
42 #include "feature/stats/rephist.h"
43 #include "feature/relay/router.h"
44 #include "feature/relay/routermode.h"
45 #include "lib/sandbox/sandbox.h"
46 #include "app/config/statefile.h"
47 #include "lib/encoding/confline.h"
48 #include "lib/net/resolve.h"
49 #include "lib/version/torversion.h"
51 #include "app/config/or_state_st.h"
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
57 /** A list of state-file "abbreviations," for compatibility. */
58 static config_abbrev_t state_abbrevs_[] = {
59 { "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 },
60 { "HelperNode", "EntryGuard", 0, 0 },
61 { "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 },
62 { "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
63 { "EntryNode", "EntryGuard", 0, 0 },
64 { "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 },
65 { "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 },
66 { NULL, NULL, 0, 0},
69 /** dummy instance of or_state_t, used for type-checking its
70 * members with CONF_CHECK_VAR_TYPE. */
71 DUMMY_TYPECHECK_INSTANCE(or_state_t);
73 /*XXXX these next two are duplicates or near-duplicates from config.c */
74 #define VAR(name,conftype,member,initvalue) \
75 { name, CONFIG_TYPE_ ## conftype, offsetof(or_state_t, member), \
76 initvalue CONF_TEST_MEMBERS(or_state_t, conftype, member) }
77 /** As VAR, but the option name and member name are the same. */
78 #define V(member,conftype,initvalue) \
79 VAR(#member, conftype, member, initvalue)
81 /** Array of "state" variables saved to the ~/.tor/state file. */
82 static config_var_t state_vars_[] = {
83 /* Remember to document these in state-contents.txt ! */
85 V(AccountingBytesReadInInterval, MEMUNIT, NULL),
86 V(AccountingBytesWrittenInInterval, MEMUNIT, NULL),
87 V(AccountingExpectedUsage, MEMUNIT, NULL),
88 V(AccountingIntervalStart, ISOTIME, NULL),
89 V(AccountingSecondsActive, INTERVAL, NULL),
90 V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL),
91 V(AccountingSoftLimitHitAt, ISOTIME, NULL),
92 V(AccountingBytesAtSoftLimit, MEMUNIT, NULL),
94 VAR("EntryGuard", LINELIST_S, EntryGuards, NULL),
95 VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL),
96 VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL),
97 VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL),
98 VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL),
99 VAR("EntryGuardPathUseBias", LINELIST_S, EntryGuards, NULL),
100 V(EntryGuards, LINELIST_V, NULL),
102 VAR("TransportProxy", LINELIST_S, TransportProxies, NULL),
103 V(TransportProxies, LINELIST_V, NULL),
105 V(HidServRevCounter, LINELIST, NULL),
107 V(BWHistoryReadEnds, ISOTIME, NULL),
108 V(BWHistoryReadInterval, UINT, "900"),
109 V(BWHistoryReadValues, CSV, ""),
110 V(BWHistoryReadMaxima, CSV, ""),
111 V(BWHistoryWriteEnds, ISOTIME, NULL),
112 V(BWHistoryWriteInterval, UINT, "900"),
113 V(BWHistoryWriteValues, CSV, ""),
114 V(BWHistoryWriteMaxima, CSV, ""),
115 V(BWHistoryDirReadEnds, ISOTIME, NULL),
116 V(BWHistoryDirReadInterval, UINT, "900"),
117 V(BWHistoryDirReadValues, CSV, ""),
118 V(BWHistoryDirReadMaxima, CSV, ""),
119 V(BWHistoryDirWriteEnds, ISOTIME, NULL),
120 V(BWHistoryDirWriteInterval, UINT, "900"),
121 V(BWHistoryDirWriteValues, CSV, ""),
122 V(BWHistoryDirWriteMaxima, CSV, ""),
124 V(Guard, LINELIST, NULL),
126 V(TorVersion, STRING, NULL),
128 V(LastRotatedOnionKey, ISOTIME, NULL),
129 V(LastWritten, ISOTIME, NULL),
131 V(TotalBuildTimes, UINT, NULL),
132 V(CircuitBuildAbandonedCount, UINT, "0"),
133 VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
134 VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
136 V(MinutesSinceUserActivity, UINT, NULL),
137 V(Dormant, AUTOBOOL, "auto"),
139 END_OF_CONFIG_VARS
142 #undef VAR
143 #undef V
145 static int or_state_validate(or_state_t *state, char **msg);
147 static int or_state_validate_cb(void *old_options, void *options,
148 void *default_options,
149 int from_setconf, char **msg);
151 static void or_state_free_cb(void *state);
153 /** Magic value for or_state_t. */
154 #define OR_STATE_MAGIC 0x57A73f57
156 /** "Extra" variable in the state that receives lines we can't parse. This
157 * lets us preserve options from versions of Tor newer than us. */
158 static config_var_t state_extra_var = {
159 "__extra", CONFIG_TYPE_LINELIST, offsetof(or_state_t, ExtraLines), NULL
160 CONF_TEST_MEMBERS(or_state_t, LINELIST, ExtraLines)
163 /** Configuration format for or_state_t. */
164 static const config_format_t state_format = {
165 sizeof(or_state_t),
166 OR_STATE_MAGIC,
167 offsetof(or_state_t, magic_),
168 state_abbrevs_,
169 NULL,
170 state_vars_,
171 or_state_validate_cb,
172 or_state_free_cb,
173 &state_extra_var,
176 /** Persistent serialized state. */
177 static or_state_t *global_state = NULL;
179 /** Return the persistent state struct for this Tor. */
180 MOCK_IMPL(or_state_t *,
181 get_or_state, (void))
183 tor_assert(global_state);
184 return global_state;
187 /** Return true iff we have loaded the global state for this Tor */
189 or_state_loaded(void)
191 return global_state != NULL;
194 /** Return true if <b>line</b> is a valid state TransportProxy line.
195 * Return false otherwise. */
196 static int
197 state_transport_line_is_valid(const char *line)
199 smartlist_t *items = NULL;
200 char *addrport=NULL;
201 tor_addr_t addr;
202 uint16_t port = 0;
203 int r;
205 items = smartlist_new();
206 smartlist_split_string(items, line, NULL,
207 SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
209 if (smartlist_len(items) != 2) {
210 log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line.");
211 goto err;
214 addrport = smartlist_get(items, 1);
215 if (tor_addr_port_lookup(addrport, &addr, &port) < 0) {
216 log_warn(LD_CONFIG, "state: Could not parse addrport.");
217 goto err;
220 if (!port) {
221 log_warn(LD_CONFIG, "state: Transport line did not contain port.");
222 goto err;
225 r = 1;
226 goto done;
228 err:
229 r = 0;
231 done:
232 SMARTLIST_FOREACH(items, char*, s, tor_free(s));
233 smartlist_free(items);
234 return r;
237 /** Return 0 if all TransportProxy lines in <b>state</b> are well
238 * formed. Otherwise, return -1. */
239 static int
240 validate_transports_in_state(or_state_t *state)
242 int broken = 0;
243 config_line_t *line;
245 for (line = state->TransportProxies ; line ; line = line->next) {
246 tor_assert(!strcmp(line->key, "TransportProxy"));
247 if (!state_transport_line_is_valid(line->value))
248 broken = 1;
251 if (broken)
252 log_warn(LD_CONFIG, "state: State file seems to be broken.");
254 return 0;
257 static int
258 or_state_validate_cb(void *old_state, void *state, void *default_state,
259 int from_setconf, char **msg)
261 /* We don't use these; only options do. Still, we need to match that
262 * signature. */
263 (void) from_setconf;
264 (void) default_state;
265 (void) old_state;
267 return or_state_validate(state, msg);
270 static void
271 or_state_free_cb(void *state)
273 or_state_free_(state);
276 /** Return 0 if every setting in <b>state</b> is reasonable, and a
277 * permissible transition from <b>old_state</b>. Else warn and return -1.
278 * Should have no side effects, except for normalizing the contents of
279 * <b>state</b>.
281 static int
282 or_state_validate(or_state_t *state, char **msg)
284 if (entry_guards_parse_state(state, 0, msg)<0)
285 return -1;
287 if (validate_transports_in_state(state)<0)
288 return -1;
290 return 0;
293 /** Replace the current persistent state with <b>new_state</b> */
294 static int
295 or_state_set(or_state_t *new_state)
297 char *err = NULL;
298 int ret = 0;
299 tor_assert(new_state);
300 config_free(&state_format, global_state);
301 global_state = new_state;
302 if (entry_guards_parse_state(global_state, 1, &err)<0) {
303 log_warn(LD_GENERAL,"%s",err);
304 tor_free(err);
305 ret = -1;
307 if (rep_hist_load_state(global_state, &err)<0) {
308 log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err);
309 tor_free(err);
310 ret = -1;
312 if (circuit_build_times_parse_state(
313 get_circuit_build_times_mutable(),global_state) < 0) {
314 ret = -1;
316 netstatus_load_from_state(global_state, time(NULL));
318 return ret;
322 * Save a broken state file to a backup location.
324 static void
325 or_state_save_broken(char *fname)
327 int i, res;
328 file_status_t status;
329 char *fname2 = NULL;
330 for (i = 0; i < 100; ++i) {
331 tor_asprintf(&fname2, "%s.%d", fname, i);
332 status = file_status(fname2);
333 if (status == FN_NOENT)
334 break;
335 tor_free(fname2);
337 if (i == 100) {
338 log_warn(LD_BUG, "Unable to parse state in \"%s\"; too many saved bad "
339 "state files to move aside. Discarding the old state file.",
340 fname);
341 res = unlink(fname);
342 if (res != 0) {
343 log_warn(LD_FS,
344 "Also couldn't discard old state file \"%s\" because "
345 "unlink() failed: %s",
346 fname, strerror(errno));
348 } else {
349 log_warn(LD_BUG, "Unable to parse state in \"%s\". Moving it aside "
350 "to \"%s\". This could be a bug in Tor; please tell "
351 "the developers.", fname, fname2);
352 if (tor_rename(fname, fname2) < 0) {//XXXX sandbox prohibits
353 log_warn(LD_BUG, "Weirdly, I couldn't even move the state aside. The "
354 "OS gave an error of %s", strerror(errno));
357 tor_free(fname2);
360 STATIC or_state_t *
361 or_state_new(void)
363 or_state_t *new_state = tor_malloc_zero(sizeof(or_state_t));
364 new_state->magic_ = OR_STATE_MAGIC;
365 config_init(&state_format, new_state);
367 return new_state;
370 /** Reload the persistent state from disk, generating a new state as needed.
371 * Return 0 on success, less than 0 on failure.
374 or_state_load(void)
376 or_state_t *new_state = NULL;
377 char *contents = NULL, *fname;
378 char *errmsg = NULL;
379 int r = -1, badstate = 0;
381 fname = get_datadir_fname("state");
382 switch (file_status(fname)) {
383 case FN_FILE:
384 if (!(contents = read_file_to_str(fname, 0, NULL))) {
385 log_warn(LD_FS, "Unable to read state file \"%s\"", fname);
386 goto done;
388 break;
389 /* treat empty state files as if the file doesn't exist, and generate
390 * a new state file, overwriting the empty file in or_state_save() */
391 case FN_NOENT:
392 case FN_EMPTY:
393 break;
394 case FN_ERROR:
395 case FN_DIR:
396 default:
397 log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname);
398 goto done;
400 new_state = or_state_new();
401 if (contents) {
402 config_line_t *lines=NULL;
403 int assign_retval;
404 if (config_get_lines(contents, &lines, 0)<0)
405 goto done;
406 assign_retval = config_assign(&state_format, new_state,
407 lines, 0, &errmsg);
408 config_free_lines(lines);
409 if (assign_retval<0)
410 badstate = 1;
411 if (errmsg) {
412 log_warn(LD_GENERAL, "%s", errmsg);
413 tor_free(errmsg);
417 if (!badstate && or_state_validate(new_state, &errmsg) < 0)
418 badstate = 1;
420 if (errmsg) {
421 log_warn(LD_GENERAL, "%s", errmsg);
422 tor_free(errmsg);
425 if (badstate && !contents) {
426 log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state."
427 " This is a bug in Tor.");
428 goto done;
429 } else if (badstate && contents) {
430 or_state_save_broken(fname);
432 tor_free(contents);
433 config_free(&state_format, new_state);
435 new_state = or_state_new();
436 } else if (contents) {
437 log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
438 /* Warn the user if their clock has been set backwards,
439 * they could be tricked into using old consensuses */
440 time_t apparent_skew = time(NULL) - new_state->LastWritten;
441 if (apparent_skew < 0) {
442 /* Initialize bootstrap event reporting because we might call
443 * clock_skew_warning() before the bootstrap state is
444 * initialized, causing an assertion failure. */
445 control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
446 clock_skew_warning(NULL, (long)apparent_skew, 1, LD_GENERAL,
447 "local state file", fname);
449 } else {
450 log_info(LD_GENERAL, "Initialized state");
452 if (or_state_set(new_state) == -1) {
453 or_state_save_broken(fname);
455 new_state = NULL;
456 if (!contents) {
457 global_state->next_write = 0;
458 or_state_save(time(NULL));
460 r = 0;
462 done:
463 tor_free(fname);
464 tor_free(contents);
465 if (new_state)
466 config_free(&state_format, new_state);
468 return r;
471 /** Did the last time we tried to write the state file fail? If so, we
472 * should consider disabling such features as preemptive circuit generation
473 * to compute circuit-build-time. */
474 static int last_state_file_write_failed = 0;
476 /** Return whether the state file failed to write last time we tried. */
478 did_last_state_file_write_fail(void)
480 return last_state_file_write_failed;
483 /** If writing the state to disk fails, try again after this many seconds. */
484 #define STATE_WRITE_RETRY_INTERVAL 3600
486 /** If we're a relay, how often should we checkpoint our state file even
487 * if nothing else dirties it? This will checkpoint ongoing stats like
488 * bandwidth used, per-country user stats, etc. */
489 #define STATE_RELAY_CHECKPOINT_INTERVAL (12*60*60)
491 /** Write the persistent state to disk. Return 0 for success, <0 on failure. */
493 or_state_save(time_t now)
495 char *state, *contents;
496 char tbuf[ISO_TIME_LEN+1];
497 char *fname;
499 tor_assert(global_state);
501 if (global_state->next_write > now)
502 return 0;
504 /* Call everything else that might dirty the state even more, in order
505 * to avoid redundant writes. */
506 entry_guards_update_state(global_state);
507 rep_hist_update_state(global_state);
508 circuit_build_times_update_state(get_circuit_build_times(), global_state);
509 netstatus_flush_to_state(global_state, now);
511 if (accounting_is_enabled(get_options()))
512 accounting_run_housekeeping(now);
514 global_state->LastWritten = now;
516 tor_free(global_state->TorVersion);
517 tor_asprintf(&global_state->TorVersion, "Tor %s", get_version());
519 state = config_dump(&state_format, NULL, global_state, 1, 0);
520 format_local_iso_time(tbuf, now);
521 tor_asprintf(&contents,
522 "# Tor state file last generated on %s local time\n"
523 "# Other times below are in UTC\n"
524 "# You *do not* need to edit this file.\n\n%s",
525 tbuf, state);
526 tor_free(state);
527 fname = get_datadir_fname("state");
528 if (write_str_to_file(fname, contents, 0)<0) {
529 log_warn(LD_FS, "Unable to write state to file \"%s\"; "
530 "will try again later", fname);
531 last_state_file_write_failed = 1;
532 tor_free(fname);
533 tor_free(contents);
534 /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state
535 * changes sooner). */
536 global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL;
537 return -1;
540 last_state_file_write_failed = 0;
541 log_info(LD_GENERAL, "Saved state to \"%s\"", fname);
542 tor_free(fname);
543 tor_free(contents);
545 if (server_mode(get_options()))
546 global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL;
547 else
548 global_state->next_write = TIME_MAX;
550 return 0;
553 /** Return the config line for transport <b>transport</b> in the current state.
554 * Return NULL if there is no config line for <b>transport</b>. */
555 STATIC config_line_t *
556 get_transport_in_state_by_name(const char *transport)
558 or_state_t *or_state = get_or_state();
559 config_line_t *line;
560 config_line_t *ret = NULL;
561 smartlist_t *items = NULL;
563 for (line = or_state->TransportProxies ; line ; line = line->next) {
564 tor_assert(!strcmp(line->key, "TransportProxy"));
566 items = smartlist_new();
567 smartlist_split_string(items, line->value, NULL,
568 SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
569 if (smartlist_len(items) != 2) /* broken state */
570 goto done;
572 if (!strcmp(smartlist_get(items, 0), transport)) {
573 ret = line;
574 goto done;
577 SMARTLIST_FOREACH(items, char*, s, tor_free(s));
578 smartlist_free(items);
579 items = NULL;
582 done:
583 if (items) {
584 SMARTLIST_FOREACH(items, char*, s, tor_free(s));
585 smartlist_free(items);
587 return ret;
590 /** Return string containing the address:port part of the
591 * TransportProxy <b>line</b> for transport <b>transport</b>.
592 * If the line is corrupted, return NULL. */
593 static const char *
594 get_transport_bindaddr(const char *line, const char *transport)
596 char *line_tmp = NULL;
598 if (strlen(line) < strlen(transport) + 2) {
599 goto broken_state;
600 } else {
601 /* line should start with the name of the transport and a space.
602 (for example, "obfs2 127.0.0.1:47245") */
603 tor_asprintf(&line_tmp, "%s ", transport);
604 if (strcmpstart(line, line_tmp))
605 goto broken_state;
607 tor_free(line_tmp);
608 return (line+strlen(transport)+1);
611 broken_state:
612 tor_free(line_tmp);
613 return NULL;
616 /** Return a string containing the address:port that a proxy transport
617 * should bind on. The string is stored on the heap and must be freed
618 * by the caller of this function. */
619 char *
620 get_stored_bindaddr_for_server_transport(const char *transport)
622 char *default_addrport = NULL;
623 const char *stored_bindaddr = NULL;
624 config_line_t *line = NULL;
627 /* See if the user explicitly asked for a specific listening
628 address for this transport. */
629 char *conf_bindaddr = get_transport_bindaddr_from_config(transport);
630 if (conf_bindaddr)
631 return conf_bindaddr;
634 line = get_transport_in_state_by_name(transport);
635 if (!line) /* Found no references in state for this transport. */
636 goto no_bindaddr_found;
638 stored_bindaddr = get_transport_bindaddr(line->value, transport);
639 if (stored_bindaddr) /* found stored bindaddr in state file. */
640 return tor_strdup(stored_bindaddr);
642 no_bindaddr_found:
643 /** If we didn't find references for this pluggable transport in the
644 state file, we should instruct the pluggable transport proxy to
645 listen on INADDR_ANY on a random ephemeral port. */
646 tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0");
647 return default_addrport;
650 /** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to
651 state */
652 void
653 save_transport_to_state(const char *transport,
654 const tor_addr_t *addr, uint16_t port)
656 or_state_t *state = get_or_state();
658 char *transport_addrport=NULL;
660 /** find where to write on the state */
661 config_line_t **next, *line;
663 /* see if this transport is already stored in state */
664 config_line_t *transport_line =
665 get_transport_in_state_by_name(transport);
667 if (transport_line) { /* if transport already exists in state... */
668 const char *prev_bindaddr = /* get its addrport... */
669 get_transport_bindaddr(transport_line->value, transport);
670 transport_addrport = tor_strdup(fmt_addrport(addr, port));
672 /* if transport in state has the same address as this one, life is good */
673 if (!strcmp(prev_bindaddr, transport_addrport)) {
674 log_info(LD_CONFIG, "Transport seems to have spawned on its usual "
675 "address:port.");
676 goto done;
677 } else { /* if addrport in state is different than the one we got */
678 log_info(LD_CONFIG, "Transport seems to have spawned on different "
679 "address:port. Let's update the state file with the new "
680 "address:port");
681 tor_free(transport_line->value); /* free the old line */
682 /* replace old addrport line with new line */
683 tor_asprintf(&transport_line->value, "%s %s", transport,
684 fmt_addrport(addr, port));
686 } else { /* never seen this one before; save it in state for next time */
687 log_info(LD_CONFIG, "It's the first time we see this transport. "
688 "Let's save its address:port");
689 next = &state->TransportProxies;
690 /* find the last TransportProxy line in the state and point 'next'
691 right after it */
692 line = state->TransportProxies;
693 while (line) {
694 next = &(line->next);
695 line = line->next;
698 /* allocate space for the new line and fill it in */
699 *next = line = tor_malloc_zero(sizeof(config_line_t));
700 line->key = tor_strdup("TransportProxy");
701 tor_asprintf(&line->value, "%s %s", transport, fmt_addrport(addr, port));
704 if (!get_options()->AvoidDiskWrites)
705 or_state_mark_dirty(state, 0);
707 done:
708 tor_free(transport_addrport);
711 /** Change the next_write time of <b>state</b> to <b>when</b>, unless the
712 * state is already scheduled to be written to disk earlier than <b>when</b>.
714 void
715 or_state_mark_dirty(or_state_t *state, time_t when)
717 if (state->next_write > when) {
718 state->next_write = when;
719 reschedule_or_state_save();
723 STATIC void
724 or_state_free_(or_state_t *state)
726 if (!state)
727 return;
729 config_free(&state_format, state);
732 void
733 or_state_free_all(void)
735 or_state_free(global_state);
736 global_state = NULL;