1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
28 #include "trackerlogic.h"
32 #include "ot_accesslist.h"
34 #ifndef NO_FULLSCRAPE_LOGGING
35 #define LOG_TO_STDERR( ... ) fprintf( stderr, __VA_ARGS__ )
37 #define LOG_TO_STDERR( ... )
40 /* Forward declaration */
41 static void stats_make( int *iovec_entries
, struct iovec
**iovector
, ot_tasktype mode
);
42 #define OT_STATS_TMPSIZE 8192
44 /* Clumsy counters... to be rethought */
45 static unsigned long long ot_overall_tcp_connections
= 0;
46 static unsigned long long ot_overall_udp_connections
= 0;
47 static unsigned long long ot_overall_tcp_successfulannounces
= 0;
48 static unsigned long long ot_overall_udp_successfulannounces
= 0;
49 static unsigned long long ot_overall_tcp_successfulscrapes
= 0;
50 static unsigned long long ot_overall_udp_successfulscrapes
= 0;
51 static unsigned long long ot_overall_udp_connectionidmissmatches
= 0;
52 static unsigned long long ot_overall_tcp_connects
= 0;
53 static unsigned long long ot_overall_udp_connects
= 0;
54 static unsigned long long ot_overall_completed
= 0;
55 static unsigned long long ot_full_scrape_count
= 0;
56 static unsigned long long ot_full_scrape_request_count
= 0;
57 static unsigned long long ot_full_scrape_size
= 0;
58 static unsigned long long ot_failed_request_counts
[CODE_HTTPERROR_COUNT
];
59 static char * ot_failed_request_names
[] = { "302 Redirect", "400 Parse Error", "400 Invalid Parameter", "400 Invalid Parameter (compact=0)", "400 Not Modest", "402 Payment Required", "403 Access Denied", "404 Not found", "500 Internal Server Error" };
60 static unsigned long long ot_renewed
[OT_PEER_TIMEOUT
];
61 static unsigned long long ot_overall_sync_count
;
62 static unsigned long long ot_overall_stall_count
;
64 static time_t ot_start_time
;
66 #define STATS_NETWORK_NODE_BITWIDTH 4
67 #define STATS_NETWORK_NODE_COUNT (1<<STATS_NETWORK_NODE_BITWIDTH)
69 #define __BYTE(P,D) (((uint8_t*)P)[D/8])
70 #define __MSK (STATS_NETWORK_NODE_COUNT-1)
71 #define __SHFT(D) ((D^STATS_NETWORK_NODE_BITWIDTH)&STATS_NETWORK_NODE_BITWIDTH)
73 #define __LDR(P,D) ((__BYTE((P),(D))>>__SHFT((D)))&__MSK)
74 #define __STR(P,D,V) __BYTE((P),(D))=(__BYTE((P),(D))&~(__MSK<<__SHFT((D))))|((V)<<__SHFT((D)))
78 #define STATS_NETWORK_NODE_MAXDEPTH (68-STATS_NETWORK_NODE_BITWIDTH)
79 #define STATS_NETWORK_NODE_LIMIT (48-STATS_NETWORK_NODE_BITWIDTH)
81 #define STATS_NETWORK_NODE_MAXDEPTH (28-STATS_NETWORK_NODE_BITWIDTH)
82 #define STATS_NETWORK_NODE_LIMIT (24-STATS_NETWORK_NODE_BITWIDTH)
84 typedef union stats_network_node stats_network_node
;
85 union stats_network_node
{
86 size_t counters
[STATS_NETWORK_NODE_COUNT
];
87 stats_network_node
*children
[STATS_NETWORK_NODE_COUNT
];
90 #ifdef WANT_LOG_NETWORKS
91 static stats_network_node
*stats_network_counters_root
;
94 static int stat_increase_network_count( stats_network_node
**pnode
, int depth
, uintptr_t ip
) {
95 int foo
= __LDR(ip
,depth
);
96 stats_network_node
*node
;
99 *pnode
= malloc( sizeof( stats_network_node
) );
102 memset( *pnode
, 0, sizeof( stats_network_node
) );
106 if( depth
< STATS_NETWORK_NODE_MAXDEPTH
)
107 return stat_increase_network_count( node
->children
+ foo
, depth
+STATS_NETWORK_NODE_BITWIDTH
, ip
);
109 node
->counters
[ foo
]++;
113 static int stats_shift_down_network_count( stats_network_node
**node
, int depth
, int shift
) {
119 for( i
=0; i
<STATS_NETWORK_NODE_COUNT
; ++i
)
120 if( depth
< STATS_NETWORK_NODE_MAXDEPTH
)
121 rest
+= stats_shift_down_network_count( (*node
)->children
+ i
, depth
+STATS_NETWORK_NODE_BITWIDTH
, shift
);
123 rest
+= (*node
)->counters
[i
] >>= shift
;
133 static size_t stats_get_highscore_networks( stats_network_node
*node
, int depth
, ot_ip6 node_value
, size_t *scores
, ot_ip6
*networks
, int network_count
, int limit
) {
137 if( !node
) return 0;
139 if( depth
< limit
) {
140 for( i
=0; i
<STATS_NETWORK_NODE_COUNT
; ++i
)
141 if( node
->children
[i
] ) {
142 __STR(node_value
,depth
,i
);
143 score
+= stats_get_highscore_networks( node
->children
[i
], depth
+STATS_NETWORK_NODE_BITWIDTH
, node_value
, scores
, networks
, network_count
, limit
);
148 if( depth
> limit
&& depth
< STATS_NETWORK_NODE_MAXDEPTH
) {
149 for( i
=0; i
<STATS_NETWORK_NODE_COUNT
; ++i
)
150 if( node
->children
[i
] )
151 score
+= stats_get_highscore_networks( node
->children
[i
], depth
+STATS_NETWORK_NODE_BITWIDTH
, node_value
, scores
, networks
, network_count
, limit
);
155 if( depth
> limit
&& depth
== STATS_NETWORK_NODE_MAXDEPTH
) {
156 for( i
=0; i
<STATS_NETWORK_NODE_COUNT
; ++i
)
157 score
+= node
->counters
[i
];
161 /* if( depth == limit ) */
162 for( i
=0; i
<STATS_NETWORK_NODE_COUNT
; ++i
) {
166 if( depth
== STATS_NETWORK_NODE_MAXDEPTH
)
167 node_score
= node
->counters
[i
];
169 node_score
= stats_get_highscore_networks( node
->children
[i
], depth
+STATS_NETWORK_NODE_BITWIDTH
, node_value
, scores
, networks
, network_count
, limit
);
173 if( node_score
<= scores
[0] ) continue;
175 __STR(node_value
,depth
,i
);
176 while( j
< network_count
&& node_score
> scores
[j
] ) ++j
;
179 memcpy( scores
, scores
+ 1, j
* sizeof( *scores
) );
180 memcpy( networks
, networks
+ 1, j
* sizeof( *networks
) );
181 scores
[ j
] = node_score
;
182 memcpy( networks
+ j
, node_value
, sizeof( *networks
) );
188 static size_t stats_return_busy_networks( char * reply
, stats_network_node
*tree
, int amount
, int limit
) {
189 ot_ip6 networks
[amount
];
191 size_t scores
[amount
];
195 memset( scores
, 0, sizeof( scores
) );
196 memset( networks
, 0, sizeof( networks
) );
197 memset( node_value
, 0, sizeof( node_value
) );
199 stats_get_highscore_networks( tree
, 0, node_value
, scores
, networks
, amount
, limit
);
201 r
+= sprintf( r
, "Networks, limit /%d:\n", limit
+STATS_NETWORK_NODE_BITWIDTH
);
202 for( i
=amount
-1; i
>=0; --i
) {
204 r
+= sprintf( r
, "%08zd: ", scores
[i
] );
206 r
+= fmt_ip6c( r
, networks
[i
] );
209 r
+= fmt_ip4( r
, networks
[i
]);
219 static size_t stats_slash24s_txt( char *reply
, size_t amount
) {
220 stats_network_node
*slash24s_network_counters_root
= NULL
;
223 size_t i
, peer_size
= OT_PEER_SIZE4
;
225 for( bucket
=0; bucket
<OT_BUCKET_COUNT
; ++bucket
) {
226 ot_vector
*torrents_list
= mutex_bucket_lock( bucket
);
227 for( i
=0; i
<torrents_list
->size
; ++i
) {
228 ot_peerlist
*peer_list
= ( ((ot_torrent
*)(torrents_list
->data
))[i
] ).peer_list4
;
229 ot_vector
*bucket_list
= &peer_list
->peers
;
232 if( OT_PEERLIST_HASBUCKETS( peer_list
) ) {
233 num_buckets
= bucket_list
->size
;
234 bucket_list
= (ot_vector
*)bucket_list
->data
;
237 while( num_buckets
-- ) {
238 ot_peer
*peers
= (ot_peer
*)bucket_list
->data
;
239 size_t numpeers
= bucket_list
->size
;
240 while( numpeers
-- ) {
241 if( stat_increase_network_count( &slash24s_network_counters_root
, 0, (uintptr_t)(peers
) ) )
248 mutex_bucket_unlock( bucket
, 0 );
249 if( !g_opentracker_running
)
253 /* The tree is built. Now analyze */
254 r
+= stats_return_busy_networks( r
, slash24s_network_counters_root
, amount
, STATS_NETWORK_NODE_MAXDEPTH
);
255 r
+= stats_return_busy_networks( r
, slash24s_network_counters_root
, amount
, STATS_NETWORK_NODE_LIMIT
);
259 mutex_bucket_unlock( bucket
, 0 );
263 stats_shift_down_network_count( &slash24s_network_counters_root
, 0, sizeof(int)*8-1 );
268 #ifdef WANT_SPOT_WOODPECKER
269 static stats_network_node
*stats_woodpeckers_tree
;
270 static pthread_mutex_t g_woodpeckers_mutex
= PTHREAD_MUTEX_INITIALIZER
;
272 static size_t stats_return_woodpeckers( char * reply
, int amount
) {
275 pthread_mutex_lock( &g_woodpeckers_mutex
);
276 r
+= stats_return_busy_networks( r
, stats_woodpeckers_tree
, amount
, STATS_NETWORK_NODE_MAXDEPTH
);
277 pthread_mutex_unlock( &g_woodpeckers_mutex
);
283 unsigned long long torrent_count
;
284 unsigned long long peer_count
;
285 unsigned long long seed_count
;
288 static int torrent_statter( ot_torrent
*torrent
, uintptr_t data
) {
289 torrent_stats
*stats
= (torrent_stats
*)data
;
290 stats
->torrent_count
++;
291 stats
->peer_count
+= torrent
->peer_list6
->peer_count
+ torrent
->peer_list4
->peer_count
;
292 stats
->seed_count
+= torrent
->peer_list6
->seed_count
+ torrent
->peer_list4
->seed_count
;
296 /* Converter function from memory to human readable hex strings */
297 static char*to_hex(char*d
,uint8_t*s
){char*m
="0123456789ABCDEF";char *t
=d
;char*e
=d
+40;while(d
<e
){*d
++=m
[*s
>>4];*d
++=m
[*s
++&15];}*d
=0;return t
;}
299 typedef struct { size_t val
; ot_hash hash
; } ot_record
;
301 /* Fetches stats from tracker */
302 size_t stats_top_txt( char * reply
, int amount
) {
304 ot_record top100s
[100], top100c
[100];
305 char *r
= reply
, hex_out
[42];
311 byte_zero( top100s
, sizeof( top100s
) );
312 byte_zero( top100c
, sizeof( top100c
) );
314 for( bucket
=0; bucket
<OT_BUCKET_COUNT
; ++bucket
) {
315 ot_vector
*torrents_list
= mutex_bucket_lock( bucket
);
316 for( j
=0; j
<torrents_list
->size
; ++j
) {
317 ot_torrent
*torrent
= (ot_torrent
*)(torrents_list
->data
) + j
;
318 size_t peer_count
= torrent
->peer_list6
->peer_count
+ torrent
->peer_list4
->peer_count
;
319 size_t seed_count
= torrent
->peer_list6
->seed_count
+ torrent
->peer_list4
->seed_count
;
321 while( (idx
>= 0) && ( peer_count
> top100c
[idx
].val
) )
323 if ( idx
++ != amount
- 1 ) {
324 memmove( top100c
+ idx
+ 1, top100c
+ idx
, ( amount
- 1 - idx
) * sizeof( ot_record
) );
325 memcpy( &top100c
[idx
].hash
, &torrent
->hash
, sizeof(ot_hash
));
326 top100c
[idx
].val
= peer_count
;
329 while( (idx
>= 0) && ( seed_count
> top100s
[idx
].val
) )
331 if ( idx
++ != amount
- 1 ) {
332 memmove( top100s
+ idx
+ 1, top100s
+ idx
, ( amount
- 1 - idx
) * sizeof( ot_record
) );
333 memcpy( &top100s
[idx
].hash
, &torrent
->hash
, sizeof(ot_hash
));
334 top100s
[idx
].val
= seed_count
;
337 mutex_bucket_unlock( bucket
, 0 );
338 if( !g_opentracker_running
)
342 r
+= sprintf( r
, "Top %d torrents by peers:\n", amount
);
343 for( idx
=0; idx
<amount
; ++idx
)
344 if( top100c
[idx
].val
)
345 r
+= sprintf( r
, "\t%zd\t%s\n", top100c
[idx
].val
, to_hex( hex_out
, top100c
[idx
].hash
) );
346 r
+= sprintf( r
, "Top %d torrents by seeds:\n", amount
);
347 for( idx
=0; idx
<amount
; ++idx
)
348 if( top100s
[idx
].val
)
349 r
+= sprintf( r
, "\t%zd\t%s\n", top100s
[idx
].val
, to_hex( hex_out
, top100s
[idx
].hash
) );
354 static unsigned long events_per_time( unsigned long long events
, time_t t
) {
355 return events
/ ( (unsigned int)t
? (unsigned int)t
: 1 );
358 static size_t stats_connections_mrtg( char * reply
) {
359 ot_time t
= time( NULL
) - ot_start_time
;
360 return sprintf( reply
,
361 "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.",
362 ot_overall_tcp_connections
+ot_overall_udp_connections
,
363 ot_overall_tcp_successfulannounces
+ot_overall_udp_successfulannounces
+ot_overall_udp_connects
,
366 events_per_time( ot_overall_tcp_connections
+ot_overall_udp_connections
, t
),
367 events_per_time( ot_overall_tcp_successfulannounces
+ot_overall_udp_successfulannounces
+ot_overall_udp_connects
, t
)
371 static size_t stats_udpconnections_mrtg( char * reply
) {
372 ot_time t
= time( NULL
) - ot_start_time
;
373 return sprintf( reply
,
374 "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.",
375 ot_overall_udp_connections
,
376 ot_overall_udp_successfulannounces
+ot_overall_udp_connects
,
379 events_per_time( ot_overall_udp_connections
, t
),
380 events_per_time( ot_overall_udp_successfulannounces
+ot_overall_udp_connects
, t
)
384 static size_t stats_tcpconnections_mrtg( char * reply
) {
385 time_t t
= time( NULL
) - ot_start_time
;
386 return sprintf( reply
,
387 "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.",
388 ot_overall_tcp_connections
,
389 ot_overall_tcp_successfulannounces
,
392 events_per_time( ot_overall_tcp_connections
, t
),
393 events_per_time( ot_overall_tcp_successfulannounces
, t
)
397 static size_t stats_scrape_mrtg( char * reply
) {
398 time_t t
= time( NULL
) - ot_start_time
;
399 return sprintf( reply
,
400 "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)",
401 ot_overall_tcp_successfulscrapes
,
402 ot_overall_udp_successfulscrapes
,
405 events_per_time( (ot_overall_tcp_successfulscrapes
+ot_overall_udp_successfulscrapes
), t
)
409 static size_t stats_fullscrapes_mrtg( char * reply
) {
410 ot_time t
= time( NULL
) - ot_start_time
;
411 return sprintf( reply
,
412 "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.",
413 ot_full_scrape_count
* 1000,
417 events_per_time( ot_full_scrape_count
, t
),
418 events_per_time( ot_full_scrape_size
, t
)
422 static size_t stats_peers_mrtg( char * reply
) {
423 torrent_stats stats
= {0,0,0};
425 iterate_all_torrents( torrent_statter
, (uintptr_t)&stats
);
427 return sprintf( reply
, "%llu\n%llu\nopentracker serving %llu torrents\nopentracker",
434 static size_t stats_torrents_mrtg( char * reply
)
436 size_t torrent_count
= mutex_get_torrent_count();
438 return sprintf( reply
, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker",
445 static size_t stats_httperrors_txt ( char * reply
) {
446 return sprintf( reply
, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n",
447 ot_failed_request_counts
[0], ot_failed_request_counts
[1], ot_failed_request_counts
[2],
448 ot_failed_request_counts
[3], ot_failed_request_counts
[4], ot_failed_request_counts
[5],
449 ot_failed_request_counts
[6] );
452 static size_t stats_return_renew_bucket( char * reply
) {
456 for( i
=0; i
<OT_PEER_TIMEOUT
; ++i
)
457 r
+=sprintf(r
,"%02i %llu\n", i
, ot_renewed
[i
] );
461 static size_t stats_return_sync_mrtg( char * reply
) {
462 ot_time t
= time( NULL
) - ot_start_time
;
463 return sprintf( reply
,
464 "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.",
465 ot_overall_sync_count
,
469 events_per_time( ot_overall_tcp_connections
+ot_overall_udp_connections
, t
),
470 events_per_time( ot_overall_tcp_successfulannounces
+ot_overall_udp_successfulannounces
+ot_overall_udp_connects
, t
)
474 static size_t stats_return_completed_mrtg( char * reply
) {
475 ot_time t
= time( NULL
) - ot_start_time
;
477 return sprintf( reply
,
478 "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.",
479 ot_overall_completed
,
483 events_per_time( ot_overall_completed
, t
/ 3600 )
487 #ifdef WANT_LOG_NUMWANT
488 extern unsigned long long numwants
[201];
489 static size_t stats_return_numwants( char * reply
) {
492 for( i
=0; i
<=200; ++i
)
493 r
+= sprintf( r
, "%03d => %lld\n", i
, numwants
[i
] );
498 #ifdef WANT_FULLLOG_NETWORKS
499 static void stats_return_fulllog( int *iovec_entries
, struct iovec
**iovector
, char *r
) {
500 ot_log
*loglist
= g_logchain_first
, *llnext
;
501 char * re
= r
+ OT_STATS_TMPSIZE
;
503 g_logchain_first
= g_logchain_last
= 0;
506 if( r
+ ( loglist
->size
+ 64 ) >= re
) {
507 r
= iovec_fix_increase_or_free( iovec_entries
, iovector
, r
, 32 * OT_STATS_TMPSIZE
);
509 re
= r
+ 32 * OT_STATS_TMPSIZE
;
511 r
+= sprintf( r
, "%08ld: ", loglist
->time
);
512 r
+= fmt_ip6c( r
, loglist
->ip
);
514 memcpy( r
, loglist
->data
, loglist
->size
);
521 llnext
= loglist
->next
;
522 free( loglist
->data
);
526 iovec_fixlast( iovec_entries
, iovector
, r
);
530 static size_t stats_return_everything( char * reply
) {
531 torrent_stats stats
= {0,0,0};
535 iterate_all_torrents( torrent_statter
, (uintptr_t)&stats
);
537 r
+= sprintf( r
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
538 r
+= sprintf( r
, "<stats>\n" );
539 r
+= sprintf( r
, " <tracker_id>%" PRIu32
"</tracker_id>\n", g_tracker_id
);
540 r
+= sprintf( r
, " <version>\n" ); r
+= stats_return_tracker_version( r
); r
+= sprintf( r
, " </version>\n" );
541 r
+= sprintf( r
, " <uptime>%llu</uptime>\n", (unsigned long long)(time( NULL
) - ot_start_time
) );
542 r
+= sprintf( r
, " <torrents>\n" );
543 r
+= sprintf( r
, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count() );
544 r
+= sprintf( r
, " <count_iterator>%llu</count_iterator>\n", stats
.torrent_count
);
545 r
+= sprintf( r
, " </torrents>\n" );
546 r
+= sprintf( r
, " <peers>\n <count>%llu</count>\n </peers>\n", stats
.peer_count
);
547 r
+= sprintf( r
, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats
.seed_count
);
548 r
+= sprintf( r
, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed
);
549 r
+= sprintf( r
, " <connections>\n" );
550 r
+= sprintf( r
, " <tcp>\n <accept>%llu</accept>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n </tcp>\n", ot_overall_tcp_connections
, ot_overall_tcp_successfulannounces
, ot_overall_tcp_successfulscrapes
);
551 r
+= sprintf( r
, " <udp>\n <overall>%llu</overall>\n <connect>%llu</connect>\n <announce>%llu</announce>\n <scrape>%llu</scrape>\n <missmatch>%llu</missmatch>\n </udp>\n", ot_overall_udp_connections
, ot_overall_udp_connects
, ot_overall_udp_successfulannounces
, ot_overall_udp_successfulscrapes
, ot_overall_udp_connectionidmissmatches
);
552 r
+= sprintf( r
, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count
);
553 r
+= sprintf( r
, " </connections>\n" );
554 r
+= sprintf( r
, " <debug>\n" );
555 r
+= sprintf( r
, " <renew>\n" );
556 for( i
=0; i
<OT_PEER_TIMEOUT
; ++i
)
557 r
+= sprintf( r
, " <count interval=\"%02i\">%llu</count>\n", i
, ot_renewed
[i
] );
558 r
+= sprintf( r
, " </renew>\n" );
559 r
+= sprintf( r
, " <http_error>\n" );
560 for( i
=0; i
<CODE_HTTPERROR_COUNT
; ++i
)
561 r
+= sprintf( r
, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names
[i
], ot_failed_request_counts
[i
] );
562 r
+= sprintf( r
, " </http_error>\n" );
563 r
+= sprintf( r
, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", ot_overall_stall_count
);
564 r
+= sprintf( r
, " </debug>\n" );
565 r
+= sprintf( r
, "</stats>" );
570 *g_version_opentracker_c
, *g_version_accesslist_c
, *g_version_clean_c
, *g_version_fullscrape_c
, *g_version_http_c
,
571 *g_version_iovec_c
, *g_version_mutex_c
, *g_version_stats_c
, *g_version_udp_c
, *g_version_vector_c
,
572 *g_version_scan_urlencoded_query_c
, *g_version_trackerlogic_c
, *g_version_livesync_c
, *g_version_rijndael_c
;
574 size_t stats_return_tracker_version( char *reply
) {
575 return sprintf( reply
, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
576 g_version_opentracker_c
, g_version_accesslist_c
, g_version_clean_c
, g_version_fullscrape_c
, g_version_http_c
,
577 g_version_iovec_c
, g_version_mutex_c
, g_version_stats_c
, g_version_udp_c
, g_version_vector_c
,
578 g_version_scan_urlencoded_query_c
, g_version_trackerlogic_c
, g_version_livesync_c
, g_version_rijndael_c
);
581 size_t return_stats_for_tracker( char *reply
, int mode
, int format
) {
583 switch( mode
& TASK_TASK_MASK
) {
584 case TASK_STATS_CONNS
:
585 return stats_connections_mrtg( reply
);
586 case TASK_STATS_SCRAPE
:
587 return stats_scrape_mrtg( reply
);
589 return stats_udpconnections_mrtg( reply
);
591 return stats_tcpconnections_mrtg( reply
);
592 case TASK_STATS_FULLSCRAPE
:
593 return stats_fullscrapes_mrtg( reply
);
594 case TASK_STATS_COMPLETED
:
595 return stats_return_completed_mrtg( reply
);
596 case TASK_STATS_HTTPERRORS
:
597 return stats_httperrors_txt( reply
);
598 case TASK_STATS_VERSION
:
599 return stats_return_tracker_version( reply
);
600 case TASK_STATS_RENEW
:
601 return stats_return_renew_bucket( reply
);
602 case TASK_STATS_SYNCS
:
603 return stats_return_sync_mrtg( reply
);
604 #ifdef WANT_LOG_NUMWANT
605 case TASK_STATS_NUMWANTS
:
606 return stats_return_numwants( reply
);
613 static void stats_make( int *iovec_entries
, struct iovec
**iovector
, ot_tasktype mode
) {
618 if( !( r
= iovec_increase( iovec_entries
, iovector
, OT_STATS_TMPSIZE
) ) )
621 switch( mode
& TASK_TASK_MASK
) {
622 case TASK_STATS_TORRENTS
: r
+= stats_torrents_mrtg( r
); break;
623 case TASK_STATS_PEERS
: r
+= stats_peers_mrtg( r
); break;
624 case TASK_STATS_SLASH24S
: r
+= stats_slash24s_txt( r
, 128 ); break;
625 case TASK_STATS_TOP10
: r
+= stats_top_txt( r
, 10 ); break;
626 case TASK_STATS_TOP100
:
627 r
= iovec_fix_increase_or_free( iovec_entries
, iovector
, r
, 4 * OT_STATS_TMPSIZE
);
629 r
+= stats_top_txt( r
, 100 ); break;
630 case TASK_STATS_EVERYTHING
: r
= iovec_fix_increase_or_free( iovec_entries
, iovector
, r
, OT_STATS_TMPSIZE
+ 64 * OT_PEER_TIMEOUT
);
632 r
+= stats_return_everything( r
); break;
633 #ifdef WANT_SPOT_WOODPECKER
634 case TASK_STATS_WOODPECKERS
: r
+= stats_return_woodpeckers( r
, 128 ); break;
636 #ifdef WANT_FULLLOG_NETWORKS
637 case TASK_STATS_FULLLOG
: stats_return_fulllog( iovec_entries
, iovector
, r
);
641 iovec_free(iovec_entries
, iovector
);
644 iovec_fixlast( iovec_entries
, iovector
, r
);
647 void stats_issue_event( ot_status_event event
, PROTO_FLAG proto
, uintptr_t event_data
) {
650 if( proto
== FLAG_TCP
) ot_overall_tcp_connections
++; else ot_overall_udp_connections
++;
651 #ifdef WANT_LOG_NETWORKS
652 stat_increase_network_count( &stats_network_counters_root
, 0, event_data
);
656 if( proto
== FLAG_TCP
) ot_overall_tcp_successfulannounces
++; else ot_overall_udp_successfulannounces
++;
659 if( proto
== FLAG_TCP
) ot_overall_tcp_connects
++; else ot_overall_udp_connects
++;
661 case EVENT_COMPLETED
:
664 struct ot_workstruct
*ws
= (struct ot_workstruct
*)event_data
;
666 char hash_hex
[42], peerid_hex
[42], ip_readable
[64];
671 localtime_r( &ttt
, &time_now
);
672 strftime( timestring
, sizeof( timestring
), "%FT%T%z", &time_now
);
674 to_hex( hash_hex
, *ws
->hash
);
676 to_hex( peerid_hex
, (uint8_t*)ws
->peer_id
);
681 ip_readable
[ fmt_ip6c( ip_readable
, (char*)&ws
->peer
) ] = 0;
684 ip_readable
[ fmt_ip4( ip_readable
, (char*)&ws
->peer
) ] = 0;
686 syslog( LOG_INFO
, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring
, hash_hex
, peerid_hex
, ip_readable
);
689 ot_overall_completed
++;
692 if( proto
== FLAG_TCP
) ot_overall_tcp_successfulscrapes
++; else ot_overall_udp_successfulscrapes
++;
694 case EVENT_FULLSCRAPE
:
695 ot_full_scrape_count
++;
696 ot_full_scrape_size
+= event_data
;
698 case EVENT_FULLSCRAPE_REQUEST
:
700 ot_ip6
*ip
= (ot_ip6
*)event_data
; /* ugly hack to transfer ip to stats */
702 int off
= snprintf( _debug
, sizeof(_debug
), "[%08d] scrp: ", (unsigned int)(g_now_seconds
- ot_start_time
)/60 );
703 off
+= fmt_ip6c( _debug
+off
, *ip
);
704 off
+= snprintf( _debug
+off
, sizeof(_debug
)-off
, " - FULL SCRAPE\n" );
705 (void)write( 2, _debug
, off
);
706 ot_full_scrape_request_count
++;
709 case EVENT_FULLSCRAPE_REQUEST_GZIP
:
711 ot_ip6
*ip
= (ot_ip6
*)event_data
; /* ugly hack to transfer ip to stats */
713 int off
= snprintf( _debug
, sizeof(_debug
), "[%08d] scrp: ", (unsigned int)(g_now_seconds
- ot_start_time
)/60 );
714 off
+= fmt_ip6c(_debug
+off
, *ip
);
715 off
+= snprintf( _debug
+off
, sizeof(_debug
)-off
, " - FULL SCRAPE\n" );
716 (void)write( 2, _debug
, off
);
717 ot_full_scrape_request_count
++;
721 ot_failed_request_counts
[event_data
]++;
724 ot_renewed
[event_data
]++;
727 ot_overall_sync_count
+=event_data
;
729 case EVENT_BUCKET_LOCKED
:
730 ot_overall_stall_count
++;
732 #ifdef WANT_SPOT_WOODPECKER
733 case EVENT_WOODPECKER
:
734 pthread_mutex_lock( &g_woodpeckers_mutex
);
735 stat_increase_network_count( &stats_woodpeckers_tree
, 0, event_data
);
736 pthread_mutex_unlock( &g_woodpeckers_mutex
);
739 case EVENT_CONNID_MISSMATCH
:
740 ++ot_overall_udp_connectionidmissmatches
;
746 void stats_cleanup() {
747 #ifdef WANT_SPOT_WOODPECKER
748 pthread_mutex_lock( &g_woodpeckers_mutex
);
749 stats_shift_down_network_count( &stats_woodpeckers_tree
, 0, 1 );
750 pthread_mutex_unlock( &g_woodpeckers_mutex
);
754 static void * stats_worker( void * args
) {
756 struct iovec
*iovector
;
761 ot_tasktype tasktype
= TASK_STATS
;
762 ot_taskid taskid
= mutex_workqueue_poptask( &tasktype
);
763 stats_make( &iovec_entries
, &iovector
, tasktype
);
764 if( mutex_workqueue_pushresult( taskid
, iovec_entries
, iovector
) )
765 iovec_free( &iovec_entries
, &iovector
);
770 void stats_deliver( int64 sock
, int tasktype
) {
771 mutex_workqueue_pushtask( sock
, tasktype
);
774 static pthread_t thread_id
;
776 ot_start_time
= g_now_seconds
;
777 pthread_create( &thread_id
, NULL
, stats_worker
, NULL
);
780 void stats_deinit( ) {
781 pthread_cancel( thread_id
);
784 const char *g_version_stats_c
= "$Source$: $Revision$\n";