only play the accounting game if it's possible you'll choose to hibernate
[tor.git] / src / or / hibernate.c
blob68fe34cfdbd538b78da38716ce9e91138d641ac2
1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
5 /**
6 * \file hibernate.c
7 * \brief Functions to close listeners, stop allowing new circuits,
8 * etc in preparation for closing down or going dormant; and to track
9 * bandwidth and time intervals to know when to hibernate and when to
10 * stop hibernating.
11 **/
14 hibernating, phase 1:
15 - send destroy in response to create cells
16 - send end (policy failed) in response to begin cells
17 - close an OR conn when it has no circuits
19 hibernating, phase 2:
20 (entered when bandwidth hard limit reached)
21 - close all OR/AP/exit conns)
24 #include "or.h"
26 #define HIBERNATE_STATE_LIVE 1
27 #define HIBERNATE_STATE_EXITING 2
28 #define HIBERNATE_STATE_LOWBANDWIDTH 3
29 #define HIBERNATE_STATE_DORMANT 4
31 #define SHUTDOWN_WAIT_LENGTH 30 /* seconds */
33 static int hibernate_state = HIBERNATE_STATE_LIVE;
34 static time_t hibernate_timeout = 0;
36 /** How many bytes have we read/written in this accounting interval? */
37 static uint64_t n_bytes_read_in_interval = 0;
38 static uint64_t n_bytes_written_in_interval = 0;
39 /** How many seconds have we been running this interval? */
40 static uint32_t n_seconds_active_in_interval = 0;
41 /** When did this accounting interval start? */
42 static time_t interval_start_time = 0;
43 /** When will this accounting interval end? */
44 static time_t interval_end_time = 0;
45 /** How far into the accounting interval should we hibernate? */
46 static time_t interval_wakeup_time = 0;
47 /** How much bandwidth do we 'expect' to use per minute? */
48 static uint32_t expected_bandwidth_usage = 0;
50 static void reset_accounting(time_t now);
51 static int read_bandwidth_usage(void);
52 static int record_bandwidth_usage(time_t now);
53 static time_t start_of_accounting_period_after(time_t now);
54 static time_t start_of_accounting_period_containing(time_t now);
55 static void accounting_set_wakeup_time(void);
57 /* ************
58 * Functions for bandwidth accounting.
59 * ************/
61 void
62 accounting_add_bytes(size_t n_read, size_t n_written, int seconds)
64 n_bytes_read_in_interval += n_read;
65 n_bytes_written_in_interval += n_written;
66 /* If we haven't been called in 10 seconds, we're probably jumping
67 * around in time. */
68 n_seconds_active_in_interval += (seconds < 10) ? seconds : 0;
71 static INLINE void
72 incr_month(struct tm *tm, unsigned int delta)
74 tm->tm_mon += delta;
75 while (tm->tm_mon > 11) {
76 ++tm->tm_year;
77 tm->tm_mon -= 12;
81 static INLINE void
82 decr_month(struct tm *tm, unsigned int delta)
84 tm->tm_mon -= delta;
85 while (tm->tm_mon < 0) {
86 --tm->tm_year;
87 tm->tm_mon += 12;
91 static time_t
92 start_of_accounting_period_containing(time_t now)
94 struct tm *tm;
95 /* Only months are supported. */
96 tm = gmtime(&now);
97 /* If this is before the Nth, we want the Nth of last month. */
98 if (tm->tm_mday < get_options()->AccountingStart) {
99 decr_month(tm, 1);
101 /* Otherwise, the month and year are correct.*/
103 tm->tm_mday = get_options()->AccountingStart;
104 tm->tm_hour = 0;
105 tm->tm_min = 0;
106 tm->tm_sec = 0;
107 return tor_timegm(tm);
109 static time_t
110 start_of_accounting_period_after(time_t now)
112 time_t start;
113 struct tm *tm;
114 start = start_of_accounting_period_containing(now);
116 tm = gmtime(&start);
117 incr_month(tm, 1);
118 return tor_timegm(tm);
121 void
122 configure_accounting(time_t now)
124 if (!interval_start_time)
125 read_bandwidth_usage(); /* If we fail, we'll leave values at zero, and
126 * reset below.*/
127 if (!interval_start_time ||
128 start_of_accounting_period_after(interval_start_time) <= now) {
129 /* We start a new interval. */
130 log_fn(LOG_INFO, "Starting new accounting interval.");
131 reset_accounting(now);
132 } if (interval_start_time ==
133 start_of_accounting_period_containing(interval_start_time)) {
134 log_fn(LOG_INFO, "Continuing accounting interval.");
135 /* We are in the interval we thought we were in. Do nothing.*/
136 } else {
137 log_fn(LOG_WARN, "Mismatched accounting interval; starting a fresh one.");
138 reset_accounting(now);
140 accounting_set_wakeup_time();
143 static void
144 update_expected_bandwidth(void)
146 uint64_t used;
147 uint32_t max_configured = (get_options()->BandwidthRateBytes * 60);
149 if (n_seconds_active_in_interval < 1800) {
150 expected_bandwidth_usage = max_configured;
151 } else {
152 used = n_bytes_written_in_interval < n_bytes_read_in_interval ?
153 n_bytes_read_in_interval : n_bytes_written_in_interval;
154 expected_bandwidth_usage = (uint32_t)
155 (used / (n_seconds_active_in_interval / 60));
156 if (expected_bandwidth_usage > max_configured)
157 expected_bandwidth_usage = max_configured;
161 static void
162 reset_accounting(time_t now) {
163 log_fn(LOG_INFO, "Starting new accounting interval.");
164 update_expected_bandwidth();
165 interval_start_time = start_of_accounting_period_containing(now);
166 interval_end_time = start_of_accounting_period_after(interval_start_time);
167 n_bytes_read_in_interval = 0;
168 n_bytes_written_in_interval = 0;
169 n_seconds_active_in_interval = 0;
172 static INLINE int time_to_record_bandwidth_usage(time_t now)
174 /* Note every 5 minutes */
175 #define NOTE_INTERVAL (5*60)
176 /* Or every 20 megabytes */
177 #define NOTE_BYTES 20*(1024*1024)
178 static uint64_t last_read_bytes_noted = 0;
179 static uint64_t last_written_bytes_noted = 0;
180 static time_t last_time_noted = 0;
182 if (last_time_noted + NOTE_INTERVAL <= now ||
183 last_read_bytes_noted + NOTE_BYTES <= n_bytes_read_in_interval ||
184 last_written_bytes_noted + NOTE_BYTES <= n_bytes_written_in_interval ||
185 (interval_end_time && interval_end_time <= now)) {
186 last_time_noted = now;
187 last_read_bytes_noted = n_bytes_read_in_interval;
188 last_written_bytes_noted = n_bytes_written_in_interval;
189 return 1;
191 return 0;
194 void
195 accounting_run_housekeeping(time_t now)
197 if (now >= interval_end_time) {
198 configure_accounting(now);
200 if (time_to_record_bandwidth_usage(now)) {
201 if (record_bandwidth_usage(now)) {
202 log_fn(LOG_ERR, "Couldn't record bandwidth usage; exiting.");
203 exit(1);
208 static void
209 accounting_set_wakeup_time(void)
211 struct tm *tm;
212 char buf[ISO_TIME_LEN+1];
213 char digest[DIGEST_LEN];
214 crypto_digest_env_t *d;
215 int n_days_in_interval;
216 int n_days_to_exhaust_bw;
217 int n_days_to_consider;
219 format_iso_time(buf, interval_start_time);
220 crypto_pk_get_digest(get_identity_key(), digest);
222 d = crypto_new_digest_env();
223 crypto_digest_add_bytes(d, buf, ISO_TIME_LEN);
224 crypto_digest_add_bytes(d, digest, DIGEST_LEN);
225 crypto_digest_get_digest(d, digest, DIGEST_LEN);
226 crypto_free_digest_env(d);
228 n_days_to_exhaust_bw = (get_options()->AccountingMaxKB/expected_bandwidth_usage)
229 /(24*60);
231 tm = gmtime(&interval_start_time);
232 if (++tm->tm_mon > 11) { tm->tm_mon = 0; ++tm->tm_year; }
233 n_days_in_interval = (tor_timegm(tm)-interval_start_time+1)/(24*60*60);
235 n_days_to_consider = n_days_in_interval - n_days_to_exhaust_bw;
237 /* XXX can we simplify this just by picking a random (non-deterministic)
238 * time to be up? If we go down and come up, then we pick a new one. Is
239 * that good enough? -RD */
240 while (((unsigned char)digest[0]) > n_days_to_consider)
241 crypto_digest(digest, digest, DIGEST_LEN);
243 interval_wakeup_time = interval_start_time +
244 24*60*60 * (unsigned char)digest[0];
247 #define BW_ACCOUNTING_VERSION 1
248 static int record_bandwidth_usage(time_t now)
250 char buf[128];
251 char fname[512];
252 char time1[ISO_TIME_LEN+1];
253 char time2[ISO_TIME_LEN+1];
254 char *cp = buf;
255 /* Format is:
256 Version\nTime\nTime\nRead\nWrite\nSeconds\nExpected-Rate\n */
258 format_iso_time(time1, interval_start_time);
259 format_iso_time(time2, now);
260 tor_snprintf(cp, sizeof(buf),
261 "%d\n%s\n%s\n"U64_FORMAT"\n"U64_FORMAT"\n%lu\n%lu\n",
262 BW_ACCOUNTING_VERSION,
263 time1,
264 time2,
265 U64_PRINTF_ARG(n_bytes_read_in_interval),
266 U64_PRINTF_ARG(n_bytes_written_in_interval),
267 (unsigned long)n_seconds_active_in_interval,
268 (unsigned long)expected_bandwidth_usage);
269 tor_snprintf(fname, sizeof(fname), "%s/bw_accounting",
270 get_data_directory());
272 return write_str_to_file(fname, buf, 0);
275 static int read_bandwidth_usage(void)
277 char *s = NULL;
278 char fname[512];
279 time_t t1, t2;
280 uint64_t n_read, n_written;
281 uint32_t expected_bw, n_seconds;
282 smartlist_t *elts;
283 int ok;
285 tor_snprintf(fname, sizeof(fname), "%s/bw_accounting",
286 get_data_directory());
287 if (!(s = read_file_to_str(fname, 0))) {
288 return 0;
290 elts = smartlist_create();
291 smartlist_split_string(elts, s, "\n", SPLIT_SKIP_SPACE, SPLIT_IGNORE_BLANK);
292 tor_free(s);
294 if (smartlist_len(elts)<1 ||
295 atoi(smartlist_get(elts,0)) != BW_ACCOUNTING_VERSION) {
296 log_fn(LOG_WARN, "Unrecognized bw_accounting file version: %s",
297 (const char*)smartlist_get(elts,0));
298 goto err;
300 if (smartlist_len(elts) < 7) {
301 log_fn(LOG_WARN, "Corrupted bw_accounting file: %d lines",
302 smartlist_len(elts));
303 goto err;
305 if (parse_iso_time(smartlist_get(elts,1), &t1)) {
306 log_fn(LOG_WARN, "Error parsing bandwidth usage start time.");
307 goto err;
309 if (parse_iso_time(smartlist_get(elts,2), &t2)) {
310 log_fn(LOG_WARN, "Error parsing bandwidth usage last-written time");
311 goto err;
313 n_read = tor_parse_uint64(smartlist_get(elts,3), 10, 0, UINT64_MAX,
314 &ok, NULL);
315 if (!ok) {
316 log_fn(LOG_WARN, "Error parsing number of bytes read");
317 goto err;
319 n_written = tor_parse_uint64(smartlist_get(elts,4), 10, 0, UINT64_MAX,
320 &ok, NULL);
321 if (!ok) {
322 log_fn(LOG_WARN, "Error parsing number of bytes read");
323 goto err;
325 n_seconds = (uint32_t)tor_parse_ulong(smartlist_get(elts,5), 10,0,ULONG_MAX,
326 &ok, NULL);
327 if (!ok) {
328 log_fn(LOG_WARN, "Error parsing number of seconds live");
329 goto err;
331 expected_bw =(uint32_t)tor_parse_ulong(smartlist_get(elts,6), 10,0,ULONG_MAX,
332 &ok, NULL);
333 if (!ok) {
334 log_fn(LOG_WARN, "Error parsing expected bandwidth");
335 goto err;
338 n_bytes_read_in_interval = n_read;
339 n_bytes_written_in_interval = n_written;
340 n_seconds_active_in_interval = n_seconds;
341 interval_start_time = t1;
342 expected_bandwidth_usage = expected_bw;
344 accounting_set_wakeup_time();
345 return 0;
346 err:
347 SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
348 smartlist_free(elts);
349 return -1;
352 static int hibernate_hard_limit_reached(void)
354 uint64_t hard_limit = get_options()->AccountingMaxKB<<10;
355 if (!hard_limit)
356 return 0;
357 return n_bytes_read_in_interval >= hard_limit
358 || n_bytes_written_in_interval >= hard_limit;
361 static int hibernate_soft_limit_reached(void)
363 uint64_t soft_limit = (uint64_t) ((get_options()->AccountingMaxKB<<10) * .99);
364 if (!soft_limit)
365 return 0;
366 return n_bytes_read_in_interval >= soft_limit
367 || n_bytes_written_in_interval >= soft_limit;
370 /** Called when we get a SIGINT, or when bandwidth soft limit
371 * is reached. */
372 static void hibernate_begin(int new_state, time_t now) {
373 connection_t *conn;
375 if(hibernate_state == HIBERNATE_STATE_EXITING) {
376 /* we've been called twice now. close immediately. */
377 log(LOG_NOTICE,"Second sigint received; exiting now.");
378 tor_cleanup();
379 exit(0);
382 tor_assert(hibernate_state == HIBERNATE_STATE_LIVE);
384 /* close listeners */
385 while((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
386 (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
387 (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) {
388 log_fn(LOG_INFO,"Closing listener type %d", conn->type);
389 connection_mark_for_close(conn);
392 /* XXX kill intro point circs */
393 /* XXX upload rendezvous service descriptors with no intro points */
395 if(new_state == HIBERNATE_STATE_EXITING) {
396 log(LOG_NOTICE,"Interrupt: will shut down in %d seconds. Interrupt again to exit now.", SHUTDOWN_WAIT_LENGTH);
397 hibernate_timeout = time(NULL) + SHUTDOWN_WAIT_LENGTH;
398 } else { /* soft limit reached */
399 log_fn(LOG_NOTICE,"Bandwidth limit reached; beginning hibernation.");
400 hibernate_timeout = interval_end_time;
403 hibernate_state = new_state;
406 /** Called when we've been hibernating and our timeout is reached. */
407 static void hibernate_end(int new_state) {
409 tor_assert(hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH ||
410 hibernate_state == HIBERNATE_STATE_DORMANT);
412 /* listeners will be relaunched in run_scheduled_events() in main.c */
413 log_fn(LOG_NOTICE,"Hibernation period ended. Resuming normal activity.");
415 hibernate_state = new_state;
416 hibernate_timeout = 0; /* no longer hibernating */
419 /** A wrapper around hibernate_begin, for when we get SIGINT. */
420 void hibernate_begin_shutdown(void) {
421 hibernate_begin(HIBERNATE_STATE_EXITING, time(NULL));
424 /** A wrapper to expose whether we're hibernating. */
425 int we_are_hibernating(void) {
426 return hibernate_state != HIBERNATE_STATE_LIVE;
429 /** The big function. Consider our environment and decide if it's
430 * time to start/stop hibernating.
432 void consider_hibernation(time_t now) {
433 connection_t *conn;
435 if (hibernate_state == HIBERNATE_STATE_EXITING) {
436 tor_assert(hibernate_timeout);
437 if(hibernate_timeout <= now) {
438 log(LOG_NOTICE,"Clean shutdown finished. Exiting.");
439 tor_cleanup();
440 exit(0);
442 return; /* if exiting soon, don't worry about bandwidth limits */
445 if(hibernate_state != HIBERNATE_STATE_LIVE) {
446 /* We've been hibernating because of bandwidth accounting. */
447 tor_assert(hibernate_timeout);
448 if (hibernate_timeout > now) {
449 /* If we're hibernating, don't wake up until it's time, regardless of
450 * whether we're in a new interval */
451 return ;
452 } else {
453 /* The interval has ended, or it is wakeup time. Find out which */
454 accounting_run_housekeeping(now);
455 if (interval_wakeup_time <= now) {
456 /* The interval hasn't changed, but interval_wakeup_time has passed.
457 * It's time to wake up. */
458 hibernate_end(HIBERNATE_STATE_LIVE);
459 return;
460 } else {
461 /* The interval has changed, and it isn't time to wake up yet. */
462 hibernate_timeout = interval_wakeup_time;
467 /* Else, see if it's time to start hibernating, or to go dormant. */
468 if (hibernate_state == HIBERNATE_STATE_LIVE &&
469 hibernate_soft_limit_reached()) {
470 log_fn(LOG_NOTICE,"Bandwidth soft limit reached; commencing hibernation.");
471 hibernate_begin(HIBERNATE_STATE_LOWBANDWIDTH, now);
474 if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH &&
475 hibernate_hard_limit_reached()) {
476 hibernate_state = HIBERNATE_STATE_DORMANT;
477 log_fn(LOG_NOTICE,"Going dormant. Blowing away remaining connections.");
479 /* Close all OR/AP/exit conns. Leave dir conns. */
480 while((conn = connection_get_by_type(CONN_TYPE_OR)) ||
481 (conn = connection_get_by_type(CONN_TYPE_AP)) ||
482 (conn = connection_get_by_type(CONN_TYPE_EXIT))) {
483 log_fn(LOG_INFO,"Closing conn type %d", conn->type);
484 connection_mark_for_close(conn);