diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-05 13:52:17 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-05 13:52:17 +0100 |
| commit | 47293a4f116e04a88b0797bafad0c98b4393b744 (patch) | |
| tree | b71d19aa8637382a6d3b453614a6a52a2c1b3245 | |
| parent | 9a3a01643fc78bf544a1e6f5ca5c7529e2026b25 (diff) | |
Clamp every elapsed time span at zero, using a set of helper functions
| -rw-r--r-- | Mk/tests.mk | 4 | ||||
| -rw-r--r-- | src/opentracker.c | 2 | ||||
| -rw-r--r-- | src/ot_accesslist.c | 2 | ||||
| -rw-r--r-- | src/ot_clean.c | 2 | ||||
| -rw-r--r-- | src/ot_http.c | 2 | ||||
| -rw-r--r-- | src/ot_livesync.c | 4 | ||||
| -rw-r--r-- | src/ot_stats.c | 23 | ||||
| -rw-r--r-- | src/ot_udp.c | 4 | ||||
| -rw-r--r-- | src/trackerlogic.h | 24 | ||||
| -rw-r--r-- | tests/runner.c | 1 | ||||
| -rw-r--r-- | tests/stubs.c | 9 | ||||
| -rw-r--r-- | tests/t_clean.c | 140 | ||||
| -rw-r--r-- | tests/test.h | 1 |
13 files changed, 187 insertions, 31 deletions
diff --git a/Mk/tests.mk b/Mk/tests.mk index 2efdd72..22ea69e 100644 --- a/Mk/tests.mk +++ b/Mk/tests.mk | |||
| @@ -4,8 +4,8 @@ BUILDDEPS+=Mk/tests.mk | |||
| 4 | # may reach a static function by including the source. tests/stubs.c supplies | 4 | # may reach a static function by including the source. tests/stubs.c supplies |
| 5 | # the few symbols those modules reference but the tests never reach. | 5 | # the few symbols those modules reference but the tests never reach. |
| 6 | TESTBIN=tests/runner | 6 | TESTBIN=tests/runner |
| 7 | TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_trackerlogic.c tests/t_workqueue.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c | 7 | TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_trackerlogic.c tests/t_clean.c tests/t_workqueue.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c |
| 8 | TEST_UNITS=src/ot_urlencoded.c src/ot_fmt.c src/ot_event.c src/ot_batch.c src/ot_fdstate.c src/ot_buf.c src/ot_ip.c src/ot_socket.c src/ot_iovec.c src/ot_vector.c src/ot_random.c src/ot_hex.c src/ot_scan.c src/ot_accesslist.c src/ot_mutex.c src/ot_workqueue.c src/trackerlogic.c | 8 | TEST_UNITS=src/ot_urlencoded.c src/ot_fmt.c src/ot_event.c src/ot_batch.c src/ot_fdstate.c src/ot_buf.c src/ot_ip.c src/ot_socket.c src/ot_iovec.c src/ot_vector.c src/ot_random.c src/ot_hex.c src/ot_scan.c src/ot_accesslist.c src/ot_mutex.c src/ot_workqueue.c src/ot_clean.c src/trackerlogic.c |
| 9 | TEST_HEADERS=tests/test.h $(HEADERS) | 9 | TEST_HEADERS=tests/test.h $(HEADERS) |
| 10 | 10 | ||
| 11 | # A test binary is not a production binary, so it is built with the features | 11 | # A test binary is not a production binary, so it is built with the features |
diff --git a/src/opentracker.c b/src/opentracker.c index 60a6d8c..8de3fc1 100644 --- a/src/opentracker.c +++ b/src/opentracker.c | |||
| @@ -151,7 +151,7 @@ static void *server_mainloop(void *args) { | |||
| 151 | while ((sock = workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) | 151 | while ((sock = workqueue_popresult(&iovec_entries, &iovector, &is_partial)) != -1) |
| 152 | http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); | 152 | http_sendiovecdata(sock, &ws, iovec_entries, iovector, is_partial); |
| 153 | 153 | ||
| 154 | if (time_now_seconds() > next_timeout_check) { | 154 | if (time_is_past(next_timeout_check)) { |
| 155 | int cursor = 0; | 155 | int cursor = 0; |
| 156 | 156 | ||
| 157 | while ((sock = fdstate_next_expired(time_now_seconds(), &cursor)) != -1) | 157 | while ((sock = fdstate_next_expired(time_now_seconds(), &cursor)) != -1) |
diff --git a/src/ot_accesslist.c b/src/ot_accesslist.c index 86f9a7d..ea5bdba 100644 --- a/src/ot_accesslist.c +++ b/src/ot_accesslist.c | |||
| @@ -91,7 +91,7 @@ static ot_accesslist *accesslist_make(ot_accesslist *next, size_t size) { | |||
| 91 | This will never delete head, because that might still be in use. */ | 91 | This will never delete head, because that might still be in use. */ |
| 92 | static void accesslist_clean(ot_accesslist *accesslist) { | 92 | static void accesslist_clean(ot_accesslist *accesslist) { |
| 93 | while (accesslist && accesslist->next) { | 93 | while (accesslist && accesslist->next) { |
| 94 | if (accesslist->next->base + 5 < time_now_minutes()) | 94 | if (time_since_minutes(accesslist->next->base) > 5) |
| 95 | accesslist->next = accesslist_free(accesslist->next); | 95 | accesslist->next = accesslist_free(accesslist->next); |
| 96 | accesslist = accesslist->next; | 96 | accesslist = accesslist->next; |
| 97 | } | 97 | } |
diff --git a/src/ot_clean.c b/src/ot_clean.c index f181e83..2ebfd42 100644 --- a/src/ot_clean.c +++ b/src/ot_clean.c | |||
| @@ -45,7 +45,7 @@ static ssize_t clean_single_bucket(ot_peer *peers, size_t peer_count, size_t pee | |||
| 45 | 45 | ||
| 46 | int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { | 46 | int clean_single_peer_list(ot_peerlist *peer_list, size_t peer_size) { |
| 47 | ot_vector *peer_vector = &peer_list->peers; | 47 | ot_vector *peer_vector = &peer_list->peers; |
| 48 | time_t timedout = (time_t)(time_now_minutes() - peer_list->base); | 48 | ot_time timedout = time_since_minutes(peer_list->base); |
| 49 | size_t num_buckets = 1, removed_seeders = 0; | 49 | size_t num_buckets = 1, removed_seeders = 0; |
| 50 | 50 | ||
| 51 | /* No need to clean empty torrent */ | 51 | /* No need to clean empty torrent */ |
diff --git a/src/ot_http.c b/src/ot_http.c index 96b8f4e..533a876 100644 --- a/src/ot_http.c +++ b/src/ot_http.c | |||
| @@ -414,7 +414,7 @@ static ssize_t http_handle_fullscrape(const int64_t sock, struct ot_workstruct * | |||
| 414 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 414 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
| 415 | HTTPERROR_500; | 415 | HTTPERROR_500; |
| 416 | } | 416 | } |
| 417 | if (exactmatch && (this_peer.last_fullscrape - new_peer->last_fullscrape) < OT_MODEST_PEER_TIMEOUT) { | 417 | if (exactmatch && time_since_seconds(new_peer->last_fullscrape) < OT_MODEST_PEER_TIMEOUT) { |
| 418 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); | 418 | pthread_mutex_unlock(&g_modest_fullscrape_mutex); |
| 419 | HTTPERROR_402_NOTMODEST; | 419 | HTTPERROR_402_NOTMODEST; |
| 420 | } | 420 | } |
diff --git a/src/ot_livesync.c b/src/ot_livesync.c index 85577f7..7d7208e 100644 --- a/src/ot_livesync.c +++ b/src/ot_livesync.c | |||
| @@ -178,13 +178,13 @@ void livesync_ticker(void) { | |||
| 178 | 178 | ||
| 179 | /* livesync_issue_peersync sets g_next_packet_time */ | 179 | /* livesync_issue_peersync sets g_next_packet_time */ |
| 180 | pthread_mutex_lock(&g_outbuf_mutex); | 180 | pthread_mutex_lock(&g_outbuf_mutex); |
| 181 | if (time_now_seconds() > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) | 181 | if (time_is_past(g_v6_buf.next_packet_time) && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) |
| 182 | livesync_issue_peersync(&g_v6_buf); | 182 | livesync_issue_peersync(&g_v6_buf); |
| 183 | else | 183 | else |
| 184 | pthread_mutex_unlock(&g_outbuf_mutex); | 184 | pthread_mutex_unlock(&g_outbuf_mutex); |
| 185 | 185 | ||
| 186 | pthread_mutex_lock(&g_outbuf_mutex); | 186 | pthread_mutex_lock(&g_outbuf_mutex); |
| 187 | if (time_now_seconds() > g_v4_buf.next_packet_time && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) | 187 | if (time_is_past(g_v4_buf.next_packet_time) && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) |
| 188 | livesync_issue_peersync(&g_v4_buf); | 188 | livesync_issue_peersync(&g_v4_buf); |
| 189 | else | 189 | else |
| 190 | pthread_mutex_unlock(&g_outbuf_mutex); | 190 | pthread_mutex_unlock(&g_outbuf_mutex); |
diff --git a/src/ot_stats.c b/src/ot_stats.c index 2d6335b..7b5b1b9 100644 --- a/src/ot_stats.c +++ b/src/ot_stats.c | |||
| @@ -373,7 +373,7 @@ void stats_top_txt(char **reply, const char *reply_end, size_t amount) { | |||
| 373 | static unsigned long events_per_time(unsigned long long events, time_t t) { return events / ((unsigned int)t ? (unsigned int)t : 1); } | 373 | static unsigned long events_per_time(unsigned long long events, time_t t) { return events / ((unsigned int)t ? (unsigned int)t : 1); } |
| 374 | 374 | ||
| 375 | static void stats_connections_mrtg(char **reply, const char *reply_end) { | 375 | static void stats_connections_mrtg(char **reply, const char *reply_end) { |
| 376 | ot_time t = time(NULL) - ot_start_time; | 376 | ot_time t = time_since_seconds(ot_start_time); |
| 377 | unsigned long long total[OT_COUNTER_COUNT]; | 377 | unsigned long long total[OT_COUNTER_COUNT]; |
| 378 | 378 | ||
| 379 | counters_total(total); | 379 | counters_total(total); |
| @@ -386,7 +386,7 @@ static void stats_connections_mrtg(char **reply, const char *reply_end) { | |||
| 386 | } | 386 | } |
| 387 | 387 | ||
| 388 | static void stats_udpconnections_mrtg(char **reply, const char *reply_end) { | 388 | static void stats_udpconnections_mrtg(char **reply, const char *reply_end) { |
| 389 | ot_time t = time(NULL) - ot_start_time; | 389 | ot_time t = time_since_seconds(ot_start_time); |
| 390 | unsigned long long total[OT_COUNTER_COUNT]; | 390 | unsigned long long total[OT_COUNTER_COUNT]; |
| 391 | 391 | ||
| 392 | counters_total(total); | 392 | counters_total(total); |
| @@ -398,7 +398,7 @@ static void stats_udpconnections_mrtg(char **reply, const char *reply_end) { | |||
| 398 | } | 398 | } |
| 399 | 399 | ||
| 400 | static void stats_tcpconnections_mrtg(char **reply, const char *reply_end) { | 400 | static void stats_tcpconnections_mrtg(char **reply, const char *reply_end) { |
| 401 | time_t t = time(NULL) - ot_start_time; | 401 | time_t t = time_since_seconds(ot_start_time); |
| 402 | unsigned long long total[OT_COUNTER_COUNT]; | 402 | unsigned long long total[OT_COUNTER_COUNT]; |
| 403 | 403 | ||
| 404 | counters_total(total); | 404 | counters_total(total); |
| @@ -409,7 +409,7 @@ static void stats_tcpconnections_mrtg(char **reply, const char *reply_end) { | |||
| 409 | } | 409 | } |
| 410 | 410 | ||
| 411 | static void stats_scrape_mrtg(char **reply, const char *reply_end) { | 411 | static void stats_scrape_mrtg(char **reply, const char *reply_end) { |
| 412 | time_t t = time(NULL) - ot_start_time; | 412 | time_t t = time_since_seconds(ot_start_time); |
| 413 | unsigned long long total[OT_COUNTER_COUNT]; | 413 | unsigned long long total[OT_COUNTER_COUNT]; |
| 414 | 414 | ||
| 415 | counters_total(total); | 415 | counters_total(total); |
| @@ -420,7 +420,7 @@ static void stats_scrape_mrtg(char **reply, const char *reply_end) { | |||
| 420 | } | 420 | } |
| 421 | 421 | ||
| 422 | static void stats_fullscrapes_mrtg(char **reply, const char *reply_end) { | 422 | static void stats_fullscrapes_mrtg(char **reply, const char *reply_end) { |
| 423 | ot_time t = time(NULL) - ot_start_time; | 423 | ot_time t = time_since_seconds(ot_start_time); |
| 424 | unsigned long long total[OT_COUNTER_COUNT]; | 424 | unsigned long long total[OT_COUNTER_COUNT]; |
| 425 | 425 | ||
| 426 | counters_total(total); | 426 | counters_total(total); |
| @@ -468,7 +468,7 @@ static void stats_return_renew_bucket(char **reply, const char *reply_end) { | |||
| 468 | } | 468 | } |
| 469 | 469 | ||
| 470 | static void stats_return_sync_mrtg(char **reply, const char *reply_end) { | 470 | static void stats_return_sync_mrtg(char **reply, const char *reply_end) { |
| 471 | ot_time t = time(NULL) - ot_start_time; | 471 | ot_time t = time_since_seconds(ot_start_time); |
| 472 | unsigned long long total[OT_COUNTER_COUNT]; | 472 | unsigned long long total[OT_COUNTER_COUNT]; |
| 473 | 473 | ||
| 474 | counters_total(total); | 474 | counters_total(total); |
| @@ -479,7 +479,7 @@ static void stats_return_sync_mrtg(char **reply, const char *reply_end) { | |||
| 479 | } | 479 | } |
| 480 | 480 | ||
| 481 | static void stats_return_completed_mrtg(char **reply, const char *reply_end) { | 481 | static void stats_return_completed_mrtg(char **reply, const char *reply_end) { |
| 482 | ot_time t = time(NULL) - ot_start_time; | 482 | ot_time t = time_since_seconds(ot_start_time); |
| 483 | unsigned long long total[OT_COUNTER_COUNT]; | 483 | unsigned long long total[OT_COUNTER_COUNT]; |
| 484 | 484 | ||
| 485 | counters_total(total); | 485 | counters_total(total); |
| @@ -510,7 +510,7 @@ static void stats_return_everything(char **reply, const char *reply_end) { | |||
| 510 | 510 | ||
| 511 | fmt_append(reply, reply_end, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | 511 | fmt_append(reply, reply_end, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
| 512 | fmt_append(reply, reply_end, "<stats>\n"); | 512 | fmt_append(reply, reply_end, "<stats>\n"); |
| 513 | fmt_append(reply, reply_end, " <uptime>%llu</uptime>\n", (unsigned long long)(time(NULL) - ot_start_time)); | 513 | fmt_append(reply, reply_end, " <uptime>%llu</uptime>\n", (unsigned long long)time_since_seconds(ot_start_time)); |
| 514 | fmt_append(reply, reply_end, " <torrents>\n"); | 514 | fmt_append(reply, reply_end, " <torrents>\n"); |
| 515 | fmt_append(reply, reply_end, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count()); | 515 | fmt_append(reply, reply_end, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count()); |
| 516 | fmt_append(reply, reply_end, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count); | 516 | fmt_append(reply, reply_end, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count); |
| @@ -785,7 +785,7 @@ void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FL | |||
| 785 | case EVENT_FULLSCRAPE_REQUEST: { | 785 | case EVENT_FULLSCRAPE_REQUEST: { |
| 786 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ | 786 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ |
| 787 | char _debug[512]; | 787 | char _debug[512]; |
| 788 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(time_now_seconds() - ot_start_time) / 60); | 788 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)time_since_seconds(ot_start_time) / 60); |
| 789 | off += ip_format(_debug + off, *ip); | 789 | off += ip_format(_debug + off, *ip); |
| 790 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); | 790 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); |
| 791 | (void)write(2, _debug, off); | 791 | (void)write(2, _debug, off); |
| @@ -794,7 +794,7 @@ void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FL | |||
| 794 | case EVENT_FULLSCRAPE_REQUEST_GZIP: { | 794 | case EVENT_FULLSCRAPE_REQUEST_GZIP: { |
| 795 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ | 795 | ot_ip6 *ip = (ot_ip6 *)event_data; /* ugly hack to transfer ip to stats */ |
| 796 | char _debug[512]; | 796 | char _debug[512]; |
| 797 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(time_now_seconds() - ot_start_time) / 60); | 797 | int off = snprintf(_debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)time_since_seconds(ot_start_time) / 60); |
| 798 | off += ip_format(_debug + off, *ip); | 798 | off += ip_format(_debug + off, *ip); |
| 799 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); | 799 | off += snprintf(_debug + off, sizeof(_debug) - off, " - FULL SCRAPE\n"); |
| 800 | (void)write(2, _debug, off); | 800 | (void)write(2, _debug, off); |
| @@ -804,7 +804,8 @@ void stats_issue_event(struct ot_workstruct *ws, ot_status_event event, PROTO_FL | |||
| 804 | COUNT(OT_COUNTER_FAILED + event_data, 1); | 804 | COUNT(OT_COUNTER_FAILED + event_data, 1); |
| 805 | break; | 805 | break; |
| 806 | case EVENT_RENEW: | 806 | case EVENT_RENEW: |
| 807 | COUNT(OT_COUNTER_RENEWED + event_data, 1); | 807 | if (event_data < OT_PEER_TIMEOUT) |
| 808 | COUNT(OT_COUNTER_RENEWED + event_data, 1); | ||
| 808 | break; | 809 | break; |
| 809 | case EVENT_SYNC: | 810 | case EVENT_SYNC: |
| 810 | COUNT(OT_COUNTER_SYNCS, event_data); | 811 | COUNT(OT_COUNTER_SYNCS, event_data); |
diff --git a/src/ot_udp.c b/src/ot_udp.c index 644b71d..e67ca1f 100644 --- a/src/ot_udp.c +++ b/src/ot_udp.c | |||
| @@ -39,12 +39,12 @@ static void udp_make_connectionid(uint32_t connid[2], const ot_ip6 remoteip, int | |||
| 39 | int i; | 39 | int i; |
| 40 | uint32_t current_key_of_the_hour; | 40 | uint32_t current_key_of_the_hour; |
| 41 | 41 | ||
| 42 | if (time_now_minutes() - g_hour_of_the_key >= 60) { | 42 | if (time_since_minutes(g_hour_of_the_key) >= 60) { |
| 43 | uint32_t old_key_of_the_hour = g_key_of_the_hour[0]; | 43 | uint32_t old_key_of_the_hour = g_key_of_the_hour[0]; |
| 44 | uint32_t new_key_of_the_hour = random_uint32(); | 44 | uint32_t new_key_of_the_hour = random_uint32(); |
| 45 | /* If in the meantime another thread has performed | 45 | /* If in the meantime another thread has performed |
| 46 | key rotation, do not overwrite their results */ | 46 | key rotation, do not overwrite their results */ |
| 47 | if (time_now_minutes() - g_hour_of_the_key >= 60) { | 47 | if (time_since_minutes(g_hour_of_the_key) >= 60) { |
| 48 | /* Upgrade en bloc */ | 48 | /* Upgrade en bloc */ |
| 49 | g_hour_of_the_key = time_now_minutes(); | 49 | g_hour_of_the_key = time_now_minutes(); |
| 50 | g_key_of_the_hour[0] = new_key_of_the_hour; | 50 | g_key_of_the_hour[0] = new_key_of_the_hour; |
diff --git a/src/trackerlogic.h b/src/trackerlogic.h index 692b6af..c8d859c 100644 --- a/src/trackerlogic.h +++ b/src/trackerlogic.h | |||
| @@ -61,10 +61,10 @@ typedef struct { | |||
| 61 | #define OT_PEER_TIMEOUT 45 | 61 | #define OT_PEER_TIMEOUT 45 |
| 62 | 62 | ||
| 63 | /* We maintain a list of 1024 pointers to sorted list of ot_torrent structs | 63 | /* We maintain a list of 1024 pointers to sorted list of ot_torrent structs |
| 64 | Sort key is, of course, its hash */ | 64 | Sort key is, of course, its hash. |
| 65 | /* Set from the Makefile, 8 to 16. More buckets means shorter vectors and a | 65 | Can be tweked from the Makefile, sensible values are between 8 in 16. More |
| 66 | cheaper insert, which only pays for a tracker carrying millions of | 66 | buckets means shorter vectors and a cheaper insert, which only pays for a |
| 67 | torrents; see DESIGN. */ | 67 | tracker carrying millions of torrents. Consult the DESIGN document. */ |
| 68 | #ifndef OT_BUCKET_COUNT_BITS | 68 | #ifndef OT_BUCKET_COUNT_BITS |
| 69 | #define OT_BUCKET_COUNT_BITS 10 | 69 | #define OT_BUCKET_COUNT_BITS 10 |
| 70 | #endif | 70 | #endif |
| @@ -95,6 +95,22 @@ static inline ot_time time_now_minutes(void) { return time_now_seconds() / 60; } | |||
| 95 | /* For the deadlines the tracker sets on itself, which read better as the | 95 | /* For the deadlines the tracker sets on itself, which read better as the |
| 96 | span they allow than as an addition to the current second */ | 96 | span they allow than as an addition to the current second */ |
| 97 | static inline ot_time time_future_seconds(int span) { return time_now_seconds() + span; } | 97 | static inline ot_time time_future_seconds(int span) { return time_now_seconds() + span; } |
| 98 | |||
| 99 | /* How long ago a stamp was taken. */ | ||
| 100 | _Static_assert((ot_time)-1 < 0, "ot_time has to be signed for time_since_* to clamp"); | ||
| 101 | |||
| 102 | static inline ot_time time_since_seconds(ot_time then) { | ||
| 103 | ot_time now = time_now_seconds(); | ||
| 104 | return now > then ? now - then : 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | static inline ot_time time_since_minutes(ot_time then) { | ||
| 108 | ot_time now = time_now_minutes(); | ||
| 109 | return now > then ? now - then : 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | /* Has a deadline the tracker set on itself gone by? */ | ||
| 113 | static inline int time_is_past(ot_time deadline) { return time_now_seconds() > deadline; } | ||
| 98 | /* Cleared from the interrupt handler and read by every worker */ | 114 | /* Cleared from the interrupt handler and read by every worker */ |
| 99 | extern _Atomic int g_opentracker_running; | 115 | extern _Atomic int g_opentracker_running; |
| 100 | 116 | ||
diff --git a/tests/runner.c b/tests/runner.c index ae2565b..2398991 100644 --- a/tests/runner.c +++ b/tests/runner.c | |||
| @@ -27,6 +27,7 @@ static const struct module modules[] = {{"ot_urlencoded", test_urlencoded}, | |||
| 27 | {"ot_socket", test_socket}, | 27 | {"ot_socket", test_socket}, |
| 28 | {"ot_mutex", test_mutex}, | 28 | {"ot_mutex", test_mutex}, |
| 29 | {"trackerlogic", test_trackerlogic}, | 29 | {"trackerlogic", test_trackerlogic}, |
| 30 | {"clean", test_clean}, | ||
| 30 | {"ot_workqueue", test_workqueue}, | 31 | {"ot_workqueue", test_workqueue}, |
| 31 | {"ot_batch", test_batch}, | 32 | {"ot_batch", test_batch}, |
| 32 | {"ot_fdstate", test_fdstate}, | 33 | {"ot_fdstate", test_fdstate}, |
diff --git a/tests/stubs.c b/tests/stubs.c index f7ac679..fd47a68 100644 --- a/tests/stubs.c +++ b/tests/stubs.c | |||
| @@ -26,14 +26,11 @@ long io_trywrite(long d, const char *buf, long len) { | |||
| 26 | /* trackerlogic.c is under test, and starting it up starts every module it | 26 | /* trackerlogic.c is under test, and starting it up starts every module it |
| 27 | leans on. None of them is driven by these tests, so they are answered here | 27 | leans on. None of them is driven by these tests, so they are answered here |
| 28 | rather than linked in and left to run threads of their own. */ | 28 | rather than linked in and left to run threads of their own. */ |
| 29 | void clean_init(void) {} | 29 | /* ot_clean.c is in the unit build for t_clean, so its thread control is the |
| 30 | void clean_deinit(void) {} | 30 | real thing and only the stats hook it calls is stubbed here. */ |
| 31 | int clean_single_torrent(ot_torrent *torrent) { | ||
| 32 | (void)torrent; | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | void fullscrape_init(void) {} | 31 | void fullscrape_init(void) {} |
| 36 | void fullscrape_deinit(void) {} | 32 | void fullscrape_deinit(void) {} |
| 33 | void stats_cleanup(void) {} | ||
| 37 | void livesync_init(void) {} | 34 | void livesync_init(void) {} |
| 38 | void livesync_deinit(void) {} | 35 | void livesync_deinit(void) {} |
| 39 | void livesync_tell(struct ot_workstruct *ws) { (void)ws; } | 36 | void livesync_tell(struct ot_workstruct *ws) { (void)ws; } |
diff --git a/tests/t_clean.c b/tests/t_clean.c new file mode 100644 index 0000000..7eca9aa --- /dev/null +++ b/tests/t_clean.c | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. */ | ||
| 3 | |||
| 4 | #include <arpa/inet.h> | ||
| 5 | #include <signal.h> | ||
| 6 | #include <pthread.h> | ||
| 7 | #include <string.h> | ||
| 8 | |||
| 9 | #include "ot_clean.h" | ||
| 10 | #include "ot_mutex.h" | ||
| 11 | #include "trackerlogic.h" | ||
| 12 | #include "test.h" | ||
| 13 | |||
| 14 | /* A clock stepped backwards used to leave peer_list->base in the future, so | ||
| 15 | the span the cleaner works from came out negative. It was then added to a | ||
| 16 | peer's age and stored in the single byte that age lives in, where -1 reads | ||
| 17 | as 255 and the next pass threw the whole swarm away. */ | ||
| 18 | |||
| 19 | static void announce(struct ot_workstruct *ws, ot_hash *hash, uint8_t last_octet, int seeding) { | ||
| 20 | static const uint8_t v4mapped[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; | ||
| 21 | |||
| 22 | /* The peer list is written into the reply, which workstruct_init leaves | ||
| 23 | for its caller to point somewhere */ | ||
| 24 | ws->reply = ws->outbuf; | ||
| 25 | |||
| 26 | memset(&ws->peer, 0, sizeof(ws->peer)); | ||
| 27 | memcpy(&ws->peer, v4mapped, sizeof(v4mapped)); | ||
| 28 | ((uint8_t *)&ws->peer)[15] = last_octet; | ||
| 29 | OT_SETPORT(&ws->peer, &(uint16_t){6881}); | ||
| 30 | if (seeding) | ||
| 31 | OT_PEERFLAG(ws->peer) |= PEER_FLAG_SEEDING; | ||
| 32 | ws->hash = hash; | ||
| 33 | add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, 0); | ||
| 34 | } | ||
| 35 | |||
| 36 | static size_t peers_in(ot_hash *hash) { | ||
| 37 | char reply[1024]; | ||
| 38 | size_t len = return_udp_scrape_for_torrent(*hash, reply); | ||
| 39 | uint32_t seeders, leechers; | ||
| 40 | |||
| 41 | if (len < 12) | ||
| 42 | return 0; | ||
| 43 | memcpy(&seeders, reply, 4); | ||
| 44 | memcpy(&leechers, reply + 8, 4); | ||
| 45 | return (size_t)ntohl(seeders) + (size_t)ntohl(leechers); | ||
| 46 | } | ||
| 47 | |||
| 48 | static ot_torrent *torrent_of(ot_hash *hash) { | ||
| 49 | ot_vector *bucket = mutex_bucket_lock_by_hash(*hash); | ||
| 50 | ot_torrent *t = (ot_torrent *)bucket->data; | ||
| 51 | ot_torrent *found = NULL; | ||
| 52 | size_t i; | ||
| 53 | |||
| 54 | for (i = 0; i < bucket->size; ++i) | ||
| 55 | if (!memcmp(t[i].hash, hash, sizeof(ot_hash))) | ||
| 56 | found = t + i; | ||
| 57 | mutex_bucket_unlock_by_hash(*hash, 0); | ||
| 58 | return found; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* What a clock stepped back by `minutes` leaves behind */ | ||
| 62 | static void step_clock_back(ot_torrent *torrent, int minutes) { | ||
| 63 | torrent->peer_list6->base += minutes; | ||
| 64 | torrent->peer_list4->base += minutes; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* What the next minute of a well behaved clock looks like */ | ||
| 68 | static void age_by_a_minute(ot_torrent *torrent) { | ||
| 69 | torrent->peer_list6->base -= 1; | ||
| 70 | torrent->peer_list4->base -= 1; | ||
| 71 | } | ||
| 72 | |||
| 73 | static void case_a_clock_that_went_back_keeps_the_swarm(void) { | ||
| 74 | struct ot_workstruct ws; | ||
| 75 | ot_hash hash; | ||
| 76 | ot_torrent *torrent; | ||
| 77 | |||
| 78 | test_case("a swarm survives the clock being stepped backwards"); | ||
| 79 | memset(hash, 0x31, sizeof(hash)); | ||
| 80 | CHECK_INT(workstruct_init(&ws, G_INBUF_SIZE), 0); | ||
| 81 | |||
| 82 | announce(&ws, &hash, 1, 1); | ||
| 83 | announce(&ws, &hash, 2, 0); | ||
| 84 | CHECK_INT(peers_in(&hash), 2); | ||
| 85 | |||
| 86 | torrent = torrent_of(&hash); | ||
| 87 | CHECK(torrent != NULL); | ||
| 88 | step_clock_back(torrent, 5); | ||
| 89 | |||
| 90 | /* The pass that sees the step keeps everybody either way: unclamped it | ||
| 91 | writes a negative span into each peer's age byte, where it reads as 251 | ||
| 92 | rather than as a removal. */ | ||
| 93 | CHECK_INT(clean_single_torrent(torrent), 0); | ||
| 94 | CHECK_INT(peers_in(&hash), 2); | ||
| 95 | |||
| 96 | /* A minute later, and this is where a poisoned age byte took the swarm: | ||
| 97 | 251 plus one minute is past any timeout. */ | ||
| 98 | age_by_a_minute(torrent); | ||
| 99 | CHECK_INT(clean_single_torrent(torrent), 0); | ||
| 100 | CHECK_INT(peers_in(&hash), 2); | ||
| 101 | |||
| 102 | workstruct_deinit(&ws); | ||
| 103 | } | ||
| 104 | |||
| 105 | static void case_an_idle_swarm_still_ages_out(void) { | ||
| 106 | struct ot_workstruct ws; | ||
| 107 | ot_hash hash; | ||
| 108 | ot_torrent *torrent; | ||
| 109 | |||
| 110 | test_case("a swarm nobody touched is still cleaned away"); | ||
| 111 | memset(hash, 0x32, sizeof(hash)); | ||
| 112 | CHECK_INT(workstruct_init(&ws, G_INBUF_SIZE), 0); | ||
| 113 | |||
| 114 | announce(&ws, &hash, 3, 1); | ||
| 115 | CHECK_INT(peers_in(&hash), 1); | ||
| 116 | |||
| 117 | torrent = torrent_of(&hash); | ||
| 118 | CHECK(torrent != NULL); | ||
| 119 | step_clock_back(torrent, -OT_PEER_TIMEOUT); | ||
| 120 | |||
| 121 | CHECK_INT(clean_single_torrent(torrent), 0); | ||
| 122 | CHECK_INT(peers_in(&hash), 0); | ||
| 123 | |||
| 124 | workstruct_deinit(&ws); | ||
| 125 | } | ||
| 126 | |||
| 127 | void test_clean(void) { | ||
| 128 | sigset_t hup; | ||
| 129 | |||
| 130 | /* accesslist_deinit sends SIGHUP to its reader, trackerlogic_init starts | ||
| 131 | the accesslist, and the test process must not take it */ | ||
| 132 | sigemptyset(&hup); | ||
| 133 | sigaddset(&hup, SIGHUP); | ||
| 134 | pthread_sigmask(SIG_BLOCK, &hup, NULL); | ||
| 135 | |||
| 136 | trackerlogic_init(); | ||
| 137 | case_a_clock_that_went_back_keeps_the_swarm(); | ||
| 138 | case_an_idle_swarm_still_ages_out(); | ||
| 139 | trackerlogic_deinit(); | ||
| 140 | } | ||
diff --git a/tests/test.h b/tests/test.h index 82ec178..9b569c7 100644 --- a/tests/test.h +++ b/tests/test.h | |||
| @@ -86,6 +86,7 @@ void test_ip(void); | |||
| 86 | void test_socket(void); | 86 | void test_socket(void); |
| 87 | void test_mutex(void); | 87 | void test_mutex(void); |
| 88 | void test_trackerlogic(void); | 88 | void test_trackerlogic(void); |
| 89 | void test_clean(void); | ||
| 89 | void test_workqueue(void); | 90 | void test_workqueue(void); |
| 90 | void test_batch(void); | 91 | void test_batch(void); |
| 91 | void test_fdstate(void); | 92 | void test_fdstate(void); |
