Commit c7ce6b98 authored by Nick Mathewson's avatar Nick Mathewson 🎨
Browse files

Split main.c into main.c and mainloop.c

The main.c code is responsible for initialization and shutdown;
the mainloop.c code is responsible for running the main loop of Tor.

Splitting the "generic event loop" part of mainloop.c from the
event-loop-specific part is not done as part of this patch.
parent 98ef3e82
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
......@@ -96,7 +95,8 @@
#include "lib/log/git_revision.h"
#include "feature/stats/geoip.h"
#include "feature/hibernate/hibernate.h"
#include "core/mainloop/main.h"
#include "app/main/main.h"
#include "core/mainloop/mainloop.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "core/or/policies.h"
......
......@@ -33,11 +33,11 @@
#include "core/or/circuitstats.h"
#include "app/config/config.h"
#include "app/config/confparse.h"
#include "core/mainloop/mainloop.h"
#include "core/mainloop/connection.h"
#include "feature/control/control.h"
#include "feature/client/entrynodes.h"
#include "feature/hibernate/hibernate.h"
#include "core/mainloop/main.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "lib/sandbox/sandbox.h"
......
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file main.c
* \brief Invocation module. Initializes subsystems and runs the main loop.
**/
#include "core/or/or.h"
#include "feature/client/addressmap.h"
#include "lib/err/backtrace.h"
#include "feature/client/bridges.h"
#include "lib/container/buffers.h"
#include "core/or/channel.h"
#include "core/or/channeltls.h"
#include "core/or/channelpadding.h"
#include "core/or/circuitlist.h"
#include "core/or/circuitmux_ewma.h"
#include "core/or/command.h"
#include "lib/compress/compress.h"
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
#include "feature/dircache/consdiffmgr.h"
#include "feature/control/control.h"
#include "core/mainloop/cpuworker.h"
#include "lib/crypt_ops/crypto_s2k.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/dircache/dirserv.h"
#include "feature/dirauth/bwauth.h"
#include "feature/dirauth/process_descs.h"
#include "feature/relay/dns.h"
#include "feature/client/entrynodes.h"
#include "feature/stats/geoip.h"
#include "feature/hibernate/hibernate.h"
#include "feature/hs/hs_cache.h"
#include "feature/dirauth/keypin.h"
#include "app/main/main.h"
#include "core/mainloop/mainloop.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
#include "app/main/ntmain.h"
#include "feature/relay/onion_queue.h"
#include "core/or/policies.h"
#include "core/or/protover.h"
#include "feature/client/transports.h"
#include "core/or/relay.h"
#include "feature/rend/rendcache.h"
#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
#include "feature/stats/rephist.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
#include "feature/nodelist/authcert.h"
#include "feature/nodelist/routerlist.h"
#include "feature/nodelist/routerparse.h"
#include "core/or/scheduler.h"
#include "app/config/statefile.h"
#include "core/or/status.h"
#include "feature/api/tor_api.h"
#include "feature/api/tor_api_internal.h"
#include "lib/process/waitpid.h"
#include "feature/relay/ext_orport.h"
#include "lib/meminfo/meminfo.h"
#include "lib/osinfo/uname.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 <event2/event.h>
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/mode.h"
#include "feature/dirauth/shared_random.h"
#include "core/or/or_connection_st.h"
#include "core/or/port_cfg_st.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYSTEMD
# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__)
/* Systemd's use of gcc's __INCLUDE_LEVEL__ extension macro appears to confuse
* Coverity. Here's a kludge to unconfuse it.
*/
# define __INCLUDE_LEVEL__ 2
#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */
#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
void rust_log_welcome_string(void);
#endif
/********* PROTOTYPES **********/
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
do_hup(void)
{
const or_options_t *options = get_options();
log_notice(LD_GENERAL,"Received reload signal (hup). Reloading config and "
"resetting internal state.");
if (accounting_is_enabled(options))
accounting_record_bandwidth_usage(time(NULL), get_or_state());
router_reset_warnings();
routerlist_reset_warnings();
/* first, reload config variables, in case they've changed */
if (options->ReloadTorrcOnSIGHUP) {
/* no need to provide argc/v, they've been cached in init_from_config */
int init_rv = options_init_from_torrc(0, NULL);
if (init_rv < 0) {
log_err(LD_CONFIG,"Reading config failed--see warnings above. "
"For usage, try -h.");
return -1;
} else if (BUG(init_rv > 0)) {
// LCOV_EXCL_START
/* This should be impossible: the only "return 1" cases in
* options_init_from_torrc are ones caused by command-line arguments;
* but they can't change while Tor is running. */
return -1;
// LCOV_EXCL_STOP
}
options = get_options(); /* they have changed now */
/* Logs are only truncated the first time they are opened, but were
probably intended to be cleaned up on signal. */
if (options->TruncateLogFile)
truncate_logs();
} else {
char *msg = NULL;
log_notice(LD_GENERAL, "Not reloading config file: the controller told "
"us not to.");
/* Make stuff get rescanned, reloaded, etc. */
if (set_options((or_options_t*)options, &msg) < 0) {
if (!msg)
msg = tor_strdup("Unknown error");
log_warn(LD_GENERAL, "Unable to re-set previous options: %s", msg);
tor_free(msg);
}
}
if (authdir_mode(options)) {
/* reload the approved-routers file */
if (dirserv_load_fingerprint_file() < 0) {
/* warnings are logged from dirserv_load_fingerprint_file() directly */
log_info(LD_GENERAL, "Error reloading fingerprints. "
"Continuing with old list.");
}
}
/* Rotate away from the old dirty circuits. This has to be done
* after we've read the new options, but before we start using
* circuits for directory fetches. */
circuit_mark_all_dirty_circs_as_unusable();
/* retry appropriate downloads */
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
if (!net_is_disabled())
update_networkstatus_downloads(time(NULL));
/* We'll retry routerstatus downloads in about 10 seconds; no need to
* force a retry there. */
if (server_mode(options)) {
/* Maybe we've been given a new ed25519 key or certificate?
*/
time_t now = approx_time();
int new_signing_key = load_ed_keys(options, now);
if (new_signing_key < 0 ||
generate_ed_link_cert(options, now, new_signing_key > 0)) {
log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys.");
}
/* Update cpuworker and dnsworker processes, so they get up-to-date
* configuration options. */
cpuworkers_rotate_keyinfo();
dns_reset();
}
return 0;
}
/** Libevent callback: invoked when we get a signal.
*/
static void
signal_callback(evutil_socket_t fd, short events, void *arg)
{
const int *sigptr = arg;
const int sig = *sigptr;
(void)fd;
(void)events;
update_current_time(time(NULL));
process_signal(sig);
}
/** Do the work of acting on a signal received in <b>sig</b> */
static void
process_signal(int sig)
{
switch (sig)
{
case SIGTERM:
log_notice(LD_GENERAL,"Catching signal TERM, exiting cleanly.");
tor_shutdown_event_loop_and_exit(0);
break;
case SIGINT:
if (!server_mode(get_options())) { /* do it now */
log_notice(LD_GENERAL,"Interrupt: exiting cleanly.");
tor_shutdown_event_loop_and_exit(0);
return;
}
#ifdef HAVE_SYSTEMD
sd_notify(0, "STOPPING=1");
#endif
hibernate_begin_shutdown();
break;
#ifdef SIGPIPE
case SIGPIPE:
log_debug(LD_GENERAL,"Caught SIGPIPE. Ignoring.");
break;
#endif
case SIGUSR1:
/* prefer to log it at INFO, but make sure we always see it */
dumpstats(get_min_log_level()<LOG_INFO ? get_min_log_level() : LOG_INFO);
control_event_signal(sig);
break;
case SIGUSR2:
switch_logs_debug();
log_debug(LD_GENERAL,"Caught USR2, going to loglevel debug. "
"Send HUP to change back.");
control_event_signal(sig);
break;
case SIGHUP:
#ifdef HAVE_SYSTEMD
sd_notify(0, "RELOADING=1");
#endif
if (do_hup() < 0) {
log_warn(LD_CONFIG,"Restart failed (config error?). Exiting.");
tor_shutdown_event_loop_and_exit(1);
return;
}
#ifdef HAVE_SYSTEMD
sd_notify(0, "READY=1");
#endif
control_event_signal(sig);
break;
#ifdef SIGCHLD
case SIGCHLD:
notify_pending_waitpid_callbacks();
break;
#endif
case SIGNEWNYM: {
do_signewnym(time(NULL));
break;
}
case SIGCLEARDNSCACHE:
addressmap_clear_transient();
control_event_signal(sig);
break;
case SIGHEARTBEAT:
log_heartbeat(time(NULL));
control_event_signal(sig);
break;
}
}
/**
* Write current memory usage information to the log.
*/
static void
dumpmemusage(int severity)
{
connection_dump_buffer_mem_stats(severity);
tor_log(severity, LD_GENERAL, "In rephist: %"PRIu64" used by %d Tors.",
(rephist_total_alloc), rephist_total_num);
dump_routerlist_mem_usage(severity);
dump_cell_pool_usage(severity);
dump_dns_mem_usage(severity);
tor_log_mallinfo(severity);
}
/** Write all statistics to the log, with log level <b>severity</b>. Called
* in response to a SIGUSR1. */
static void
dumpstats(int severity)
{
time_t now = time(NULL);
time_t elapsed;
size_t rbuf_cap, wbuf_cap, rbuf_len, wbuf_len;
tor_log(severity, LD_GENERAL, "Dumping stats:");
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),
(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),
(int)buf_allocation(conn->inbuf),
(int)(now - conn->timestamp_last_read_allowed));
tor_log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on outbuf "
"(len %d, last written %d secs ago)",i,
(int)connection_get_outbuf_len(conn),
(int)buf_allocation(conn->outbuf),
(int)(now - conn->timestamp_last_write_allowed));
if (conn->type == CONN_TYPE_OR) {
or_connection_t *or_conn = TO_OR_CONN(conn);
if (or_conn->tls) {
if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
&wbuf_cap, &wbuf_len) == 0) {
tor_log(severity, LD_GENERAL,
"Conn %d: %d/%d bytes used on OpenSSL read buffer; "
"%d/%d bytes used on write buffer.",
i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
}
}
}
}
circuit_dump_by_conn(conn, severity); /* dump info about all the circuits
* using this conn */
} SMARTLIST_FOREACH_END(conn);
channel_dumpstats(severity);
channel_listener_dumpstats(severity);
tor_log(severity, LD_NET,
"Cells processed: %"PRIu64" padding\n"
" %"PRIu64" create\n"
" %"PRIu64" created\n"
" %"PRIu64" relay\n"
" (%"PRIu64" relayed)\n"
" (%"PRIu64" delivered)\n"
" %"PRIu64" destroy",
(stats_n_padding_cells_processed),
(stats_n_create_cells_processed),
(stats_n_created_cells_processed),
(stats_n_relay_cells_processed),
(stats_n_relay_cells_relayed),
(stats_n_relay_cells_delivered),
(stats_n_destroy_cells_processed));
if (stats_n_data_cells_packaged)
tor_log(severity,LD_NET,"Average packaged cell fullness: %2.3f%%",
100*(((double)stats_n_data_bytes_packaged) /
((double)stats_n_data_cells_packaged*RELAY_PAYLOAD_SIZE)) );
if (stats_n_data_cells_received)
tor_log(severity,LD_NET,"Average delivered cell fullness: %2.3f%%",
100*(((double)stats_n_data_bytes_received) /
((double)stats_n_data_cells_received*RELAY_PAYLOAD_SIZE)) );
cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_TAP, "TAP");
cpuworker_log_onionskin_overhead(severity, ONION_HANDSHAKE_TYPE_NTOR,"ntor");
if (now - time_of_process_start >= 0)
elapsed = now - time_of_process_start;
else
elapsed = 0;
if (elapsed) {
tor_log(severity, LD_NET,
"Average bandwidth: %"PRIu64"/%d = %d bytes/sec reading",
(get_bytes_read()),
(int)elapsed,
(int) (get_bytes_read()/elapsed));
tor_log(severity, LD_NET,
"Average bandwidth: %"PRIu64"/%d = %d bytes/sec writing",
(get_bytes_written()),
(int)elapsed,
(int) (get_bytes_written()/elapsed));
}
tor_log(severity, LD_NET, "--------------- Dumping memory information:");
dumpmemusage(severity);
rep_hist_dump_stats(now,severity);
rend_service_dump_stats(severity);
dump_distinct_digest_count(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
}
#ifdef _WIN32
#define UNIX_ONLY 0
#else
#define UNIX_ONLY 1
#endif
static struct {
/** A numeric code for this signal. Must match the signal value if
* try_to_register is true. */
int signal_value;
/** True if we should try to register this signal with libevent and catch
* corresponding posix signals. False otherwise. */
int try_to_register;
/** Pointer to hold the event object constructed for this signal. */
struct event *signal_event;
} signal_handlers[] = {
#ifdef SIGINT
{ SIGINT, UNIX_ONLY, NULL }, /* do a controlled slow shutdown */
#endif
#ifdef SIGTERM
{ SIGTERM, UNIX_ONLY, NULL }, /* to terminate now */
#endif
#ifdef SIGPIPE
{ SIGPIPE, UNIX_ONLY, NULL }, /* otherwise SIGPIPE kills us */
#endif
#ifdef SIGUSR1
{ SIGUSR1, UNIX_ONLY, NULL }, /* dump stats */
#endif
#ifdef SIGUSR2
{ SIGUSR2, UNIX_ONLY, NULL }, /* go to loglevel debug */
#endif
#ifdef SIGHUP
{ SIGHUP, UNIX_ONLY, NULL }, /* to reload config, retry conns, etc */
#endif
#ifdef SIGXFSZ
{ SIGXFSZ, UNIX_ONLY, NULL }, /* handle file-too-big resource exhaustion */
#endif
#ifdef SIGCHLD
{ SIGCHLD, UNIX_ONLY, NULL }, /* handle dns/cpu workers that exit */
#endif
/* These are controller-only */
{ SIGNEWNYM, 0, NULL },
{ SIGCLEARDNSCACHE, 0, NULL },
{ SIGHEARTBEAT, 0, NULL },
{ -1, -1, NULL }
};
/** Set up the signal handler events for this process, and register them
* with libevent if appropriate. */
void
handle_signals(void)
{
int i;
const int enabled = !get_options()->DisableSignalHandlers;
for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
/* Signal handlers are only registered with libevent if they need to catch
* real POSIX signals. We construct these signal handler events in either
* case, though, so that controllers can activate them with the SIGNAL
* command.
*/
if (enabled && signal_handlers[i].try_to_register) {
signal_handlers[i].signal_event =
tor_evsignal_new(tor_libevent_get_base(),
signal_handlers[i].signal_value,
signal_callback,
&signal_handlers[i].signal_value);
if (event_add(signal_handlers[i].signal_event, NULL))
log_warn(LD_BUG, "Error from libevent when adding "
"event for signal %d",
signal_handlers[i].signal_value);
} else {
signal_handlers[i].signal_event =
tor_event_new(tor_libevent_get_base(), -1,
EV_SIGNAL, signal_callback,
&signal_handlers[i].signal_value);
}
}
}
/* Cause the signal handler for signal_num to be called in the event loop. */
void
activate_signal(int signal_num)
{
int i;
for (i = 0; signal_handlers[i].signal_value >= 0; ++i) {
if (signal_handlers[i].signal_value == signal_num) {
event_active(signal_handlers[i].signal_event, EV_SIGNAL, 1);
return;
}
}
}
/** Main entry point for the Tor command-line client. Return 0 on "success",
* negative on "failure", and positive on "success and exit".
*/
int
tor_init(int argc, char *argv[])
{
char progname[256];
int quiet = 0;
time_of_process_start = time(NULL);
tor_init_connection_lists();
/* Have the log set up with our application name. */
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();
/* 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, "--hash-password") ||
!strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
if (quiet < 1)
quiet = 1;
}
}
config_free_lines(opts);
config_free_lines(cmdline_opts);
}
/* 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);
}
quiet_level = quiet;
{