/** Write the persistent state to disk. Return 0 for success, <0 on failure. */ int or_state_save(time_t now) { char *state, *contents; char tbuf[ISO_TIME_LEN+1]; char *fname; tor_assert(global_state); if (global_state->next_write > now) return 0; /* Call everything else that might dirty the state even more, in order * to avoid redundant writes. */ entry_guards_update_state(global_state); rep_hist_update_state(global_state); circuit_build_times_update_state(&circ_times, global_state); if (accounting_is_enabled(get_options())) accounting_run_housekeeping(now); global_state->LastWritten = now; tor_free(global_state->TorVersion); tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); state = config_dump(&state_format, NULL, global_state, 1, 0); format_local_iso_time(tbuf, now); tor_asprintf(&contents, "# Tor state file last generated on %s local time\n" "# Other times below are in GMT\n" "# You *do not* need to edit this file.\n\n%s", tbuf, state); tor_free(state); fname = get_datadir_fname("state"); if (write_str_to_file(fname, contents, 0)<0) { log_warn(LD_FS, "Unable to write state to file \"%s\"; " "will try again later", fname); last_state_file_write_failed = 1; tor_free(fname); tor_free(contents); /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state * changes sooner). */ global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL; return -1; } last_state_file_write_failed = 0; log_info(LD_GENERAL, "Saved state to \"%s\"", fname); tor_free(fname); tor_free(contents); if (server_mode(get_options())) global_state->next_write = now + STATE_RELAY_CHECKPOINT_INTERVAL; else global_state->next_write = TIME_MAX; return 0; }
/** Consider our environment and decide if it's time * to start/stop hibernating. */ void consider_hibernation(time_t now) { int accounting_enabled = get_options()->AccountingMax != 0; char buf[ISO_TIME_LEN+1]; /* If we're in 'exiting' mode, then we just shut down after the interval * elapses. */ if (hibernate_state == HIBERNATE_STATE_EXITING) { tor_assert(shutdown_time); if (shutdown_time <= now) { log_notice(LD_GENERAL, "Clean shutdown finished. Exiting."); tor_cleanup(); exit(0); } return; /* if exiting soon, don't worry about bandwidth limits */ } if (hibernate_state == HIBERNATE_STATE_DORMANT) { /* We've been hibernating because of bandwidth accounting. */ tor_assert(hibernate_end_time); if (hibernate_end_time > now && accounting_enabled) { /* If we're hibernating, don't wake up until it's time, regardless of * whether we're in a new interval. */ return ; } else { hibernate_end_time_elapsed(now); } } /* Else, we aren't hibernating. See if it's time to start hibernating, or to * go dormant. */ if (hibernate_state == HIBERNATE_STATE_LIVE) { if (hibernate_soft_limit_reached()) { log_notice(LD_ACCT, "Bandwidth soft limit reached; commencing hibernation."); hibernate_begin(HIBERNATE_STATE_LOWBANDWIDTH, now); } else if (accounting_enabled && now < interval_wakeup_time) { format_local_iso_time(buf,interval_wakeup_time); log_notice(LD_ACCT, "Commencing hibernation. We will wake up at %s local time.", buf); hibernate_go_dormant(now); } } if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH) { if (!accounting_enabled) { hibernate_end_time_elapsed(now); } else if (hibernate_hard_limit_reached()) { hibernate_go_dormant(now); } else if (hibernate_end_time <= now) { /* The hibernation period ended while we were still in lowbandwidth.*/ hibernate_end_time_elapsed(now); } } }
/** * A circuit was just forcibly closed. If there has been no recent network * activity at all, but this circuit was launched back when we thought the * network was live, increment the number of "nonlive" circuit timeouts. * * This is used by circuit_build_times_network_check_live() to decide * if we should record the circuit build timeout or not. */ static void circuit_build_times_network_close(circuit_build_times_t *cbt, int did_onehop, time_t start_time) { time_t now = time(NULL); /* * Check if this is a timeout that was for a circuit that spent its * entire existence during a time where we have had no network activity. */ if (cbt->liveness.network_last_live < start_time) { if (did_onehop) { char last_live_buf[ISO_TIME_LEN+1]; char start_time_buf[ISO_TIME_LEN+1]; char now_buf[ISO_TIME_LEN+1]; format_local_iso_time(last_live_buf, cbt->liveness.network_last_live); format_local_iso_time(start_time_buf, start_time); format_local_iso_time(now_buf, now); log_warn(LD_BUG, "Circuit somehow completed a hop while the network was " "not live. Network was last live at %s, but circuit launched " "at %s. It's now %s.", last_live_buf, start_time_buf, now_buf); } cbt->liveness.nonlive_timeouts++; if (cbt->liveness.nonlive_timeouts == 1) { log_notice(LD_CIRC, "Tor has not observed any network activity for the past %d " "seconds. Disabling circuit build timeout recording.", (int)(now - cbt->liveness.network_last_live)); } else { log_info(LD_CIRC, "Got non-live timeout. Current count is: %d", cbt->liveness.nonlive_timeouts); } } }
/** * Log when a certificate, <b>cert</b>, with some <b>description</b> and * stored in a file named <b>fname</b>, is going to expire. */ static void log_ed_cert_expiration(const tor_cert_t *cert, const char *description, const char *fname) { char expiration[ISO_TIME_LEN+1]; if (BUG(!cert)) { /* If the specified key hasn't been loaded */ log_warn(LD_OR, "No %s key loaded; can't get certificate expiration.", description); } else { format_local_iso_time(expiration, cert->valid_until); log_notice(LD_OR, "The %s certificate stored in %s is valid until %s.", description, fname, expiration); print_cert_expiration(expiration, description); } }
/* Save the disk state to disk but before that update it from the current * state so we always have the latest. Return 0 on success else -1. */ static int disk_state_save_to_disk(void) { int ret; char *state, *content = NULL, *fname = NULL; char tbuf[ISO_TIME_LEN + 1]; time_t now = time(NULL); /* If we didn't have the opportunity to setup an internal disk state, * don't bother saving something to disk. */ if (sr_disk_state == NULL) { ret = 0; goto done; } /* Make sure that our disk state is up to date with our memory state * before saving it to disk. */ disk_state_update(); state = config_dump(&state_format, NULL, sr_disk_state, 0, 0); format_local_iso_time(tbuf, now); tor_asprintf(&content, "# Tor shared random state file last generated on %s " "local time\n" "# Other times below are in UTC\n" "# Please *do not* edit this file.\n\n%s", tbuf, state); tor_free(state); fname = get_datadir_fname(default_fname); if (write_str_to_file(fname, content, 0) < 0) { log_warn(LD_FS, "SR: Unable to write SR state to file %s", fname); ret = -1; goto done; } ret = 0; log_debug(LD_DIR, "SR: Saved state to file %s", fname); done: tor_free(fname); tor_free(content); return ret; }
/** Based on our interval and our estimated bandwidth, choose a * deterministic (but random-ish) time to wake up. */ static void accounting_set_wakeup_time(void) { char buf[ISO_TIME_LEN+1]; char digest[DIGEST_LEN]; crypto_digest_env_t *d_env; int time_in_interval; uint64_t time_to_exhaust_bw; int time_to_consider; if (! identity_key_is_set()) { if (init_keys() < 0) { log_err(LD_BUG, "Error initializing keys"); tor_assert(0); } } format_iso_time(buf, interval_start_time); crypto_pk_get_digest(get_identity_key(), digest); d_env = crypto_new_digest_env(); crypto_digest_add_bytes(d_env, buf, ISO_TIME_LEN); crypto_digest_add_bytes(d_env, digest, DIGEST_LEN); crypto_digest_get_digest(d_env, digest, DIGEST_LEN); crypto_free_digest_env(d_env); if (!expected_bandwidth_usage) { char buf1[ISO_TIME_LEN+1]; char buf2[ISO_TIME_LEN+1]; format_local_iso_time(buf1, interval_start_time); format_local_iso_time(buf2, interval_end_time); time_to_exhaust_bw = GUESS_TIME_TO_USE_BANDWIDTH; interval_wakeup_time = interval_start_time; log_notice(LD_ACCT, "Configured hibernation. This interval begins at %s " "and ends at %s. We have no prior estimate for bandwidth, so " "we will start out awake and hibernate when we exhaust our quota.", buf1, buf2); return; } time_in_interval = (int)(interval_end_time - interval_start_time); time_to_exhaust_bw = (get_options()->AccountingMax/expected_bandwidth_usage)*60; if (time_to_exhaust_bw > TIME_MAX) { time_to_exhaust_bw = TIME_MAX; time_to_consider = 0; } else { time_to_consider = time_in_interval - (int)time_to_exhaust_bw; } if (time_to_consider<=0) { interval_wakeup_time = interval_start_time; } else { /* XXX can we simplify this just by picking a random (non-deterministic) * time to be up? If we go down and come up, then we pick a new one. Is * that good enough? -RD */ /* This is not a perfectly unbiased conversion, but it is good enough: * in the worst case, the first half of the day is 0.06 percent likelier * to be chosen than the last half. */ interval_wakeup_time = interval_start_time + (get_uint32(digest) % time_to_consider); format_iso_time(buf, interval_wakeup_time); } { char buf1[ISO_TIME_LEN+1]; char buf2[ISO_TIME_LEN+1]; char buf3[ISO_TIME_LEN+1]; char buf4[ISO_TIME_LEN+1]; time_t down_time; if (interval_wakeup_time+time_to_exhaust_bw > TIME_MAX) down_time = TIME_MAX; else down_time = (time_t)(interval_wakeup_time+time_to_exhaust_bw); if (down_time>interval_end_time) down_time = interval_end_time; format_local_iso_time(buf1, interval_start_time); format_local_iso_time(buf2, interval_wakeup_time); format_local_iso_time(buf3, down_time); format_local_iso_time(buf4, interval_end_time); log_notice(LD_ACCT, "Configured hibernation. This interval began at %s; " "the scheduled wake-up time %s %s; " "we expect%s to exhaust our quota for this interval around %s; " "the next interval begins at %s (all times local)", buf1, time(NULL)<interval_wakeup_time?"is":"was", buf2, time(NULL)<down_time?"":"ed", buf3, buf4); } }