/** If we aren't currently dormant, close all connections and become * dormant. */ static void hibernate_go_dormant(time_t now) { connection_t *conn; if (hibernate_state == HIBERNATE_STATE_DORMANT) return; else if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH) hibernate_state = HIBERNATE_STATE_DORMANT; else hibernate_begin(HIBERNATE_STATE_DORMANT, now); log_notice(LD_ACCT,"Going dormant. Blowing away remaining connections."); /* Close all OR/AP/exit conns. Leave dir conns because we still want * to be able to upload server descriptors so people know we're still * running, and download directories so we can detect if we're obsolete. * Leave control conns because we still want to be controllable. */ while ((conn = connection_get_by_type(CONN_TYPE_OR)) || (conn = connection_get_by_type(CONN_TYPE_AP)) || (conn = connection_get_by_type(CONN_TYPE_EXIT))) { if (CONN_IS_EDGE(conn)) connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING); log_info(LD_NET,"Closing conn type %d", conn->type); if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */ connection_mark_unattached_ap(TO_ENTRY_CONN(conn), END_STREAM_REASON_HIBERNATING); else if (conn->type == CONN_TYPE_OR) { if (TO_OR_CONN(conn)->chan) { channel_mark_for_close(TLS_CHAN_TO_BASE(TO_OR_CONN(conn)->chan)); } else { connection_mark_for_close(conn); } } else connection_mark_for_close(conn); } if (now < interval_wakeup_time) hibernate_end_time = interval_wakeup_time; else hibernate_end_time = interval_end_time; accounting_record_bandwidth_usage(now, get_or_state()); or_state_mark_dirty(get_or_state(), get_options()->AvoidDiskWrites ? now+600 : 0); }
/** Return the config line for transport <b>transport</b> in the current state. * Return NULL if there is no config line for <b>transport</b>. */ static config_line_t * get_transport_in_state_by_name(const char *transport) { or_state_t *or_state = get_or_state(); config_line_t *line; config_line_t *ret = NULL; smartlist_t *items = NULL; for (line = or_state->TransportProxies ; line ; line = line->next) { tor_assert(!strcmp(line->key, "TransportProxy")); items = smartlist_new(); smartlist_split_string(items, line->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); if (smartlist_len(items) != 2) /* broken state */ goto done; if (!strcmp(smartlist_get(items, 0), transport)) { ret = line; goto done; } SMARTLIST_FOREACH(items, char*, s, tor_free(s)); smartlist_free(items); items = NULL; } done: if (items) { SMARTLIST_FOREACH(items, char*, s, tor_free(s)); smartlist_free(items); } return ret; }
/** * Add a new build time value <b>time</b> to the set of build times. Time * units are milliseconds. * * circuit_build_times <b>cbt</b> is a circular array, so loop around when * array is full. */ int circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time) { if (time <= 0 || time > CBT_BUILD_TIME_MAX) { log_warn(LD_BUG, "Circuit build time is too large (%u)." "This is probably a bug.", time); tor_fragile_assert(); return -1; } log_debug(LD_CIRC, "Adding circuit build time %u", time); cbt->circuit_build_times[cbt->build_times_idx] = time; cbt->build_times_idx = (cbt->build_times_idx + 1) % CBT_NCIRCUITS_TO_OBSERVE; if (cbt->total_build_times < CBT_NCIRCUITS_TO_OBSERVE) cbt->total_build_times++; if ((cbt->total_build_times % CBT_SAVE_STATE_EVERY) == 0) { /* Save state every n circuit builds */ if (!unit_tests && !get_options()->AvoidDiskWrites) or_state_mark_dirty(get_or_state(), 0); } return 0; }
/** Read stored accounting information from disk. Return 0 on success; * return -1 and change nothing on failure. */ static int read_bandwidth_usage(void) { or_state_t *state = get_or_state(); { char *fname = get_datadir_fname("bw_accounting"); unlink(fname); tor_free(fname); } if (!state) return -1; log_info(LD_ACCT, "Reading bandwidth accounting data from state file"); n_bytes_read_in_interval = state->AccountingBytesReadInInterval; n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval; n_seconds_active_in_interval = state->AccountingSecondsActive; interval_start_time = state->AccountingIntervalStart; expected_bandwidth_usage = state->AccountingExpectedUsage; /* Older versions of Tor (before 0.2.2.17-alpha or so) didn't generate these * fields. If you switch back and forth, you might get an * AccountingSoftLimitHitAt value from long before the most recent * interval_start_time. If that's so, then ignore the softlimit-related * values. */ if (state->AccountingSoftLimitHitAt > interval_start_time) { soft_limit_hit_at = state->AccountingSoftLimitHitAt; n_bytes_at_soft_limit = state->AccountingBytesAtSoftLimit; n_seconds_to_hit_soft_limit = state->AccountingSecondsToReachSoftLimit; } else { soft_limit_hit_at = 0; n_bytes_at_soft_limit = 0; n_seconds_to_hit_soft_limit = 0; } { char tbuf1[ISO_TIME_LEN+1]; char tbuf2[ISO_TIME_LEN+1]; format_iso_time(tbuf1, state->LastWritten); format_iso_time(tbuf2, state->AccountingIntervalStart); log_info(LD_ACCT, "Successfully read bandwidth accounting info from state written at %s " "for interval starting at %s. We have been active for %lu seconds in " "this interval. At the start of the interval, we expected to use " "about %lu KB per second. ("U64_FORMAT" bytes read so far, " U64_FORMAT" bytes written so far)", tbuf1, tbuf2, (unsigned long)n_seconds_active_in_interval, (unsigned long)(expected_bandwidth_usage*1024/60), U64_PRINTF_ARG(n_bytes_read_in_interval), U64_PRINTF_ARG(n_bytes_written_in_interval)); } return 0; }
/** Called when we get a SIGINT, or when bandwidth soft limit is * reached. Puts us into "loose hibernation": we don't accept new * connections, but we continue handling old ones. */ static void hibernate_begin(hibernate_state_t new_state, time_t now) { connection_t *conn; or_options_t *options = get_options(); if (new_state == HIBERNATE_STATE_EXITING && hibernate_state != HIBERNATE_STATE_LIVE) { log_notice(LD_GENERAL,"SIGINT received %s; exiting now.", hibernate_state == HIBERNATE_STATE_EXITING ? "a second time" : "while hibernating"); tor_cleanup(); exit(0); } /* close listeners. leave control listener(s). */ while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) || (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) || (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) || (conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) || (conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) || (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) { log_info(LD_NET,"Closing listener type %d", conn->type); connection_mark_for_close(conn); } /* XXX kill intro point circs */ /* XXX upload rendezvous service descriptors with no intro points */ if (new_state == HIBERNATE_STATE_EXITING) { log_notice(LD_GENERAL,"Interrupt: will shut down in %d seconds. Interrupt " "again to exit now.", options->ShutdownWaitLength); shutdown_time = time(NULL) + options->ShutdownWaitLength; } else { /* soft limit reached */ hibernate_end_time = interval_end_time; } hibernate_state = new_state; accounting_record_bandwidth_usage(now, get_or_state()); or_state_mark_dirty(get_or_state(), get_options()->AvoidDiskWrites ? now+600 : 0); }
/** Called when we get a SIGINT, or when bandwidth soft limit is * reached. Puts us into "loose hibernation": we don't accept new * connections, but we continue handling old ones. */ static void hibernate_begin(hibernate_state_t new_state, time_t now) { const or_options_t *options = get_options(); if (new_state == HIBERNATE_STATE_EXITING && hibernate_state != HIBERNATE_STATE_LIVE) { log_notice(LD_GENERAL,"SIGINT received %s; exiting now.", hibernate_state == HIBERNATE_STATE_EXITING ? "a second time" : "while hibernating"); tor_cleanup(); exit(0); } if (new_state == HIBERNATE_STATE_LOWBANDWIDTH && hibernate_state == HIBERNATE_STATE_LIVE) { soft_limit_hit_at = now; n_seconds_to_hit_soft_limit = n_seconds_active_in_interval; n_bytes_at_soft_limit = MAX(n_bytes_read_in_interval, n_bytes_written_in_interval); } /* close listeners. leave control listener(s). */ connection_mark_all_noncontrol_listeners(); /* XXX kill intro point circs */ /* XXX upload rendezvous service descriptors with no intro points */ if (new_state == HIBERNATE_STATE_EXITING) { log_notice(LD_GENERAL,"Interrupt: we have stopped accepting new " "connections, and will shut down in %d seconds. Interrupt " "again to exit now.", options->ShutdownWaitLength); shutdown_time = time(NULL) + options->ShutdownWaitLength; } else { /* soft limit reached */ hibernate_end_time = interval_end_time; } hibernate_state = new_state; accounting_record_bandwidth_usage(now, get_or_state()); or_state_mark_dirty(get_or_state(), get_options()->AvoidDiskWrites ? now+600 : 0); }
/** Invoked once per second. Checks whether it is time to hibernate, * record bandwidth used, etc. */ void accounting_run_housekeeping(time_t now) { if (now >= interval_end_time) { configure_accounting(now); } if (time_to_record_bandwidth_usage(now)) { if (accounting_record_bandwidth_usage(now, get_or_state())) { log_warn(LD_FS, "Couldn't record bandwidth usage to disk."); } } }
/** Read stored accounting information from disk. Return 0 on success; * return -1 and change nothing on failure. */ static int read_bandwidth_usage(void) { or_state_t *state = get_or_state(); { char *fname = get_datadir_fname("bw_accounting"); unlink(fname); tor_free(fname); } if (!state) return -1; /* Okay; it looks like the state file is more up-to-date than the * bw_accounting file, or the bw_accounting file is nonexistent, * or the bw_accounting file is corrupt. */ log_info(LD_ACCT, "Reading bandwidth accounting data from state file"); n_bytes_read_in_interval = state->AccountingBytesReadInInterval; n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval; n_seconds_active_in_interval = state->AccountingSecondsActive; interval_start_time = state->AccountingIntervalStart; expected_bandwidth_usage = state->AccountingExpectedUsage; { char tbuf1[ISO_TIME_LEN+1]; char tbuf2[ISO_TIME_LEN+1]; format_iso_time(tbuf1, state->LastWritten); format_iso_time(tbuf2, state->AccountingIntervalStart); log_info(LD_ACCT, "Successfully read bandwidth accounting info from state written at %s " "for interval starting at %s. We have been active for %lu seconds in " "this interval. At the start of the interval, we expected to use " "about %lu KB per second. ("U64_FORMAT" bytes read so far, " U64_FORMAT" bytes written so far)", tbuf1, tbuf2, (unsigned long)n_seconds_active_in_interval, (unsigned long)(expected_bandwidth_usage*1024/60), U64_PRINTF_ARG(n_bytes_read_in_interval), U64_PRINTF_ARG(n_bytes_written_in_interval)); } return 0; }
/** * Output a histogram of current circuit build times to * the or_state_t state structure. */ void circuit_build_times_update_state(circuit_build_times_t *cbt, or_state_t *state) { uint32_t *histogram; build_time_t i = 0; build_time_t nbins = 0; config_line_t **next, *line; histogram = circuit_build_times_create_histogram(cbt, &nbins); // write to state config_free_lines(state->BuildtimeHistogram); next = &state->BuildtimeHistogram; *next = NULL; state->TotalBuildTimes = cbt->total_build_times; state->CircuitBuildAbandonedCount = 0; for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { if (cbt->circuit_build_times[i] == CBT_BUILD_ABANDONED) state->CircuitBuildAbandonedCount++; } for (i = 0; i < nbins; i++) { // compress the histogram by skipping the blanks if (histogram[i] == 0) continue; *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("CircuitBuildTimeBin"); tor_asprintf(&line->value, "%d %d", CBT_BIN_TO_MS(i), histogram[i]); next = &(line->next); } if (!unit_tests) { if (!get_options()->AvoidDiskWrites) or_state_mark_dirty(get_or_state(), 0); } tor_free(histogram); }
/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to state */ void save_transport_to_state(const char *transport, const tor_addr_t *addr, uint16_t port) { or_state_t *state = get_or_state(); char *transport_addrport=NULL; /** find where to write on the state */ config_line_t **next, *line; /* see if this transport is already stored in state */ config_line_t *transport_line = get_transport_in_state_by_name(transport); if (transport_line) { /* if transport already exists in state... */ const char *prev_bindaddr = /* get its addrport... */ get_transport_bindaddr(transport_line->value, transport); tor_asprintf(&transport_addrport, "%s:%d", fmt_addr(addr), (int)port); /* if transport in state has the same address as this one, life is good */ if (!strcmp(prev_bindaddr, transport_addrport)) { log_info(LD_CONFIG, "Transport seems to have spawned on its usual " "address:port."); goto done; } else { /* if addrport in state is different than the one we got */ log_info(LD_CONFIG, "Transport seems to have spawned on different " "address:port. Let's update the state file with the new " "address:port"); tor_free(transport_line->value); /* free the old line */ tor_asprintf(&transport_line->value, "%s %s:%d", transport, fmt_addr(addr), (int) port); /* replace old addrport line with new line */ } } else { /* never seen this one before; save it in state for next time */ log_info(LD_CONFIG, "It's the first time we see this transport. " "Let's save its address:port"); next = &state->TransportProxies; /* find the last TransportProxy line in the state and point 'next' right after it */ line = state->TransportProxies; while (line) { next = &(line->next); line = line->next; } /* allocate space for the new line and fill it in */ *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("TransportProxy"); tor_asprintf(&line->value, "%s %s:%d", transport, fmt_addr(addr), (int) port); next = &(line->next); } if (!get_options()->AvoidDiskWrites) or_state_mark_dirty(state, 0); done: tor_free(transport_addrport); }