aboutsummaryrefslogtreecommitdiff
path: root/src/app/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/main')
-rw-r--r--src/app/main/.may_include1
-rw-r--r--src/app/main/app_main.md2
-rw-r--r--src/app/main/include.am20
-rw-r--r--src/app/main/main.c482
-rw-r--r--src/app/main/main.h8
-rw-r--r--src/app/main/ntmain.c16
-rw-r--r--src/app/main/ntmain.h5
-rw-r--r--src/app/main/risky_options.c35
-rw-r--r--src/app/main/risky_options.h17
-rw-r--r--src/app/main/shutdown.c171
-rw-r--r--src/app/main/shutdown.h18
-rw-r--r--src/app/main/subsysmgr.c478
-rw-r--r--src/app/main/subsysmgr.h53
-rw-r--r--src/app/main/subsystem_list.c77
-rw-r--r--src/app/main/tor_main.c2
15 files changed, 1064 insertions, 321 deletions
diff --git a/src/app/main/.may_include b/src/app/main/.may_include
new file mode 100644
index 0000000000..424c745c12
--- /dev/null
+++ b/src/app/main/.may_include
@@ -0,0 +1 @@
+*.h
diff --git a/src/app/main/app_main.md b/src/app/main/app_main.md
new file mode 100644
index 0000000000..b8c789716c
--- /dev/null
+++ b/src/app/main/app_main.md
@@ -0,0 +1,2 @@
+@dir /app/main
+@brief app/main: Entry point for tor.
diff --git a/src/app/main/include.am b/src/app/main/include.am
new file mode 100644
index 0000000000..576c750377
--- /dev/null
+++ b/src/app/main/include.am
@@ -0,0 +1,20 @@
+
+# ADD_C_FILE: INSERT SOURCES HERE.
+LIBTOR_APP_A_SOURCES += \
+ src/app/main/main.c \
+ src/app/main/risky_options.c \
+ src/app/main/shutdown.c \
+ src/app/main/subsystem_list.c \
+ src/app/main/subsysmgr.c
+
+# ADD_C_FILE: INSERT HEADERS HERE.
+noinst_HEADERS += \
+ src/app/main/main.h \
+ src/app/main/ntmain.h \
+ src/app/main/risky_options.h \
+ src/app/main/shutdown.h \
+ src/app/main/subsysmgr.h
+
+if BUILD_NT_SERVICES
+LIBTOR_APP_A_SOURCES += src/app/main/ntmain.c
+endif
diff --git a/src/app/main/main.c b/src/app/main/main.c
index ca3148e7fc..5043caedb6 100644
--- a/src/app/main/main.c
+++ b/src/app/main/main.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -13,80 +13,73 @@
#include "app/config/config.h"
#include "app/config/statefile.h"
+#include "app/config/quiet_level.h"
#include "app/main/main.h"
#include "app/main/ntmain.h"
+#include "app/main/risky_options.h"
+#include "app/main/shutdown.h"
+#include "app/main/subsysmgr.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/cpuworker.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/mainloop_pubsub.h"
#include "core/mainloop/netstatus.h"
#include "core/or/channel.h"
#include "core/or/channelpadding.h"
-#include "core/or/channeltls.h"
+#include "core/or/circuitpadding.h"
#include "core/or/circuitlist.h"
-#include "core/or/circuitmux_ewma.h"
#include "core/or/command.h"
-#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
-#include "core/or/dos.h"
-#include "core/or/policies.h"
-#include "core/or/protover.h"
#include "core/or/relay.h"
-#include "core/or/scheduler.h"
#include "core/or/status.h"
#include "feature/api/tor_api.h"
#include "feature/api/tor_api_internal.h"
#include "feature/client/addressmap.h"
-#include "feature/client/bridges.h"
-#include "feature/client/entrynodes.h"
-#include "feature/client/transports.h"
#include "feature/control/control.h"
-#include "feature/dirauth/bwauth.h"
+#include "feature/control/control_auth.h"
+#include "feature/control/control_events.h"
#include "feature/dirauth/keypin.h"
#include "feature/dirauth/process_descs.h"
#include "feature/dircache/consdiffmgr.h"
-#include "feature/dircache/dirserv.h"
#include "feature/dirparse/routerparse.h"
#include "feature/hibernate/hibernate.h"
-#include "feature/hs/hs_cache.h"
+#include "feature/hs/hs_dos.h"
#include "feature/nodelist/authcert.h"
-#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
-#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerlist.h"
#include "feature/relay/dns.h"
#include "feature/relay/ext_orport.h"
-#include "feature/relay/onion_queue.h"
#include "feature/relay/routerkeys.h"
#include "feature/relay/routermode.h"
#include "feature/rend/rendcache.h"
-#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
-#include "feature/stats/geoip_stats.h"
#include "feature/stats/predict_ports.h"
+#include "feature/stats/bwhist.h"
#include "feature/stats/rephist.h"
#include "lib/compress/compress.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_s2k.h"
-#include "lib/err/backtrace.h"
-#include "lib/geoip/geoip.h"
+#include "lib/net/resolve.h"
+#include "lib/trace/trace.h"
#include "lib/process/waitpid.h"
+#include "lib/pubsub/pubsub_build.h"
#include "lib/meminfo/meminfo.h"
#include "lib/osinfo/uname.h"
+#include "lib/osinfo/libc.h"
#include "lib/sandbox/sandbox.h"
#include "lib/fs/lockfile.h"
-#include "lib/net/resolve.h"
#include "lib/tls/tortls.h"
#include "lib/evloop/compat_libevent.h"
#include "lib/encoding/confline.h"
#include "lib/evloop/timers.h"
#include "lib/crypt_ops/crypto_init.h"
+#include "lib/version/torversion.h"
#include <event2/event.h>
-#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/authmode.h"
#include "feature/dirauth/shared_random.h"
@@ -107,8 +100,6 @@
#include <systemd/sd-daemon.h>
#endif /* defined(HAVE_SYSTEMD) */
-void evdns_shutdown(int);
-
#ifdef HAVE_RUST
// helper function defined in Rust to output a log message indicating if tor is
// running with Rust enabled. See src/rust/tor_util
@@ -121,16 +112,6 @@ static void dumpmemusage(int severity);
static void dumpstats(int severity); /* log stats */
static void process_signal(int sig);
-/********* START VARIABLES **********/
-
-/** Decides our behavior when no logs are configured/before any
- * logs have been configured. For 0, we log notice to stdout as normal.
- * For 1, we log warnings only. For 2, we log nothing.
- */
-int quiet_level = 0;
-
-/********* END VARIABLES ************/
-
/** Called when we get a SIGHUP: reload configuration files and keys,
* retry all connections, and so on. */
static int
@@ -301,9 +282,35 @@ process_signal(int sig)
log_heartbeat(time(NULL));
control_event_signal(sig);
break;
+ case SIGACTIVE:
+ /* "SIGACTIVE" counts as ersatz user activity. */
+ note_user_activity(approx_time());
+ control_event_signal(sig);
+ break;
+ case SIGDORMANT:
+ /* "SIGDORMANT" means to ignore past user activity */
+ log_notice(LD_GENERAL, "Going dormant because of controller request.");
+ reset_user_activity(0);
+ set_network_participation(false);
+ schedule_rescan_periodic_events();
+ control_event_signal(sig);
+ break;
}
}
+#ifdef _WIN32
+/** Activate SIGINT on receiving a control signal in console. */
+static BOOL WINAPI
+process_win32_console_ctrl(DWORD ctrl_type)
+{
+ /* Ignore type of the ctrl signal */
+ (void) ctrl_type;
+
+ activate_signal(SIGINT);
+ return TRUE;
+}
+#endif
+
/**
* Write current memory usage information to the log.
*/
@@ -332,16 +339,12 @@ dumpstats(int severity)
SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
int i = conn_sl_idx;
tor_log(severity, LD_GENERAL,
- "Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago",
- i, (int)conn->s, conn->type, conn_type_to_string(conn->type),
- conn->state, conn_state_to_string(conn->type, conn->state),
+ "Conn %d (socket %d) is a %s, created %d secs ago",
+ i, (int)conn->s,
+ connection_describe(conn),
(int)(now - conn->timestamp_created));
if (!connection_is_listener(conn)) {
tor_log(severity,LD_GENERAL,
- "Conn %d is to %s:%d.", i,
- safe_str_client(conn->address),
- conn->port);
- tor_log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)",
i,
(int)connection_get_inbuf_len(conn),
@@ -423,18 +426,7 @@ dumpstats(int severity)
rep_hist_dump_stats(now,severity);
rend_service_dump_stats(severity);
-}
-
-/** Called by exit() as we shut down the process.
- */
-static void
-exit_function(void)
-{
- /* NOTE: If we ever daemonize, this gets called immediately. That's
- * okay for now, because we only use this on Windows. */
-#ifdef _WIN32
- WSACleanup();
-#endif
+ hs_service_dump_stats(severity);
}
#ifdef _WIN32
@@ -481,6 +473,8 @@ static struct {
{ SIGNEWNYM, 0, NULL },
{ SIGCLEARDNSCACHE, 0, NULL },
{ SIGHEARTBEAT, 0, NULL },
+ { SIGACTIVE, 0, NULL },
+ { SIGDORMANT, 0, NULL },
{ -1, -1, NULL }
};
@@ -515,6 +509,13 @@ handle_signals(void)
&signal_handlers[i].signal_value);
}
}
+
+#ifdef _WIN32
+ /* Windows lacks traditional POSIX signals but WinAPI provides a function
+ * to handle control signals like Ctrl+C in the console, we can use this to
+ * simulate the SIGINT signal */
+ if (enabled) SetConsoleCtrlHandler(process_win32_console_ctrl, TRUE);
+#endif
}
/* Cause the signal handler for signal_num to be called in the event loop. */
@@ -537,7 +538,8 @@ int
tor_init(int argc, char *argv[])
{
char progname[256];
- int quiet = 0;
+ quiet_level_t quiet = QUIET_NONE;
+ bool running_tor = false;
time_of_process_start = time(NULL);
tor_init_connection_lists();
@@ -545,66 +547,39 @@ tor_init(int argc, char *argv[])
tor_snprintf(progname, sizeof(progname), "Tor %s", get_version());
log_set_application_name(progname);
- /* Set up the crypto nice and early */
- if (crypto_early_init() < 0) {
- log_err(LD_GENERAL, "Unable to initialize the crypto subsystem!");
- return -1;
- }
-
/* Initialize the history structures. */
rep_hist_init();
+ bwhist_init();
/* Initialize the service cache. */
rend_cache_init();
addressmap_init(); /* Init the client dns cache. Do it always, since it's
* cheap. */
+
/* Initialize the HS subsystem. */
hs_init();
{
- /* We search for the "quiet" option first, since it decides whether we
- * will log anything at all to the command line. */
- config_line_t *opts = NULL, *cmdline_opts = NULL;
- const config_line_t *cl;
- (void) config_parse_commandline(argc, argv, 1, &opts, &cmdline_opts);
- for (cl = cmdline_opts; cl; cl = cl->next) {
- if (!strcmp(cl->key, "--hush"))
- quiet = 1;
- if (!strcmp(cl->key, "--quiet") ||
- !strcmp(cl->key, "--dump-config"))
- quiet = 2;
- /* The following options imply --hush */
- if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") ||
- !strcmp(cl->key, "--list-torrc-options") ||
- !strcmp(cl->key, "--library-versions") ||
- !strcmp(cl->key, "--list-modules") ||
- !strcmp(cl->key, "--hash-password") ||
- !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
- if (quiet < 1)
- quiet = 1;
- }
+ /* We check for the "quiet"/"hush" settings first, since they decide
+ whether we log anything at all to stdout. */
+ parsed_cmdline_t *cmdline;
+ cmdline = config_parse_commandline(argc, argv, 1);
+ if (cmdline) {
+ quiet = cmdline->quiet_level;
+ running_tor = (cmdline->command == CMD_RUN_TOR);
}
- config_free_lines(opts);
- config_free_lines(cmdline_opts);
+ parsed_cmdline_free(cmdline);
}
/* give it somewhere to log to initially */
- switch (quiet) {
- case 2:
- /* no initial logging */
- break;
- case 1:
- add_temp_log(LOG_WARN);
- break;
- default:
- add_temp_log(LOG_NOTICE);
- }
+ add_default_log_for_quiet_level(quiet);
quiet_level = quiet;
{
const char *version = get_version();
log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, "
- "%s %s, Zlib %s, Liblzma %s, and Libzstd %s.", version,
+ "%s %s, Zlib %s, Liblzma %s, Libzstd %s and %s %s as libc.",
+ version,
get_uname(),
tor_libevent_get_version_str(),
crypto_get_library_name(),
@@ -614,7 +589,10 @@ tor_init(int argc, char *argv[])
tor_compress_supports_method(LZMA_METHOD) ?
tor_compress_version_str(LZMA_METHOD) : "N/A",
tor_compress_supports_method(ZSTD_METHOD) ?
- tor_compress_version_str(ZSTD_METHOD) : "N/A");
+ tor_compress_version_str(ZSTD_METHOD) : "N/A",
+ tor_libc_get_name() ?
+ tor_libc_get_name() : "Unknown",
+ tor_libc_get_version_str());
log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! "
"Learn how to be safe at "
@@ -624,6 +602,12 @@ tor_init(int argc, char *argv[])
log_notice(LD_GENERAL, "This version is not a stable Tor release. "
"Expect more bugs than usual.");
+ if (strlen(risky_option_list) && running_tor) {
+ log_warn(LD_GENERAL, "This build of Tor has been compiled with one "
+ "or more options that might make it less reliable or secure! "
+ "They are:%s", risky_option_list);
+ }
+
tor_compress_log_init_warnings();
}
@@ -631,11 +615,8 @@ tor_init(int argc, char *argv[])
rust_log_welcome_string();
#endif /* defined(HAVE_RUST) */
- if (network_init()<0) {
- log_err(LD_BUG,"Error initializing network; exiting.");
- return -1;
- }
- atexit(exit_function);
+ /* Warn _if_ the tracing subsystem is built in. */
+ tracing_log_warning();
int init_rv = options_init_from_torrc(argc,argv);
if (init_rv < 0) {
@@ -647,12 +628,17 @@ tor_init(int argc, char *argv[])
return 1;
}
- /* The options are now initialised */
- const or_options_t *options = get_options();
-
- /* Initialize channelpadding parameters to defaults until we get
- * a consensus */
+ /* Initialize channelpadding and circpad parameters to defaults
+ * until we get a consensus */
channelpadding_new_consensus_params(NULL);
+ circpad_new_consensus_params(NULL);
+
+ /* Initialize circuit padding to defaults+torrc until we get a consensus */
+ circpad_machines_init();
+
+ /* Initialize hidden service DoS subsystem. We need to do this once the
+ * configuration object has been set because it can be accessed. */
+ hs_dos_init();
/* Initialize predicted ports list after loading options */
predicted_ports_init();
@@ -663,17 +649,6 @@ tor_init(int argc, char *argv[])
"and you probably shouldn't.");
#endif
- if (crypto_global_init(options->HardwareAccel,
- options->AccelName,
- options->AccelDir)) {
- log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
- return -1;
- }
- stream_choice_seed_weak_rng();
- if (tor_init_libevent_rng() < 0) {
- log_warn(LD_NET, "Problem initializing libevent RNG.");
- }
-
/* Scan/clean unparseable descriptors; after reading config */
routerparse_init();
@@ -742,86 +717,6 @@ release_lockfile(void)
}
}
-/** Free all memory that we might have allocated somewhere.
- * If <b>postfork</b>, we are a worker process and we want to free
- * only the parts of memory that we won't touch. If !<b>postfork</b>,
- * Tor is shutting down and we should free everything.
- *
- * Helps us find the real leaks with sanitizers and the like. Also valgrind
- * should then report 0 reachable in its leak report (in an ideal world --
- * in practice libevent, SSL, libc etc never quite free everything). */
-void
-tor_free_all(int postfork)
-{
- if (!postfork) {
- evdns_shutdown(1);
- }
- geoip_free_all();
- geoip_stats_free_all();
- dirvote_free_all();
- routerlist_free_all();
- networkstatus_free_all();
- addressmap_free_all();
- dirserv_free_fingerprint_list();
- dirserv_free_all();
- dirserv_clear_measured_bw_cache();
- rend_cache_free_all();
- rend_service_authorization_free_all();
- rep_hist_free_all();
- dns_free_all();
- clear_pending_onions();
- circuit_free_all();
- entry_guards_free_all();
- pt_free_all();
- channel_tls_free_all();
- channel_free_all();
- connection_free_all();
- connection_edge_free_all();
- scheduler_free_all();
- nodelist_free_all();
- microdesc_free_all();
- routerparse_free_all();
- ext_orport_free_all();
- control_free_all();
- tor_free_getaddrinfo_cache();
- protover_free_all();
- bridges_free_all();
- consdiffmgr_free_all();
- hs_free_all();
- dos_free_all();
- circuitmux_ewma_free_all();
- accounting_free_all();
-
- if (!postfork) {
- config_free_all();
- or_state_free_all();
- router_free_all();
- routerkeys_free_all();
- policies_free_all();
- }
- if (!postfork) {
- tor_tls_free_all();
-#ifndef _WIN32
- tor_getpwnam(NULL);
-#endif
- }
- /* stuff in main.c */
-
- tor_mainloop_free_all();
-
- if (!postfork) {
- release_lockfile();
- }
- tor_libevent_free_all();
- /* Stuff in util.c and address.c*/
- if (!postfork) {
- escaped(NULL);
- esc_router_info(NULL);
- clean_up_backtrace_handler();
- logs_free_all(); /* free log strings. do this last so logs keep working. */
- }
-}
-
/**
* Remove the specified file, and log a warning if the operation fails for
* any reason other than the file not existing. Ignores NULL filenames.
@@ -835,51 +730,6 @@ tor_remove_file(const char *filename)
}
}
-/** Do whatever cleanup is necessary before shutting Tor down. */
-void
-tor_cleanup(void)
-{
- const or_options_t *options = get_options();
- if (options->command == CMD_RUN_TOR) {
- time_t now = time(NULL);
- /* Remove our pid file. We don't care if there was an error when we
- * unlink, nothing we could do about it anyways. */
- tor_remove_file(options->PidFile);
- /* Remove control port file */
- tor_remove_file(options->ControlPortWriteToFile);
- /* Remove cookie authentication file */
- {
- char *cookie_fname = get_controller_cookie_file_name();
- tor_remove_file(cookie_fname);
- tor_free(cookie_fname);
- }
- /* Remove Extended ORPort cookie authentication file */
- {
- char *cookie_fname = get_ext_or_auth_cookie_file_name();
- tor_remove_file(cookie_fname);
- tor_free(cookie_fname);
- }
- if (accounting_is_enabled(options))
- accounting_record_bandwidth_usage(now, get_or_state());
- or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
- or_state_save(now);
- if (authdir_mode(options)) {
- sr_save_and_cleanup();
- }
- if (authdir_mode_tests_reachability(options))
- rep_hist_record_mtbf_data(now, 0);
- keypin_close_journal();
- }
-
- timers_shutdown();
-
- tor_free_all(0); /* We could move tor_free_all back into the ifdef below
- later, if it makes shutdown unacceptably slow. But for
- now, leave it here: it's helped us catch bugs in the
- past. */
- crypto_global_cleanup();
-}
-
/** Read/create keys as needed, and echo our fingerprint to stdout. */
static int
do_list_fingerprint(void)
@@ -941,12 +791,14 @@ do_dump_config(void)
if (!strcmp(arg, "short")) {
how = OPTIONS_DUMP_MINIMAL;
} else if (!strcmp(arg, "non-builtin")) {
- how = OPTIONS_DUMP_DEFAULTS;
+ // Deprecated since 0.4.5.1-alpha.
+ fprintf(stderr, "'non-builtin' is deprecated; use 'short' instead.\n");
+ how = OPTIONS_DUMP_MINIMAL;
} else if (!strcmp(arg, "full")) {
how = OPTIONS_DUMP_ALL;
} else {
fprintf(stderr, "No valid argument to --dump-config found!\n");
- fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n");
+ fprintf(stderr, "Please select 'short' or 'full'.\n");
return -1;
}
@@ -961,8 +813,7 @@ do_dump_config(void)
static void
init_addrinfo(void)
{
- if (! server_mode(get_options()) ||
- (get_options()->Address && strlen(get_options()->Address) > 0)) {
+ if (! server_mode(get_options()) || get_options()->Address) {
/* We don't need to seed our own hostname, because we won't be calling
* resolve_my_address on it.
*/
@@ -980,7 +831,6 @@ sandbox_init_filter(void)
{
const or_options_t *options = get_options();
sandbox_cfg_t *cfg = sandbox_cfg_new();
- int i;
sandbox_cfg_allow_openat_filename(&cfg,
get_cachedir_fname("cached-status"));
@@ -988,6 +838,9 @@ sandbox_init_filter(void)
#define OPEN(name) \
sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name))
+#define OPENDIR(dir) \
+ sandbox_cfg_allow_opendir_dirname(&cfg, tor_strdup(dir))
+
#define OPEN_DATADIR(name) \
sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname(name))
@@ -1004,8 +857,10 @@ sandbox_init_filter(void)
OPEN_DATADIR2(name, name2 suffix); \
} while (0)
+// KeyDirectory is a directory, but it is only opened in check_private_dir
+// which calls open instead of opendir
#define OPEN_KEY_DIRECTORY() \
- sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory))
+ OPEN(options->KeyDirectory)
#define OPEN_CACHEDIR(name) \
sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name))
#define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \
@@ -1019,6 +874,8 @@ sandbox_init_filter(void)
OPEN_KEYDIR(name suffix); \
} while (0)
+ // DataDirectory is a directory, but it is only opened in check_private_dir
+ // which calls open instead of opendir
OPEN(options->DataDirectory);
OPEN_KEY_DIRECTORY();
@@ -1059,14 +916,31 @@ sandbox_init_filter(void)
else
sandbox_cfg_allow_open_filename(&cfg, tor_strdup("/etc/resolv.conf"));
- for (i = 0; i < 2; ++i) {
- if (get_torrc_fname(i)) {
- sandbox_cfg_allow_open_filename(&cfg, tor_strdup(get_torrc_fname(i)));
- }
+ const char *torrc_defaults_fname = get_torrc_fname(1);
+ if (torrc_defaults_fname) {
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(torrc_defaults_fname));
+ }
+ const char *torrc_fname = get_torrc_fname(0);
+ if (torrc_fname) {
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(torrc_fname));
+ // allow torrc backup and torrc.tmp to make SAVECONF work
+ char *torrc_bck = NULL;
+ tor_asprintf(&torrc_bck, CONFIG_BACKUP_PATTERN, torrc_fname);
+ sandbox_cfg_allow_rename(&cfg, tor_strdup(torrc_fname), torrc_bck);
+ char *torrc_tmp = NULL;
+ tor_asprintf(&torrc_tmp, "%s.tmp", torrc_fname);
+ sandbox_cfg_allow_rename(&cfg, torrc_tmp, tor_strdup(torrc_fname));
+ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(torrc_tmp));
+ // we need to stat the existing backup file
+ sandbox_cfg_allow_stat_filename(&cfg, tor_strdup(torrc_bck));
}
SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, {
- OPEN(f);
+ if (file_status(f) == FN_DIR) {
+ OPENDIR(f);
+ } else {
+ OPEN(f);
+ }
});
#define RENAME_SUFFIX(name, suffix) \
@@ -1179,7 +1053,7 @@ sandbox_init_filter(void)
* directory that holds it. */
char *dirname = tor_strdup(port->unix_addr);
if (get_parent_directory(dirname) == 0) {
- OPEN(dirname);
+ OPENDIR(dirname);
}
tor_free(dirname);
sandbox_cfg_allow_chmod_filename(&cfg, tor_strdup(port->unix_addr));
@@ -1220,12 +1094,14 @@ sandbox_init_filter(void)
OPEN_DATADIR("approved-routers");
OPEN_DATADIR_SUFFIX("fingerprint", ".tmp");
+ OPEN_DATADIR_SUFFIX("fingerprint-ed25519", ".tmp");
OPEN_DATADIR_SUFFIX("hashed-fingerprint", ".tmp");
OPEN_DATADIR_SUFFIX("router-stability", ".tmp");
OPEN("/etc/resolv.conf");
RENAME_SUFFIX("fingerprint", ".tmp");
+ RENAME_SUFFIX("fingerprint-ed25519", ".tmp");
RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp");
RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp");
@@ -1273,7 +1149,6 @@ int
run_tor_main_loop(void)
{
handle_signals();
- monotime_init();
timers_initialize();
initialize_mainloop_events();
@@ -1378,6 +1253,32 @@ run_tor_main_loop(void)
return do_main_loop();
}
+/** Install the publish/subscribe relationships for all the subsystems. */
+void
+pubsub_install(void)
+{
+ pubsub_builder_t *builder = pubsub_builder_new();
+ int r = subsystems_add_pubsub(builder);
+ tor_assert(r == 0);
+ r = tor_mainloop_connect_pubsub(builder); // consumes builder
+ tor_assert(r == 0);
+}
+
+/** Connect the mainloop to its publish/subscribe message delivery events if
+ * appropriate, and configure the global channels appropriately. */
+void
+pubsub_connect(void)
+{
+ if (get_options()->command == CMD_RUN_TOR) {
+ tor_mainloop_connect_pubsub_events();
+ /* XXXX For each pubsub channel, its delivery strategy should be set at
+ * this XXXX point, using tor_mainloop_set_delivery_strategy().
+ */
+ tor_mainloop_set_delivery_strategy("orconn", DELIV_IMMEDIATE);
+ tor_mainloop_set_delivery_strategy("ocirc", DELIV_IMMEDIATE);
+ }
+}
+
/* Main entry point for the Tor process. Called from tor_main(), and by
* anybody embedding Tor. */
int
@@ -1385,54 +1286,13 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
{
int result = 0;
-#ifdef _WIN32
-#ifndef HeapEnableTerminationOnCorruption
-#define HeapEnableTerminationOnCorruption 1
-#endif
- /* On heap corruption, just give up; don't try to play along. */
- HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
-
- /* SetProcessDEPPolicy is only supported on 32-bit Windows.
- * (On 64-bit Windows it always fails, and some compilers don't like the
- * PSETDEP cast.)
- * 32-bit Windows defines _WIN32.
- * 64-bit Windows defines _WIN32 and _WIN64. */
-#ifndef _WIN64
- /* Call SetProcessDEPPolicy to permanently enable DEP.
- The function will not resolve on earlier versions of Windows,
- and failure is not dangerous. */
- HMODULE hMod = GetModuleHandleA("Kernel32.dll");
- if (hMod) {
- typedef BOOL (WINAPI *PSETDEP)(DWORD);
- PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod,
- "SetProcessDEPPolicy");
- if (setdeppolicy) {
- /* PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION */
- setdeppolicy(3);
- }
- }
-#endif /* !defined(_WIN64) */
-#endif /* defined(_WIN32) */
-
- {
- int bt_err = configure_backtrace_handler(get_version());
- if (bt_err < 0) {
- log_warn(LD_BUG, "Unable to install backtrace handler: %s",
- strerror(-bt_err));
- }
- }
-
#ifdef EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED
event_set_mem_functions(tor_malloc_, tor_realloc_, tor_free_);
#endif
- init_protocol_warning_severity_level();
+ subsystems_init();
- update_approx_time(time(NULL));
- tor_threads_init();
- tor_compress_init();
- init_logging(0);
- monotime_init();
+ init_protocol_warning_severity_level();
int argc = tor_cfg->argc + tor_cfg->argc_owned;
char **argv = tor_calloc(argc, sizeof(char*));
@@ -1441,15 +1301,13 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
memcpy(argv + tor_cfg->argc, tor_cfg->argv_owned,
tor_cfg->argc_owned*sizeof(char*));
-#ifdef NT_SERVICE
- {
- int done = 0;
- result = nt_service_parse_options(argc, argv, &done);
- if (done) {
- goto done;
- }
- }
-#endif /* defined(NT_SERVICE) */
+ int done = 0;
+ result = nt_service_parse_options(argc, argv, &done);
+ if (POSSIBLE(done))
+ goto done;
+
+ pubsub_install();
+
{
int init_rv = tor_init(argc, argv);
if (init_rv) {
@@ -1459,6 +1317,8 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
}
}
+ pubsub_connect();
+
if (get_options()->Sandbox && get_options()->command == CMD_RUN_TOR) {
sandbox_cfg_t* cfg = sandbox_init_filter();
@@ -1468,6 +1328,7 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
tor_free_all(0);
return -1;
}
+ tor_make_getaddrinfo_cache_active();
// registering libevent rng
#ifdef HAVE_EVUTIL_SECURE_RNG_SET_URANDOM_DEVICE_FILE
@@ -1478,9 +1339,7 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
switch (get_options()->command) {
case CMD_RUN_TOR:
-#ifdef NT_SERVICE
nt_service_set_state(SERVICE_RUNNING);
-#endif
result = run_tor_main_loop();
break;
case CMD_KEYGEN:
@@ -1498,7 +1357,7 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
result = 0;
break;
case CMD_VERIFY_CONFIG:
- if (quiet_level == 0)
+ if (quiet_level == QUIET_NONE)
printf("Configuration was valid\n");
result = 0;
break;
@@ -1506,6 +1365,7 @@ tor_run_main(const tor_main_configuration_t *tor_cfg)
result = do_dump_config();
break;
case CMD_RUN_UNITTESTS: /* only set by test.c */
+ case CMD_IMMEDIATE: /* Handled in config.c */
default:
log_warn(LD_BUG,"Illegal command number %d: internal error.",
get_options()->command);
diff --git a/src/app/main/main.h b/src/app/main/main.h
index bbbbf984fb..e6ed978c61 100644
--- a/src/app/main/main.h
+++ b/src/app/main/main.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -21,11 +21,11 @@ void release_lockfile(void);
void tor_remove_file(const char *filename);
-void tor_cleanup(void);
-void tor_free_all(int postfork);
-
int tor_init(int argc, char **argv);
int run_tor_main_loop(void);
+void pubsub_install(void);
+void pubsub_connect(void);
+
#endif /* !defined(TOR_MAIN_H) */
diff --git a/src/app/main/ntmain.c b/src/app/main/ntmain.c
index 05d203b0be..5dc0edd591 100644
--- a/src/app/main/ntmain.c
+++ b/src/app/main/ntmain.c
@@ -1,6 +1,6 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -24,6 +24,7 @@
#include "app/config/config.h"
#include "app/main/main.h"
#include "app/main/ntmain.h"
+#include "app/main/shutdown.h"
#include "core/mainloop/mainloop.h"
#include "lib/evloop/compat_libevent.h"
#include "lib/fs/winlib.h"
@@ -65,7 +66,7 @@ static int nt_service_cmd_stop(void);
/** Struct to hold dynamically loaded NT-service related function pointers.
*/
-struct service_fns {
+struct {
int loaded;
/** @{ */
@@ -282,7 +283,9 @@ nt_service_body(int argc, char **argv)
return;
}
+ pubsub_install();
r = tor_init(backup_argc, backup_argv);
+
if (r) {
/* Failed to start the Tor service */
r = NT_SERVICE_ERROR_TORINIT_FAILED;
@@ -293,6 +296,8 @@ nt_service_body(int argc, char **argv)
return;
}
+ pubsub_connect();
+
/* Set the service's status to SERVICE_RUNNING and start the main
* event loop */
service_status.dwCurrentState = SERVICE_RUNNING;
@@ -321,9 +326,12 @@ nt_service_main(void)
errmsg = format_win32_error(result);
printf("Service error %d : %s\n", (int) result, errmsg);
tor_free(errmsg);
+
+ pubsub_install();
if (result == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
if (tor_init(backup_argc, backup_argv))
return;
+ pubsub_connect();
switch (get_options()->command) {
case CMD_RUN_TOR:
run_tor_main_loop();
@@ -339,6 +347,7 @@ nt_service_main(void)
"or --key-expiration) in NT service.");
break;
case CMD_RUN_UNITTESTS:
+ case CMD_IMMEDIATE:
default:
log_err(LD_CONFIG, "Illegal command number %d: internal error.",
get_options()->command);
@@ -594,7 +603,7 @@ nt_service_install(int argc, char **argv)
/* Genericity is apparently _so_ last year in Redmond, where some
* accounts are accounts that you can look up, and some accounts
* are magic and undetectable via the security subsystem. See
- * http://msdn2.microsoft.com/en-us/library/ms684188.aspx
+ * https://msdn2.microsoft.com/en-us/library/ms684188.aspx
*/
printf("Running on a Post-Win2K OS, so we'll assume that the "
"LocalService account exists.\n");
@@ -607,6 +616,7 @@ nt_service_install(int argc, char **argv)
&sidUse) == 0) {
/* XXXX For some reason, the above test segfaults. Fix that. */
printf("User \"%s\" doesn't seem to exist.\n", user_acct);
+ tor_free(command);
return -1;
} else {
printf("Will try to install service as user \"%s\".\n", user_acct);
diff --git a/src/app/main/ntmain.h b/src/app/main/ntmain.h
index c39386c054..c2d6e23da7 100644
--- a/src/app/main/ntmain.h
+++ b/src/app/main/ntmain.h
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -22,7 +22,8 @@ int nt_service_is_stopping(void);
void nt_service_set_state(DWORD state);
#else
#define nt_service_is_stopping() 0
+#define nt_service_parse_options(a, b, c) (0)
+#define nt_service_set_state(s) STMT_NIL
#endif /* defined(NT_SERVICE) */
#endif /* !defined(TOR_NTMAIN_H) */
-
diff --git a/src/app/main/risky_options.c b/src/app/main/risky_options.c
new file mode 100644
index 0000000000..747dda766b
--- /dev/null
+++ b/src/app/main/risky_options.c
@@ -0,0 +1,35 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file risky_options.c
+ * \brief List compile-time options that might make Tor less reliable.
+ **/
+
+#include "orconfig.h"
+#include "app/main/risky_options.h"
+
+/** A space-separated list of the compile-time options might make Tor less
+ * reliable or secure. These options mainly exist for testing or debugging.
+ */
+const char risky_option_list[] =
+ ""
+#ifdef DISABLE_ASSERTS_IN_TEST
+ " --disable-asserts-in-test"
+#endif
+#ifdef TOR_UNIT_TESTS
+ " TOR_UNIT_TESTS"
+#endif
+#ifdef ENABLE_RESTART_DEBUGGING
+ " --enable-restart-debugging"
+#endif
+#ifdef ALL_BUGS_ARE_FATAL
+ " --enable-all-bugs-are-fatal"
+#endif
+#ifdef DISABLE_MEMORY_SENTINELS
+ " --disable-memory-sentinels"
+#endif
+ ;
diff --git a/src/app/main/risky_options.h b/src/app/main/risky_options.h
new file mode 100644
index 0000000000..4548ae3efb
--- /dev/null
+++ b/src/app/main/risky_options.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file risky_options.h
+ * \brief Header for risky_options.c
+ **/
+
+#ifndef TOR_RISKY_OPTIONS_H
+#define TOR_RISKY_OPTIONS_H
+
+extern const char risky_option_list[];
+
+#endif
diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c
new file mode 100644
index 0000000000..4a556333db
--- /dev/null
+++ b/src/app/main/shutdown.c
@@ -0,0 +1,171 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file shutdown.c
+ * @brief Code to free global resources used by Tor.
+ *
+ * In the future, this should all be handled by the subsystem manager. */
+
+#include "core/or/or.h"
+
+#include "app/config/config.h"
+#include "app/config/statefile.h"
+#include "app/main/main.h"
+#include "app/main/shutdown.h"
+#include "app/main/subsysmgr.h"
+#include "core/mainloop/connection.h"
+#include "core/mainloop/mainloop_pubsub.h"
+#include "core/or/channeltls.h"
+#include "core/or/circuitlist.h"
+#include "core/or/circuitmux_ewma.h"
+#include "core/or/circuitpadding.h"
+#include "core/or/connection_edge.h"
+#include "core/or/dos.h"
+#include "core/or/scheduler.h"
+#include "feature/client/addressmap.h"
+#include "feature/client/bridges.h"
+#include "feature/client/entrynodes.h"
+#include "feature/client/transports.h"
+#include "feature/control/control.h"
+#include "feature/control/control_auth.h"
+#include "feature/dirauth/authmode.h"
+#include "feature/dirauth/shared_random.h"
+#include "feature/dircache/consdiffmgr.h"
+#include "feature/dircache/dirserv.h"
+#include "feature/dirparse/routerparse.h"
+#include "feature/hibernate/hibernate.h"
+#include "feature/hs/hs_common.h"
+#include "feature/nodelist/microdesc.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "feature/relay/ext_orport.h"
+#include "feature/relay/relay_config.h"
+#include "feature/rend/rendcache.h"
+#include "feature/rend/rendclient.h"
+#include "feature/stats/bwhist.h"
+#include "feature/stats/geoip_stats.h"
+#include "feature/stats/rephist.h"
+#include "lib/evloop/compat_libevent.h"
+#include "lib/geoip/geoip.h"
+
+void evdns_shutdown(int);
+
+/** Do whatever cleanup is necessary before shutting Tor down. */
+void
+tor_cleanup(void)
+{
+ const or_options_t *options = get_options();
+ if (options->command == CMD_RUN_TOR) {
+ time_t now = time(NULL);
+ /* Remove our pid file. We don't care if there was an error when we
+ * unlink, nothing we could do about it anyways. */
+ tor_remove_file(options->PidFile);
+ /* Remove control port file */
+ tor_remove_file(options->ControlPortWriteToFile);
+ /* Remove cookie authentication file */
+ {
+ char *cookie_fname = get_controller_cookie_file_name();
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
+ }
+ /* Remove Extended ORPort cookie authentication file */
+ {
+ char *cookie_fname = get_ext_or_auth_cookie_file_name();
+ if (cookie_fname)
+ tor_remove_file(cookie_fname);
+ tor_free(cookie_fname);
+ }
+ if (accounting_is_enabled(options))
+ accounting_record_bandwidth_usage(now, get_or_state());
+ or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
+ or_state_save(now);
+ if (authdir_mode(options)) {
+ sr_save_and_cleanup();
+ }
+ if (authdir_mode_tests_reachability(options))
+ rep_hist_record_mtbf_data(now, 0);
+ }
+
+ timers_shutdown();
+
+ tor_free_all(0); /* We could move tor_free_all back into the ifdef below
+ later, if it makes shutdown unacceptably slow. But for
+ now, leave it here: it's helped us catch bugs in the
+ past. */
+}
+
+/** Free all memory that we might have allocated somewhere.
+ * If <b>postfork</b>, we are a worker process and we want to free
+ * only the parts of memory that we won't touch. If !<b>postfork</b>,
+ * Tor is shutting down and we should free everything.
+ *
+ * Helps us find the real leaks with sanitizers and the like. Also valgrind
+ * should then report 0 reachable in its leak report (in an ideal world --
+ * in practice libevent, SSL, libc etc never quite free everything). */
+void
+tor_free_all(int postfork)
+{
+ if (!postfork) {
+ evdns_shutdown(1);
+ }
+ geoip_free_all();
+ geoip_stats_free_all();
+ routerlist_free_all();
+ networkstatus_free_all();
+ addressmap_free_all();
+ dirserv_free_all();
+ rend_cache_free_all();
+ rend_service_authorization_free_all();
+ rep_hist_free_all();
+ bwhist_free_all();
+ circuit_free_all();
+ circpad_machines_free();
+ entry_guards_free_all();
+ pt_free_all();
+ channel_tls_free_all();
+ channel_free_all();
+ connection_free_all();
+ connection_edge_free_all();
+ scheduler_free_all();
+ nodelist_free_all();
+ microdesc_free_all();
+ routerparse_free_all();
+ control_free_all();
+ bridges_free_all();
+ consdiffmgr_free_all();
+ hs_free_all();
+ dos_free_all();
+ circuitmux_ewma_free_all();
+ accounting_free_all();
+ circpad_free_all();
+
+ if (!postfork) {
+ config_free_all();
+ relay_config_free_all();
+ or_state_free_all();
+ }
+ if (!postfork) {
+#ifndef _WIN32
+ tor_getpwnam(NULL);
+#endif
+ }
+ /* stuff in main.c */
+
+ tor_mainloop_disconnect_pubsub();
+
+ if (!postfork) {
+ release_lockfile();
+ }
+
+ subsystems_shutdown();
+
+ /* Stuff in util.c and address.c*/
+ if (!postfork) {
+ esc_router_info(NULL);
+ }
+}
diff --git a/src/app/main/shutdown.h b/src/app/main/shutdown.h
new file mode 100644
index 0000000000..623ae9525b
--- /dev/null
+++ b/src/app/main/shutdown.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shutdown.h
+ * \brief Header file for shutdown.c.
+ **/
+
+#ifndef TOR_SHUTDOWN_H
+#define TOR_SHUTDOWN_H
+
+void tor_cleanup(void);
+void tor_free_all(int postfork);
+
+#endif /* !defined(TOR_SHUTDOWN_H) */
diff --git a/src/app/main/subsysmgr.c b/src/app/main/subsysmgr.c
new file mode 100644
index 0000000000..349803cd46
--- /dev/null
+++ b/src/app/main/subsysmgr.c
@@ -0,0 +1,478 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file subsysmgr.c
+ * @brief Manager for Tor's subsystems.
+ *
+ * This code is responsible for initializing, configuring, and shutting
+ * down all of Tor's individual subsystems.
+ **/
+
+#include "orconfig.h"
+#include "app/main/subsysmgr.h"
+
+#include "lib/confmgt/confmgt.h"
+#include "lib/dispatch/dispatch_naming.h"
+#include "lib/dispatch/msgtypes.h"
+#include "lib/err/torerr.h"
+#include "lib/log/log.h"
+#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
+#include "lib/pubsub/pubsub_build.h"
+#include "lib/pubsub/pubsub_connect.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * True iff we have checked tor_subsystems for consistency.
+ **/
+static bool subsystem_array_validated = false;
+
+/** Index value indicating that a subsystem has no options/state object, and
+ * so that object does not have an index. */
+#define IDX_NONE (-1)
+
+/**
+ * Runtime status of a single subsystem.
+ **/
+typedef struct subsys_status_t {
+ /** True if the given subsystem is initialized. */
+ bool initialized;
+ /** Index for this subsystem's options object, or IDX_NONE for none. */
+ int options_idx;
+ /** Index for this subsystem's state object, or IDX_NONE for none. */
+ int state_idx;
+} subsys_status_t;
+
+/** An overestimate of the number of subsystems. */
+#define N_SYS_STATUS 128
+/**
+ * True if a given subsystem is initialized. Expand this array if there
+ * are more than this number of subsystems. (We'd rather not
+ * dynamically allocate in this module.)
+ **/
+static subsys_status_t sys_status[N_SYS_STATUS];
+
+/** Set <b>status</b> to a default (not set-up) state. */
+static void
+subsys_status_clear(subsys_status_t *status)
+{
+ if (!status)
+ return;
+ memset(status, 0, sizeof(*status));
+ status->initialized = false;
+ status->state_idx = IDX_NONE;
+ status->options_idx = IDX_NONE;
+}
+
+/**
+ * Exit with a raw assertion if the subsystems list is inconsistent;
+ * initialize the subsystem_initialized array.
+ **/
+static void
+check_and_setup(void)
+{
+ if (subsystem_array_validated)
+ return;
+
+ raw_assert(ARRAY_LENGTH(sys_status) >= n_tor_subsystems);
+ memset(sys_status, 0, sizeof(sys_status));
+
+ int last_level = MIN_SUBSYS_LEVEL;
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (sys->level < MIN_SUBSYS_LEVEL || sys->level > MAX_SUBSYS_LEVEL) {
+ fprintf(stderr, "BUG: Subsystem %s (at %u) has an invalid level %d. "
+ "It is supposed to be between %d and %d (inclusive).\n",
+ sys->name, i, sys->level, MIN_SUBSYS_LEVEL, MAX_SUBSYS_LEVEL);
+ raw_assert_unreached_msg("There is a bug in subsystem_list.c");
+ }
+ if (sys->level < last_level) {
+ fprintf(stderr, "BUG: Subsystem %s (at #%u) is in the wrong position. "
+ "Its level is %d; but the previous subsystem's level was %d.\n",
+ sys->name, i, sys->level, last_level);
+ raw_assert_unreached_msg("There is a bug in subsystem_list.c");
+ }
+ subsys_status_clear(&sys_status[i]);
+
+ last_level = sys->level;
+ }
+
+ subsystem_array_validated = true;
+}
+
+/**
+ * Initialize all the subsystems; exit on failure.
+ **/
+int
+subsystems_init(void)
+{
+ return subsystems_init_upto(MAX_SUBSYS_LEVEL);
+}
+
+/**
+ * Initialize all the subsystems whose level is less than or equal to
+ * <b>target_level</b>; exit on failure.
+ **/
+int
+subsystems_init_upto(int target_level)
+{
+ check_and_setup();
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (sys->level > target_level)
+ break;
+ if (sys_status[i].initialized)
+ continue;
+ int r = 0;
+ if (sys->initialize) {
+ // Note that the logging subsystem is designed so that it does no harm
+ // to log a message in an uninitialized state. These messages will be
+ // discarded for now, however.
+ log_debug(LD_GENERAL, "Initializing %s", sys->name);
+ r = sys->initialize();
+ }
+ if (r < 0) {
+ fprintf(stderr, "BUG: subsystem %s (at %u) initialization failed.\n",
+ sys->name, i);
+ raw_assert_unreached_msg("A subsystem couldn't be initialized.");
+ }
+ sys_status[i].initialized = true;
+ }
+
+ return 0;
+}
+
+/**
+ * Add publish/subscribe relationships to <b>builder</b> for all
+ * initialized subsystems of level no more than <b>target_level</b>.
+ **/
+int
+subsystems_add_pubsub_upto(pubsub_builder_t *builder,
+ int target_level)
+{
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (sys->level > target_level)
+ break;
+ if (! sys_status[i].initialized)
+ continue;
+ int r = 0;
+ if (sys->add_pubsub) {
+ subsys_id_t sysid = get_subsys_id(sys->name);
+ raw_assert(sysid != ERROR_ID);
+ pubsub_connector_t *connector;
+ connector = pubsub_connector_for_subsystem(builder, sysid);
+ r = sys->add_pubsub(connector);
+ pubsub_connector_free(connector);
+ }
+ if (r < 0) {
+ fprintf(stderr, "BUG: subsystem %s (at %u) could not connect to "
+ "publish/subscribe system.", sys->name, sys->level);
+ raw_assert_unreached_msg("A subsystem couldn't be connected.");
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Add publish/subscribe relationships to <b>builder</b> for all
+ * initialized subsystems.
+ **/
+int
+subsystems_add_pubsub(pubsub_builder_t *builder)
+{
+ return subsystems_add_pubsub_upto(builder, MAX_SUBSYS_LEVEL);
+}
+
+/**
+ * Shut down all the subsystems.
+ **/
+void
+subsystems_shutdown(void)
+{
+ subsystems_shutdown_downto(MIN_SUBSYS_LEVEL - 1);
+}
+
+/**
+ * Shut down all the subsystems whose level is above <b>target_level</b>.
+ **/
+void
+subsystems_shutdown_downto(int target_level)
+{
+ check_and_setup();
+
+ for (int i = (int)n_tor_subsystems - 1; i >= 0; --i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (sys->level <= target_level)
+ break;
+ if (! sys_status[i].initialized)
+ continue;
+ if (sys->shutdown) {
+ log_debug(LD_GENERAL, "Shutting down %s", sys->name);
+ sys->shutdown();
+ }
+ subsys_status_clear(&sys_status[i]);
+ }
+}
+
+/**
+ * Run pre-fork code on all subsystems that declare any
+ **/
+void
+subsystems_prefork(void)
+{
+ check_and_setup();
+
+ for (int i = (int)n_tor_subsystems - 1; i >= 0; --i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (! sys_status[i].initialized)
+ continue;
+ if (sys->prefork) {
+ log_debug(LD_GENERAL, "Pre-fork: %s", sys->name);
+ sys->prefork();
+ }
+ }
+}
+
+/**
+ * Run post-fork code on all subsystems that declare any
+ **/
+void
+subsystems_postfork(void)
+{
+ check_and_setup();
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (! sys_status[i].initialized)
+ continue;
+ if (sys->postfork) {
+ log_debug(LD_GENERAL, "Post-fork: %s", sys->name);
+ sys->postfork();
+ }
+ }
+}
+
+/**
+ * Run thread-cleanup code on all subsystems that declare any
+ **/
+void
+subsystems_thread_cleanup(void)
+{
+ check_and_setup();
+
+ for (int i = (int)n_tor_subsystems - 1; i >= 0; --i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (!sys->supported)
+ continue;
+ if (! sys_status[i].initialized)
+ continue;
+ if (sys->thread_cleanup) {
+ log_debug(LD_GENERAL, "Thread cleanup: %s", sys->name);
+ sys->thread_cleanup();
+ }
+ }
+}
+
+/**
+ * Dump a human- and machine-readable list of all the subsystems to stdout,
+ * in their initialization order, prefixed with their level.
+ **/
+void
+subsystems_dump_list(void)
+{
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ printf("% 4d\t%16s\t%s\n", sys->level, sys->name,
+ sys->location?sys->location:"");
+ }
+}
+
+/**
+ * Register all subsystem-declared options formats in <b>mgr</b>.
+ *
+ * Return 0 on success, -1 on failure.
+ **/
+int
+subsystems_register_options_formats(config_mgr_t *mgr)
+{
+ tor_assert(mgr);
+ check_and_setup();
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (sys->options_format) {
+ int options_idx = config_mgr_add_format(mgr, sys->options_format);
+ sys_status[i].options_idx = options_idx;
+ log_debug(LD_CONFIG, "Added options format for %s with index %d",
+ sys->name, options_idx);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Register all subsystem-declared state formats in <b>mgr</b>.
+ *
+ * Return 0 on success, -1 on failure.
+ **/
+int
+subsystems_register_state_formats(config_mgr_t *mgr)
+{
+ tor_assert(mgr);
+ check_and_setup();
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (sys->state_format) {
+ int state_idx = config_mgr_add_format(mgr, sys->state_format);
+ sys_status[i].state_idx = state_idx;
+ log_debug(LD_CONFIG, "Added state format for %s with index %d",
+ sys->name, state_idx);
+ }
+ }
+ return 0;
+}
+
+#ifdef TOR_UNIT_TESTS
+/**
+ * Helper: look up the index for <b>sys</b>. Return -1 if the subsystem
+ * is not recognized.
+ **/
+static int
+subsys_get_idx(const subsys_fns_t *sys)
+{
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ if (sys == tor_subsystems[i])
+ return (int)i;
+ }
+ return -1;
+}
+
+/**
+ * Return the current state-manager's index for any state held by the
+ * subsystem <b>sys</b>. If <b>sys</b> has no options, return -1.
+ *
+ * Using raw indices can be error-prone: only do this from the unit
+ * tests. If you need a way to access another subsystem's configuration,
+ * that subsystem should provide access functions.
+ **/
+int
+subsystems_get_options_idx(const subsys_fns_t *sys)
+{
+ int i = subsys_get_idx(sys);
+ tor_assert(i >= 0);
+ return sys_status[i].options_idx;
+}
+
+/**
+ * Return the current state-manager's index for any state held by the
+ * subsystem <b>sys</b>. If <b>sys</b> has no state, return -1.
+ *
+ * Using raw indices can be error-prone: only do this from the unit
+ * tests. If you need a way to access another subsystem's state
+ * that subsystem should provide access functions.
+ **/
+int
+subsystems_get_state_idx(const subsys_fns_t *sys)
+{
+ int i = subsys_get_idx(sys);
+ tor_assert(i >= 0);
+ return sys_status[i].state_idx;
+}
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/**
+ * Call all appropriate set_options() methods to tell the various subsystems
+ * about a new set of torrc options. Return 0 on success, -1 on
+ * nonrecoverable failure.
+ **/
+int
+subsystems_set_options(const config_mgr_t *mgr, struct or_options_t *options)
+{
+ /* XXXX This does not yet handle reversible option assignment; I'll
+ * do that later in this branch. */
+
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (sys_status[i].options_idx >= 0 && sys->set_options) {
+ void *obj = config_mgr_get_obj_mutable(mgr, options,
+ sys_status[i].options_idx);
+ int rv = sys->set_options(obj);
+ if (rv < 0) {
+ log_err(LD_CONFIG, "Error when handling option for %s; "
+ "cannot proceed.", sys->name);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Call all appropriate set_state() methods to tell the various subsystems
+ * about an initial DataDir/state file. Return 0 on success, -1 on
+ * nonrecoverable failure.
+ **/
+int
+subsystems_set_state(const config_mgr_t *mgr, struct or_state_t *state)
+{
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (sys_status[i].state_idx >= 0 && sys->set_state) {
+ void *obj = config_mgr_get_obj_mutable(mgr, state,
+ sys_status[i].state_idx);
+ int rv = sys->set_state(obj);
+ if (rv < 0) {
+ log_err(LD_CONFIG, "Error when handling state for %s; "
+ "cannot proceed.", sys->name);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Call all appropriate flush_state() methods to tell the various subsystems
+ * to update the state objects in <b>state</b>. Return 0 on success,
+ * -1 on failure.
+ **/
+int
+subsystems_flush_state(const config_mgr_t *mgr, struct or_state_t *state)
+{
+ int result = 0;
+ for (unsigned i = 0; i < n_tor_subsystems; ++i) {
+ const subsys_fns_t *sys = tor_subsystems[i];
+ if (sys_status[i].state_idx >= 0 && sys->flush_state) {
+ void *obj = config_mgr_get_obj_mutable(mgr, state,
+ sys_status[i].state_idx);
+ int rv = sys->flush_state(obj);
+ if (rv < 0) {
+ log_warn(LD_CONFIG, "Error when flushing state to state object for %s",
+ sys->name);
+ result = -1;
+ }
+ }
+ }
+ return result;
+}
diff --git a/src/app/main/subsysmgr.h b/src/app/main/subsysmgr.h
new file mode 100644
index 0000000000..ae0b3df469
--- /dev/null
+++ b/src/app/main/subsysmgr.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file subsysmgr.h
+ * @brief Header for subsysmgr.c
+ **/
+
+#ifndef TOR_SUBSYSMGR_T
+#define TOR_SUBSYSMGR_T
+
+#include "lib/subsys/subsys.h"
+
+extern const struct subsys_fns_t *tor_subsystems[];
+extern const unsigned n_tor_subsystems;
+
+int subsystems_init(void);
+int subsystems_init_upto(int level);
+
+struct pubsub_builder_t;
+int subsystems_add_pubsub_upto(struct pubsub_builder_t *builder,
+ int target_level);
+int subsystems_add_pubsub(struct pubsub_builder_t *builder);
+
+void subsystems_shutdown(void);
+void subsystems_shutdown_downto(int level);
+
+void subsystems_prefork(void);
+void subsystems_postfork(void);
+void subsystems_thread_cleanup(void);
+
+void subsystems_dump_list(void);
+
+struct config_mgr_t;
+int subsystems_register_options_formats(struct config_mgr_t *mgr);
+int subsystems_register_state_formats(struct config_mgr_t *mgr);
+struct or_options_t;
+struct or_state_t;
+int subsystems_set_options(const struct config_mgr_t *mgr,
+ struct or_options_t *options);
+int subsystems_set_state(const struct config_mgr_t *mgr,
+ struct or_state_t *state);
+int subsystems_flush_state(const struct config_mgr_t *mgr,
+ struct or_state_t *state);
+
+#ifdef TOR_UNIT_TESTS
+int subsystems_get_options_idx(const subsys_fns_t *sys);
+int subsystems_get_state_idx(const subsys_fns_t *sys);
+#endif
+
+#endif /* !defined(TOR_SUBSYSMGR_T) */
diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c
new file mode 100644
index 0000000000..cb79909e69
--- /dev/null
+++ b/src/app/main/subsystem_list.c
@@ -0,0 +1,77 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file subsystem_list.c
+ * @brief List of Tor's subsystems.
+ **/
+
+#include "orconfig.h"
+#include "app/main/subsysmgr.h"
+#include "lib/cc/compat_compiler.h"
+#include "lib/cc/torint.h"
+
+#include "core/mainloop/mainloop_sys.h"
+#include "core/or/or_sys.h"
+#include "feature/control/btrack_sys.h"
+#include "lib/compress/compress_sys.h"
+#include "lib/crypt_ops/crypto_sys.h"
+#include "lib/err/torerr_sys.h"
+#include "lib/log/log_sys.h"
+#include "lib/net/network_sys.h"
+#include "lib/process/process_sys.h"
+#include "lib/llharden/winprocess_sys.h"
+#include "lib/thread/thread_sys.h"
+#include "lib/time/time_sys.h"
+#include "lib/tls/tortls_sys.h"
+#include "lib/trace/trace_sys.h"
+#include "lib/wallclock/wallclock_sys.h"
+#include "lib/evloop/evloop_sys.h"
+
+#include "feature/dirauth/dirauth_sys.h"
+#include "feature/hs/hs_sys.h"
+#include "feature/metrics/metrics_sys.h"
+#include "feature/relay/relay_sys.h"
+
+#include <stddef.h>
+
+/**
+ * Global list of the subsystems in Tor, in the order of their initialization.
+ * Want to know the exact level numbers?
+ * We'll implement a level dump command in #31614.
+ **/
+const subsys_fns_t *tor_subsystems[] = {
+ &sys_winprocess,
+ &sys_torerr,
+
+ &sys_wallclock,
+ &sys_logging,
+ &sys_threads,
+
+ &sys_tracing,
+
+ &sys_time,
+
+ &sys_crypto,
+ &sys_compress,
+ &sys_network,
+ &sys_tortls,
+
+ &sys_evloop,
+ &sys_process,
+
+ &sys_mainloop,
+ &sys_or,
+
+ &sys_relay,
+ &sys_hs,
+
+ &sys_btrack,
+
+ &sys_dirauth,
+ &sys_metrics,
+};
+
+const unsigned n_tor_subsystems = ARRAY_LENGTH(tor_subsystems);
diff --git a/src/app/main/tor_main.c b/src/app/main/tor_main.c
index 8a887ed269..0ee03fd5e9 100644
--- a/src/app/main/tor_main.c
+++ b/src/app/main/tor_main.c
@@ -1,6 +1,6 @@
/* Copyright 2001-2004 Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "orconfig.h"