An open and free bittorrent tracker https://erdgeist.org/gitweb/opentracker
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

774 lines
28 KiB

  1. /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
  2. It is considered beerware. Prost. Skol. Cheers or whatever.
  3. $id$ */
  4. /* System */
  5. #include <stdlib.h>
  6. #include <arpa/inet.h>
  7. #include <sys/types.h>
  8. #include <sys/uio.h>
  9. #include <sys/mman.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <pthread.h>
  13. #include <unistd.h>
  14. #include <inttypes.h>
  15. #ifdef WANT_SYSLOGS
  16. #include <syslog.h>
  17. #endif
  18. /* Libowfat */
  19. #include "byte.h"
  20. #include "io.h"
  21. #include "ip4.h"
  22. #include "ip6.h"
  23. /* Opentracker */
  24. #include "trackerlogic.h"
  25. #include "ot_mutex.h"
  26. #include "ot_iovec.h"
  27. #include "ot_stats.h"
  28. #include "ot_accesslist.h"
  29. #ifndef NO_FULLSCRAPE_LOGGING
  30. #define LOG_TO_STDERR( ... ) fprintf( stderr, __VA_ARGS__ )
  31. #else
  32. #define LOG_TO_STDERR( ... )
  33. #endif
  34. /* Forward declaration */
  35. static void stats_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode );
  36. #define OT_STATS_TMPSIZE 8192
  37. /* Clumsy counters... to be rethought */
  38. static unsigned long long ot_overall_tcp_connections = 0;
  39. static unsigned long long ot_overall_udp_connections = 0;
  40. static unsigned long long ot_overall_tcp_successfulannounces = 0;
  41. static unsigned long long ot_overall_udp_successfulannounces = 0;
  42. static unsigned long long ot_overall_tcp_successfulscrapes = 0;
  43. static unsigned long long ot_overall_udp_successfulscrapes = 0;
  44. static unsigned long long ot_overall_udp_connectionidmissmatches = 0;
  45. static unsigned long long ot_overall_tcp_connects = 0;
  46. static unsigned long long ot_overall_udp_connects = 0;
  47. static unsigned long long ot_overall_completed = 0;
  48. static unsigned long long ot_full_scrape_count = 0;
  49. static unsigned long long ot_full_scrape_request_count = 0;
  50. static unsigned long long ot_full_scrape_size = 0;
  51. static unsigned long long ot_failed_request_counts[CODE_HTTPERROR_COUNT];
  52. 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" };
  53. static unsigned long long ot_renewed[OT_PEER_TIMEOUT];
  54. static unsigned long long ot_overall_sync_count;
  55. static unsigned long long ot_overall_stall_count;
  56. static time_t ot_start_time;
  57. #define STATS_NETWORK_NODE_BITWIDTH 4
  58. #define STATS_NETWORK_NODE_COUNT (1<<STATS_NETWORK_NODE_BITWIDTH)
  59. #define __BYTE(P,D) (((uint8_t*)P)[D/8])
  60. #define __MSK (STATS_NETWORK_NODE_COUNT-1)
  61. #define __SHFT(D) ((D^STATS_NETWORK_NODE_BITWIDTH)&STATS_NETWORK_NODE_BITWIDTH)
  62. #define __LDR(P,D) ((__BYTE((P),(D))>>__SHFT((D)))&__MSK)
  63. #define __STR(P,D,V) __BYTE((P),(D))=(__BYTE((P),(D))&~(__MSK<<__SHFT((D))))|((V)<<__SHFT((D)))
  64. #ifdef WANT_V6
  65. #define STATS_NETWORK_NODE_MAXDEPTH (68-STATS_NETWORK_NODE_BITWIDTH)
  66. #define STATS_NETWORK_NODE_LIMIT (48-STATS_NETWORK_NODE_BITWIDTH)
  67. #else
  68. #define STATS_NETWORK_NODE_MAXDEPTH (28-STATS_NETWORK_NODE_BITWIDTH)
  69. #define STATS_NETWORK_NODE_LIMIT (24-STATS_NETWORK_NODE_BITWIDTH)
  70. #endif
  71. typedef union stats_network_node stats_network_node;
  72. union stats_network_node {
  73. size_t counters[STATS_NETWORK_NODE_COUNT];
  74. stats_network_node *children[STATS_NETWORK_NODE_COUNT];
  75. };
  76. #ifdef WANT_LOG_NETWORKS
  77. static stats_network_node *stats_network_counters_root;
  78. #endif
  79. static int stat_increase_network_count( stats_network_node **pnode, int depth, uintptr_t ip ) {
  80. int foo = __LDR(ip,depth);
  81. stats_network_node *node;
  82. if( !*pnode ) {
  83. *pnode = malloc( sizeof( stats_network_node ) );
  84. if( !*pnode )
  85. return -1;
  86. memset( *pnode, 0, sizeof( stats_network_node ) );
  87. }
  88. node = *pnode;
  89. if( depth < STATS_NETWORK_NODE_MAXDEPTH )
  90. return stat_increase_network_count( node->children + foo, depth+STATS_NETWORK_NODE_BITWIDTH, ip );
  91. node->counters[ foo ]++;
  92. return 0;
  93. }
  94. static int stats_shift_down_network_count( stats_network_node **node, int depth, int shift ) {
  95. int i, rest = 0;
  96. if( !*node )
  97. return 0;
  98. for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i )
  99. if( depth < STATS_NETWORK_NODE_MAXDEPTH )
  100. rest += stats_shift_down_network_count( (*node)->children + i, depth+STATS_NETWORK_NODE_BITWIDTH, shift );
  101. else
  102. rest += (*node)->counters[i] >>= shift;
  103. if( !rest ) {
  104. free( *node );
  105. *node = NULL;
  106. }
  107. return rest;
  108. }
  109. 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 ) {
  110. size_t score = 0;
  111. int i;
  112. if( !node ) return 0;
  113. if( depth < limit ) {
  114. for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i )
  115. if( node->children[i] ) {
  116. __STR(node_value,depth,i);
  117. score += stats_get_highscore_networks( node->children[i], depth+STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit );
  118. }
  119. return score;
  120. }
  121. if( depth > limit && depth < STATS_NETWORK_NODE_MAXDEPTH ) {
  122. for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i )
  123. if( node->children[i] )
  124. score += stats_get_highscore_networks( node->children[i], depth+STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit );
  125. return score;
  126. }
  127. if( depth > limit && depth == STATS_NETWORK_NODE_MAXDEPTH ) {
  128. for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i )
  129. score += node->counters[i];
  130. return score;
  131. }
  132. /* if( depth == limit ) */
  133. for( i=0; i<STATS_NETWORK_NODE_COUNT; ++i ) {
  134. int j=1;
  135. size_t node_score;
  136. if( depth == STATS_NETWORK_NODE_MAXDEPTH )
  137. node_score = node->counters[i];
  138. else
  139. node_score = stats_get_highscore_networks( node->children[i], depth+STATS_NETWORK_NODE_BITWIDTH, node_value, scores, networks, network_count, limit );
  140. score += node_score;
  141. if( node_score <= scores[0] ) continue;
  142. __STR(node_value,depth,i);
  143. while( j < network_count && node_score > scores[j] ) ++j;
  144. --j;
  145. memcpy( scores, scores + 1, j * sizeof( *scores ) );
  146. memcpy( networks, networks + 1, j * sizeof( *networks ) );
  147. scores[ j ] = node_score;
  148. memcpy( networks + j, node_value, sizeof( *networks ) );
  149. }
  150. return score;
  151. }
  152. static size_t stats_return_busy_networks( char * reply, stats_network_node *tree, int amount, int limit ) {
  153. ot_ip6 networks[amount];
  154. ot_ip6 node_value;
  155. size_t scores[amount];
  156. int i;
  157. char * r = reply;
  158. memset( scores, 0, sizeof( scores ) );
  159. memset( networks, 0, sizeof( networks ) );
  160. memset( node_value, 0, sizeof( node_value ) );
  161. stats_get_highscore_networks( tree, 0, node_value, scores, networks, amount, limit );
  162. r += sprintf( r, "Networks, limit /%d:\n", limit+STATS_NETWORK_NODE_BITWIDTH );
  163. for( i=amount-1; i>=0; --i) {
  164. if( scores[i] ) {
  165. r += sprintf( r, "%08zd: ", scores[i] );
  166. #ifdef WANT_V6
  167. r += fmt_ip6c( r, networks[i] );
  168. #else
  169. r += fmt_ip4( r, networks[i]);
  170. #endif
  171. *r++ = '\n';
  172. }
  173. }
  174. *r++ = '\n';
  175. return r - reply;
  176. }
  177. static size_t stats_slash24s_txt( char *reply, size_t amount ) {
  178. stats_network_node *slash24s_network_counters_root = NULL;
  179. char *r=reply;
  180. int bucket;
  181. size_t i;
  182. for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
  183. ot_vector *torrents_list = mutex_bucket_lock( bucket );
  184. for( i=0; i<torrents_list->size; ++i ) {
  185. ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[i] ).peer_list;
  186. ot_vector *bucket_list = &peer_list->peers;
  187. int num_buckets = 1;
  188. if( OT_PEERLIST_HASBUCKETS( peer_list ) ) {
  189. num_buckets = bucket_list->size;
  190. bucket_list = (ot_vector *)bucket_list->data;
  191. }
  192. while( num_buckets-- ) {
  193. ot_peer *peers = (ot_peer*)bucket_list->data;
  194. size_t numpeers = bucket_list->size;
  195. while( numpeers-- )
  196. if( stat_increase_network_count( &slash24s_network_counters_root, 0, (uintptr_t)(peers++) ) )
  197. goto bailout_unlock;
  198. ++bucket_list;
  199. }
  200. }
  201. mutex_bucket_unlock( bucket, 0 );
  202. if( !g_opentracker_running )
  203. goto bailout_error;
  204. }
  205. /* The tree is built. Now analyze */
  206. r += stats_return_busy_networks( r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_MAXDEPTH );
  207. r += stats_return_busy_networks( r, slash24s_network_counters_root, amount, STATS_NETWORK_NODE_LIMIT );
  208. goto success;
  209. bailout_unlock:
  210. mutex_bucket_unlock( bucket, 0 );
  211. bailout_error:
  212. r = reply;
  213. success:
  214. stats_shift_down_network_count( &slash24s_network_counters_root, 0, sizeof(int)*8-1 );
  215. return r-reply;
  216. }
  217. #ifdef WANT_SPOT_WOODPECKER
  218. static stats_network_node *stats_woodpeckers_tree;
  219. static pthread_mutex_t g_woodpeckers_mutex = PTHREAD_MUTEX_INITIALIZER;
  220. static size_t stats_return_woodpeckers( char * reply, int amount ) {
  221. char * r = reply;
  222. pthread_mutex_lock( &g_woodpeckers_mutex );
  223. r += stats_return_busy_networks( r, stats_woodpeckers_tree, amount, STATS_NETWORK_NODE_MAXDEPTH );
  224. pthread_mutex_unlock( &g_woodpeckers_mutex );
  225. return r-reply;
  226. }
  227. #endif
  228. typedef struct {
  229. unsigned long long torrent_count;
  230. unsigned long long peer_count;
  231. unsigned long long seed_count;
  232. } torrent_stats;
  233. static int torrent_statter( ot_torrent *torrent, uintptr_t data ) {
  234. torrent_stats *stats = (torrent_stats*)data;
  235. stats->torrent_count++;
  236. stats->peer_count += torrent->peer_list->peer_count;
  237. stats->seed_count += torrent->peer_list->seed_count;
  238. return 0;
  239. }
  240. /* Converter function from memory to human readable hex strings */
  241. 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;}
  242. typedef struct { size_t val; ot_torrent * torrent; } ot_record;
  243. /* Fetches stats from tracker */
  244. size_t stats_top_txt( char * reply, int amount ) {
  245. size_t j;
  246. ot_record top100s[100], top100c[100];
  247. char *r = reply, hex_out[42];
  248. int idx, bucket;
  249. if( amount > 100 )
  250. amount = 100;
  251. byte_zero( top100s, sizeof( top100s ) );
  252. byte_zero( top100c, sizeof( top100c ) );
  253. for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) {
  254. ot_vector *torrents_list = mutex_bucket_lock( bucket );
  255. for( j=0; j<torrents_list->size; ++j ) {
  256. ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list;
  257. int idx = amount - 1; while( (idx >= 0) && ( peer_list->peer_count > top100c[idx].val ) ) --idx;
  258. if ( idx++ != amount - 1 ) {
  259. memmove( top100c + idx + 1, top100c + idx, ( amount - 1 - idx ) * sizeof( ot_record ) );
  260. top100c[idx].val = peer_list->peer_count;
  261. top100c[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
  262. }
  263. idx = amount - 1; while( (idx >= 0) && ( peer_list->seed_count > top100s[idx].val ) ) --idx;
  264. if ( idx++ != amount - 1 ) {
  265. memmove( top100s + idx + 1, top100s + idx, ( amount - 1 - idx ) * sizeof( ot_record ) );
  266. top100s[idx].val = peer_list->seed_count;
  267. top100s[idx].torrent = (ot_torrent*)(torrents_list->data) + j;
  268. }
  269. }
  270. mutex_bucket_unlock( bucket, 0 );
  271. if( !g_opentracker_running )
  272. return 0;
  273. }
  274. r += sprintf( r, "Top %d torrents by peers:\n", amount );
  275. for( idx=0; idx<amount; ++idx )
  276. if( top100c[idx].torrent )
  277. r += sprintf( r, "\t%zd\t%s\n", top100c[idx].val, to_hex( hex_out, top100c[idx].torrent->hash) );
  278. r += sprintf( r, "Top %d torrents by seeds:\n", amount );
  279. for( idx=0; idx<amount; ++idx )
  280. if( top100s[idx].torrent )
  281. r += sprintf( r, "\t%zd\t%s\n", top100s[idx].val, to_hex( hex_out, top100s[idx].torrent->hash) );
  282. return r - reply;
  283. }
  284. static unsigned long events_per_time( unsigned long long events, time_t t ) {
  285. return events / ( (unsigned int)t ? (unsigned int)t : 1 );
  286. }
  287. static size_t stats_connections_mrtg( char * reply ) {
  288. ot_time t = time( NULL ) - ot_start_time;
  289. return sprintf( reply,
  290. "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.",
  291. ot_overall_tcp_connections+ot_overall_udp_connections,
  292. ot_overall_tcp_successfulannounces+ot_overall_udp_successfulannounces+ot_overall_udp_connects,
  293. (int)t,
  294. (int)(t / 3600),
  295. events_per_time( ot_overall_tcp_connections+ot_overall_udp_connections, t ),
  296. events_per_time( ot_overall_tcp_successfulannounces+ot_overall_udp_successfulannounces+ot_overall_udp_connects, t )
  297. );
  298. }
  299. static size_t stats_udpconnections_mrtg( char * reply ) {
  300. ot_time t = time( NULL ) - ot_start_time;
  301. return sprintf( reply,
  302. "%llu\n%llu\n%i seconds (%i hours)\nopentracker udp4 stats, %lu conns/s :: %lu success/s.",
  303. ot_overall_udp_connections,
  304. ot_overall_udp_successfulannounces+ot_overall_udp_connects,
  305. (int)t,
  306. (int)(t / 3600),
  307. events_per_time( ot_overall_udp_connections, t ),
  308. events_per_time( ot_overall_udp_successfulannounces+ot_overall_udp_connects, t )
  309. );
  310. }
  311. static size_t stats_tcpconnections_mrtg( char * reply ) {
  312. time_t t = time( NULL ) - ot_start_time;
  313. return sprintf( reply,
  314. "%llu\n%llu\n%i seconds (%i hours)\nopentracker tcp4 stats, %lu conns/s :: %lu success/s.",
  315. ot_overall_tcp_connections,
  316. ot_overall_tcp_successfulannounces,
  317. (int)t,
  318. (int)(t / 3600),
  319. events_per_time( ot_overall_tcp_connections, t ),
  320. events_per_time( ot_overall_tcp_successfulannounces, t )
  321. );
  322. }
  323. static size_t stats_scrape_mrtg( char * reply ) {
  324. time_t t = time( NULL ) - ot_start_time;
  325. return sprintf( reply,
  326. "%llu\n%llu\n%i seconds (%i hours)\nopentracker scrape stats, %lu scrape/s (tcp and udp)",
  327. ot_overall_tcp_successfulscrapes,
  328. ot_overall_udp_successfulscrapes,
  329. (int)t,
  330. (int)(t / 3600),
  331. events_per_time( (ot_overall_tcp_successfulscrapes+ot_overall_udp_successfulscrapes), t )
  332. );
  333. }
  334. static size_t stats_fullscrapes_mrtg( char * reply ) {
  335. ot_time t = time( NULL ) - ot_start_time;
  336. return sprintf( reply,
  337. "%llu\n%llu\n%i seconds (%i hours)\nopentracker full scrape stats, %lu conns/s :: %lu bytes/s.",
  338. ot_full_scrape_count * 1000,
  339. ot_full_scrape_size,
  340. (int)t,
  341. (int)(t / 3600),
  342. events_per_time( ot_full_scrape_count, t ),
  343. events_per_time( ot_full_scrape_size, t )
  344. );
  345. }
  346. static size_t stats_peers_mrtg( char * reply ) {
  347. torrent_stats stats = {0,0,0};
  348. iterate_all_torrents( torrent_statter, (uintptr_t)&stats );
  349. return sprintf( reply, "%llu\n%llu\nopentracker serving %llu torrents\nopentracker",
  350. stats.peer_count,
  351. stats.seed_count,
  352. stats.torrent_count
  353. );
  354. }
  355. static size_t stats_torrents_mrtg( char * reply )
  356. {
  357. size_t torrent_count = mutex_get_torrent_count();
  358. return sprintf( reply, "%zd\n%zd\nopentracker serving %zd torrents\nopentracker",
  359. torrent_count,
  360. (size_t)0,
  361. torrent_count
  362. );
  363. }
  364. static size_t stats_httperrors_txt ( char * reply ) {
  365. return sprintf( reply, "302 RED %llu\n400 ... %llu\n400 PAR %llu\n400 COM %llu\n403 IP %llu\n404 INV %llu\n500 SRV %llu\n",
  366. ot_failed_request_counts[0], ot_failed_request_counts[1], ot_failed_request_counts[2],
  367. ot_failed_request_counts[3], ot_failed_request_counts[4], ot_failed_request_counts[5],
  368. ot_failed_request_counts[6] );
  369. }
  370. static size_t stats_return_renew_bucket( char * reply ) {
  371. char *r = reply;
  372. int i;
  373. for( i=0; i<OT_PEER_TIMEOUT; ++i )
  374. r+=sprintf(r,"%02i %llu\n", i, ot_renewed[i] );
  375. return r - reply;
  376. }
  377. static size_t stats_return_sync_mrtg( char * reply ) {
  378. ot_time t = time( NULL ) - ot_start_time;
  379. return sprintf( reply,
  380. "%llu\n%llu\n%i seconds (%i hours)\nopentracker connections, %lu conns/s :: %lu success/s.",
  381. ot_overall_sync_count,
  382. 0LL,
  383. (int)t,
  384. (int)(t / 3600),
  385. events_per_time( ot_overall_tcp_connections+ot_overall_udp_connections, t ),
  386. events_per_time( ot_overall_tcp_successfulannounces+ot_overall_udp_successfulannounces+ot_overall_udp_connects, t )
  387. );
  388. }
  389. static size_t stats_return_completed_mrtg( char * reply ) {
  390. ot_time t = time( NULL ) - ot_start_time;
  391. return sprintf( reply,
  392. "%llu\n%llu\n%i seconds (%i hours)\nopentracker, %lu completed/h.",
  393. ot_overall_completed,
  394. 0LL,
  395. (int)t,
  396. (int)(t / 3600),
  397. events_per_time( ot_overall_completed, t / 3600 )
  398. );
  399. }
  400. #ifdef WANT_LOG_NUMWANT
  401. extern unsigned long long numwants[201];
  402. static size_t stats_return_numwants( char * reply ) {
  403. char * r = reply;
  404. int i;
  405. for( i=0; i<=200; ++i )
  406. r += sprintf( r, "%03d => %lld\n", i, numwants[i] );
  407. return r-reply;
  408. }
  409. #endif
  410. #ifdef WANT_FULLLOG_NETWORKS
  411. static void stats_return_fulllog( int *iovec_entries, struct iovec **iovector, char *r ) {
  412. ot_log *loglist = g_logchain_first, *llnext;
  413. char * re = r + OT_STATS_TMPSIZE;
  414. g_logchain_first = g_logchain_last = 0;
  415. while( loglist ) {
  416. if( r + ( loglist->size + 64 ) >= re ) {
  417. r = iovec_fix_increase_or_free( iovec_entries, iovector, r, 32 * OT_STATS_TMPSIZE );
  418. if( !r ) return;
  419. re = r + 32 * OT_STATS_TMPSIZE;
  420. }
  421. r += sprintf( r, "%08ld: ", loglist->time );
  422. r += fmt_ip6c( r, loglist->ip );
  423. *r++ = '\n';
  424. memcpy( r, loglist->data, loglist->size );
  425. r += loglist->size;
  426. *r++ = '\n';
  427. *r++ = '*';
  428. *r++ = '\n';
  429. *r++ = '\n';
  430. llnext = loglist->next;
  431. free( loglist->data );
  432. free( loglist );
  433. loglist = llnext;
  434. }
  435. iovec_fixlast( iovec_entries, iovector, r );
  436. }
  437. #endif
  438. static size_t stats_return_everything( char * reply ) {
  439. torrent_stats stats = {0,0,0};
  440. int i;
  441. char * r = reply;
  442. iterate_all_torrents( torrent_statter, (uintptr_t)&stats );
  443. r += sprintf( r, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
  444. r += sprintf( r, "<stats>\n" );
  445. r += sprintf( r, " <tracker_id>%" PRIu32 "</tracker_id>\n", g_tracker_id );
  446. r += sprintf( r, " <version>\n" ); r += stats_return_tracker_version( r ); r += sprintf( r, " </version>\n" );
  447. r += sprintf( r, " <uptime>%llu</uptime>\n", (unsigned long long)(time( NULL ) - ot_start_time) );
  448. r += sprintf( r, " <torrents>\n" );
  449. r += sprintf( r, " <count_mutex>%zd</count_mutex>\n", mutex_get_torrent_count() );
  450. r += sprintf( r, " <count_iterator>%llu</count_iterator>\n", stats.torrent_count );
  451. r += sprintf( r, " </torrents>\n" );
  452. r += sprintf( r, " <peers>\n <count>%llu</count>\n </peers>\n", stats.peer_count );
  453. r += sprintf( r, " <seeds>\n <count>%llu</count>\n </seeds>\n", stats.seed_count );
  454. r += sprintf( r, " <completed>\n <count>%llu</count>\n </completed>\n", ot_overall_completed );
  455. r += sprintf( r, " <connections>\n" );
  456. 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 );
  457. 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 );
  458. r += sprintf( r, " <livesync>\n <count>%llu</count>\n </livesync>\n", ot_overall_sync_count );
  459. r += sprintf( r, " </connections>\n" );
  460. r += sprintf( r, " <debug>\n" );
  461. r += sprintf( r, " <renew>\n" );
  462. for( i=0; i<OT_PEER_TIMEOUT; ++i )
  463. r += sprintf( r, " <count interval=\"%02i\">%llu</count>\n", i, ot_renewed[i] );
  464. r += sprintf( r, " </renew>\n" );
  465. r += sprintf( r, " <http_error>\n" );
  466. for( i=0; i<CODE_HTTPERROR_COUNT; ++i )
  467. r += sprintf( r, " <count code=\"%s\">%llu</count>\n", ot_failed_request_names[i], ot_failed_request_counts[i] );
  468. r += sprintf( r, " </http_error>\n" );
  469. r += sprintf( r, " <mutex_stall>\n <count>%llu</count>\n </mutex_stall>\n", ot_overall_stall_count );
  470. r += sprintf( r, " </debug>\n" );
  471. r += sprintf( r, "</stats>" );
  472. return r - reply;
  473. }
  474. extern const char
  475. *g_version_opentracker_c, *g_version_accesslist_c, *g_version_clean_c, *g_version_fullscrape_c, *g_version_http_c,
  476. *g_version_iovec_c, *g_version_mutex_c, *g_version_stats_c, *g_version_udp_c, *g_version_vector_c,
  477. *g_version_scan_urlencoded_query_c, *g_version_trackerlogic_c, *g_version_livesync_c, *g_version_rijndael_c;
  478. size_t stats_return_tracker_version( char *reply ) {
  479. return sprintf( reply, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
  480. g_version_opentracker_c, g_version_accesslist_c, g_version_clean_c, g_version_fullscrape_c, g_version_http_c,
  481. g_version_iovec_c, g_version_mutex_c, g_version_stats_c, g_version_udp_c, g_version_vector_c,
  482. g_version_scan_urlencoded_query_c, g_version_trackerlogic_c, g_version_livesync_c, g_version_rijndael_c );
  483. }
  484. size_t return_stats_for_tracker( char *reply, int mode, int format ) {
  485. (void) format;
  486. switch( mode & TASK_TASK_MASK ) {
  487. case TASK_STATS_CONNS:
  488. return stats_connections_mrtg( reply );
  489. case TASK_STATS_SCRAPE:
  490. return stats_scrape_mrtg( reply );
  491. case TASK_STATS_UDP:
  492. return stats_udpconnections_mrtg( reply );
  493. case TASK_STATS_TCP:
  494. return stats_tcpconnections_mrtg( reply );
  495. case TASK_STATS_FULLSCRAPE:
  496. return stats_fullscrapes_mrtg( reply );
  497. case TASK_STATS_COMPLETED:
  498. return stats_return_completed_mrtg( reply );
  499. case TASK_STATS_HTTPERRORS:
  500. return stats_httperrors_txt( reply );
  501. case TASK_STATS_VERSION:
  502. return stats_return_tracker_version( reply );
  503. case TASK_STATS_RENEW:
  504. return stats_return_renew_bucket( reply );
  505. case TASK_STATS_SYNCS:
  506. return stats_return_sync_mrtg( reply );
  507. #ifdef WANT_LOG_NUMWANT
  508. case TASK_STATS_NUMWANTS:
  509. return stats_return_numwants( reply );
  510. #endif
  511. default:
  512. return 0;
  513. }
  514. }
  515. static void stats_make( int *iovec_entries, struct iovec **iovector, ot_tasktype mode ) {
  516. char *r;
  517. *iovec_entries = 0;
  518. *iovector = NULL;
  519. if( !( r = iovec_increase( iovec_entries, iovector, OT_STATS_TMPSIZE ) ) )
  520. return;
  521. switch( mode & TASK_TASK_MASK ) {
  522. case TASK_STATS_TORRENTS: r += stats_torrents_mrtg( r ); break;
  523. case TASK_STATS_PEERS: r += stats_peers_mrtg( r ); break;
  524. case TASK_STATS_SLASH24S: r += stats_slash24s_txt( r, 128 ); break;
  525. case TASK_STATS_TOP10: r += stats_top_txt( r, 10 ); break;
  526. case TASK_STATS_TOP100:
  527. r = iovec_fix_increase_or_free( iovec_entries, iovector, r, 4 * OT_STATS_TMPSIZE );
  528. if( !r ) return;
  529. r += stats_top_txt( r, 100 ); break;
  530. case TASK_STATS_EVERYTHING: r += stats_return_everything( r ); break;
  531. #ifdef WANT_SPOT_WOODPECKER
  532. case TASK_STATS_WOODPECKERS: r += stats_return_woodpeckers( r, 128 ); break;
  533. #endif
  534. #ifdef WANT_FULLLOG_NETWORKS
  535. case TASK_STATS_FULLLOG: stats_return_fulllog( iovec_entries, iovector, r );
  536. return;
  537. #endif
  538. default:
  539. iovec_free(iovec_entries, iovector);
  540. return;
  541. }
  542. iovec_fixlast( iovec_entries, iovector, r );
  543. }
  544. void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ) {
  545. switch( event ) {
  546. case EVENT_ACCEPT:
  547. if( proto == FLAG_TCP ) ot_overall_tcp_connections++; else ot_overall_udp_connections++;
  548. #ifdef WANT_LOG_NETWORKS
  549. stat_increase_network_count( &stats_network_counters_root, 0, event_data );
  550. #endif
  551. break;
  552. case EVENT_ANNOUNCE:
  553. if( proto == FLAG_TCP ) ot_overall_tcp_successfulannounces++; else ot_overall_udp_successfulannounces++;
  554. break;
  555. case EVENT_CONNECT:
  556. if( proto == FLAG_TCP ) ot_overall_tcp_connects++; else ot_overall_udp_connects++;
  557. break;
  558. case EVENT_COMPLETED:
  559. #ifdef WANT_SYSLOGS
  560. if( event_data) {
  561. struct ot_workstruct *ws = (struct ot_workstruct *)event_data;
  562. char timestring[64];
  563. char hash_hex[42], peerid_hex[42], ip_readable[64];
  564. struct tm time_now;
  565. time_t ttt;
  566. time( &ttt );
  567. localtime_r( &ttt, &time_now );
  568. strftime( timestring, sizeof( timestring ), "%FT%T%z", &time_now );
  569. to_hex( hash_hex, *ws->hash );
  570. if( ws->peer_id )
  571. to_hex( peerid_hex, (uint8_t*)ws->peer_id );
  572. else {
  573. *peerid_hex=0;
  574. }
  575. #ifdef WANT_V6
  576. ip_readable[ fmt_ip6c( ip_readable, (char*)&ws->peer ) ] = 0;
  577. #else
  578. ip_readable[ fmt_ip4( ip_readable, (char*)&ws->peer ) ] = 0;
  579. #endif
  580. syslog( LOG_INFO, "time=%s event=completed info_hash=%s peer_id=%s ip=%s", timestring, hash_hex, peerid_hex, ip_readable );
  581. }
  582. #endif
  583. ot_overall_completed++;
  584. break;
  585. case EVENT_SCRAPE:
  586. if( proto == FLAG_TCP ) ot_overall_tcp_successfulscrapes++; else ot_overall_udp_successfulscrapes++;
  587. break;
  588. case EVENT_FULLSCRAPE:
  589. ot_full_scrape_count++;
  590. ot_full_scrape_size += event_data;
  591. break;
  592. case EVENT_FULLSCRAPE_REQUEST:
  593. {
  594. ot_ip6 *ip = (ot_ip6*)event_data; /* ugly hack to transfer ip to stats */
  595. char _debug[512];
  596. int off = snprintf( _debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time)/60 );
  597. off += fmt_ip6c( _debug+off, *ip );
  598. off += snprintf( _debug+off, sizeof(_debug)-off, " - FULL SCRAPE\n" );
  599. write( 2, _debug, off );
  600. ot_full_scrape_request_count++;
  601. }
  602. break;
  603. case EVENT_FULLSCRAPE_REQUEST_GZIP:
  604. {
  605. ot_ip6 *ip = (ot_ip6*)event_data; /* ugly hack to transfer ip to stats */
  606. char _debug[512];
  607. int off = snprintf( _debug, sizeof(_debug), "[%08d] scrp: ", (unsigned int)(g_now_seconds - ot_start_time)/60 );
  608. off += fmt_ip6c(_debug+off, *ip );
  609. off += snprintf( _debug+off, sizeof(_debug)-off, " - FULL SCRAPE\n" );
  610. write( 2, _debug, off );
  611. ot_full_scrape_request_count++;
  612. }
  613. break;
  614. case EVENT_FAILED:
  615. ot_failed_request_counts[event_data]++;
  616. break;
  617. case EVENT_RENEW:
  618. ot_renewed[event_data]++;
  619. break;
  620. case EVENT_SYNC:
  621. ot_overall_sync_count+=event_data;
  622. break;
  623. case EVENT_BUCKET_LOCKED:
  624. ot_overall_stall_count++;
  625. break;
  626. #ifdef WANT_SPOT_WOODPECKER
  627. case EVENT_WOODPECKER:
  628. pthread_mutex_lock( &g_woodpeckers_mutex );
  629. stat_increase_network_count( &stats_woodpeckers_tree, 0, event_data );
  630. pthread_mutex_unlock( &g_woodpeckers_mutex );
  631. break;
  632. #endif
  633. case EVENT_CONNID_MISSMATCH:
  634. ++ot_overall_udp_connectionidmissmatches;
  635. default:
  636. break;
  637. }
  638. }
  639. void stats_cleanup() {
  640. #ifdef WANT_SPOT_WOODPECKER
  641. pthread_mutex_lock( &g_woodpeckers_mutex );
  642. stats_shift_down_network_count( &stats_woodpeckers_tree, 0, 1 );
  643. pthread_mutex_unlock( &g_woodpeckers_mutex );
  644. #endif
  645. }
  646. static void * stats_worker( void * args ) {
  647. int iovec_entries;
  648. struct iovec *iovector;
  649. (void) args;
  650. while( 1 ) {
  651. ot_tasktype tasktype = TASK_STATS;
  652. ot_taskid taskid = mutex_workqueue_poptask( &tasktype );
  653. stats_make( &iovec_entries, &iovector, tasktype );
  654. if( mutex_workqueue_pushresult( taskid, iovec_entries, iovector ) )
  655. iovec_free( &iovec_entries, &iovector );
  656. }
  657. return NULL;
  658. }
  659. void stats_deliver( int64 sock, int tasktype ) {
  660. mutex_workqueue_pushtask( sock, tasktype );
  661. }
  662. static pthread_t thread_id;
  663. void stats_init( ) {
  664. ot_start_time = g_now_seconds;
  665. pthread_create( &thread_id, NULL, stats_worker, NULL );
  666. }
  667. void stats_deinit( ) {
  668. pthread_cancel( thread_id );
  669. }
  670. const char *g_version_stats_c = "$Source: /home/cvsroot/opentracker/ot_stats.c,v $: $Revision: 1.76 $\n";