diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | opentracker.c | 12 | ||||
-rw-r--r-- | opentracker.conf.sample | 37 | ||||
-rw-r--r-- | ot_accesslist.c | 281 | ||||
-rw-r--r-- | ot_accesslist.h | 15 | ||||
-rw-r--r-- | ot_clean.c | 4 | ||||
-rw-r--r-- | ot_mutex.h | 6 | ||||
-rw-r--r-- | ot_stats.c | 28 | ||||
-rw-r--r-- | ot_stats.h | 6 | ||||
-rw-r--r-- | trackerlogic.h | 5 |
10 files changed, 334 insertions, 61 deletions
@@ -24,6 +24,7 @@ STRIP?=strip | |||
24 | 24 | ||
25 | #FEATURES+=-DWANT_ACCESSLIST_BLACK | 25 | #FEATURES+=-DWANT_ACCESSLIST_BLACK |
26 | #FEATURES+=-DWANT_ACCESSLIST_WHITE | 26 | #FEATURES+=-DWANT_ACCESSLIST_WHITE |
27 | #FEATURES+=-DWANT_DYNAMIC_ACCESSLIST | ||
27 | 28 | ||
28 | #FEATURES+=-DWANT_SYNC_LIVE | 29 | #FEATURES+=-DWANT_SYNC_LIVE |
29 | #FEATURES+=-DWANT_IP_FROM_QUERY_STRING | 30 | #FEATURES+=-DWANT_IP_FROM_QUERY_STRING |
diff --git a/opentracker.c b/opentracker.c index be169be..2ca9e06 100644 --- a/opentracker.c +++ b/opentracker.c | |||
@@ -135,8 +135,8 @@ static void help( char *name ) { | |||
135 | } | 135 | } |
136 | #undef HELPLINE | 136 | #undef HELPLINE |
137 | 137 | ||
138 | static size_t header_complete( char * request, ssize_t byte_count ) { | 138 | static ssize_t header_complete( char * request, ssize_t byte_count ) { |
139 | int i = 0, state = 0; | 139 | ssize_t i = 0, state = 0; |
140 | 140 | ||
141 | for( i=1; i < byte_count; i+=2 ) | 141 | for( i=1; i < byte_count; i+=2 ) |
142 | if( request[i] <= 13 ) { | 142 | if( request[i] <= 13 ) { |
@@ -187,7 +187,7 @@ static void handle_read( const int64 sock, struct ot_workstruct *ws ) { | |||
187 | ws->request_size = byte_count; | 187 | ws->request_size = byte_count; |
188 | http_handle_request( sock, ws ); | 188 | http_handle_request( sock, ws ); |
189 | } else | 189 | } else |
190 | array_catb( &cookie->request, ws->inbuf, byte_count ); | 190 | array_catb( &cookie->request, ws->inbuf, (size_t)byte_count ); |
191 | return; | 191 | return; |
192 | } | 192 | } |
193 | 193 | ||
@@ -466,6 +466,12 @@ int parse_configfile( char * config_filename ) { | |||
466 | } else if(!byte_diff(p, 16, "access.blacklist" ) && isspace(p[16])) { | 466 | } else if(!byte_diff(p, 16, "access.blacklist" ) && isspace(p[16])) { |
467 | set_config_option( &g_accesslist_filename, p+17 ); | 467 | set_config_option( &g_accesslist_filename, p+17 ); |
468 | #endif | 468 | #endif |
469 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
470 | } else if(!byte_diff(p, 15, "access.fifo_add" ) && isspace(p[15])) { | ||
471 | set_config_option( &g_accesslist_pipe_add, p+16 ); | ||
472 | } else if(!byte_diff(p, 18, "access.fifo_delete" ) && isspace(p[18])) { | ||
473 | set_config_option( &g_accesslist_pipe_delete, p+19 ); | ||
474 | #endif | ||
469 | #ifdef WANT_RESTRICT_STATS | 475 | #ifdef WANT_RESTRICT_STATS |
470 | } else if(!byte_diff(p, 12, "access.stats" ) && isspace(p[12])) { | 476 | } else if(!byte_diff(p, 12, "access.stats" ) && isspace(p[12])) { |
471 | if( !scan_ip6( p+13, tmpip )) goto parse_error; | 477 | if( !scan_ip6( p+13, tmpip )) goto parse_error; |
diff --git a/opentracker.conf.sample b/opentracker.conf.sample index db45122..55c2828 100644 --- a/opentracker.conf.sample +++ b/opentracker.conf.sample | |||
@@ -44,6 +44,43 @@ | |||
44 | # listing, so choose one of those options at compile time. File format | 44 | # listing, so choose one of those options at compile time. File format |
45 | # is straight forward: "<hex info hash>\n<hex info hash>\n..." | 45 | # is straight forward: "<hex info hash>\n<hex info hash>\n..." |
46 | # | 46 | # |
47 | # IIa) You can enable dynamic changesets to accesslists by enabling | ||
48 | # WANT_DYNAMIC_ACCESSLIST. | ||
49 | # | ||
50 | # The suggested way to work with dynamic changeset lists is to keep a | ||
51 | # main accesslist file that is loaded when opentracker (re)starts and | ||
52 | # reloaded infrequently (hourly or daily). | ||
53 | # | ||
54 | # All changes to the accesslist (e.g. from a web frontend) should be | ||
55 | # both appended to or removed from that file and sent to opentracker. By | ||
56 | # keeping dynamic changeset lists, you can avoid reloading huge | ||
57 | # accesslists whenever just a single entry is added or removed. | ||
58 | # | ||
59 | # Any info_hash (format see above) written to the fifo_add file will be | ||
60 | # kept on a dynamic add-changeset, removed from the dynamic | ||
61 | # delete-changeset and treated as if it was in the main accesslist file. | ||
62 | # The semantic of the respective dynamic changeset depends on whether | ||
63 | # WANT_ACCESSLIST_WHITE or WANT_ACCESSLIST_BLACK is enabled. | ||
64 | # | ||
65 | # access.fifo_add /var/run/opentracker/adder.fifo | ||
66 | # | ||
67 | # Any info_hash (format see above) written to the fifo_delete file will | ||
68 | # be kept on a dynamic delete-changeset, removed from the dynamic | ||
69 | # add-changeset and treated as if it was not in the main accesslist | ||
70 | # file. | ||
71 | # | ||
72 | # access.fifo_delete /var/run/opentracker/deleter.fifo | ||
73 | # | ||
74 | # If you reload the accesslist by sending SIGHUP to the tracker process, | ||
75 | # the dynamic lists are flushed, as opentracker assumes thoses lists are | ||
76 | # merged into the main accesslist. | ||
77 | # | ||
78 | # NOTE: While you can have multiple writers sending lines to the fifos, | ||
79 | # any writes larger than PIPE_BUF (see your limits.h, minimally 512 | ||
80 | # bytes but usually 4096) may be interleaved with data sent by other | ||
81 | # writers. This can lead to unparsable lines of info_hashes. | ||
82 | # | ||
83 | # IIb) | ||
47 | # If you do not want to grant anyone access to your stats, enable the | 84 | # If you do not want to grant anyone access to your stats, enable the |
48 | # WANT_RESTRICT_STATS option in Makefile and bless the ip addresses | 85 | # WANT_RESTRICT_STATS option in Makefile and bless the ip addresses |
49 | # allowed to fetch stats here. | 86 | # allowed to fetch stats here. |
diff --git a/ot_accesslist.c b/ot_accesslist.c index a3a2049..7df503f 100644 --- a/ot_accesslist.c +++ b/ot_accesslist.c | |||
@@ -10,6 +10,11 @@ | |||
10 | #include <stdio.h> | 10 | #include <stdio.h> |
11 | #include <signal.h> | 11 | #include <signal.h> |
12 | #include <unistd.h> | 12 | #include <unistd.h> |
13 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
14 | #include <sys/types.h> | ||
15 | #include <sys/stat.h> | ||
16 | #include <errno.h> | ||
17 | #endif | ||
13 | 18 | ||
14 | /* Libowfat */ | 19 | /* Libowfat */ |
15 | #include "byte.h" | 20 | #include "byte.h" |
@@ -24,23 +29,80 @@ | |||
24 | 29 | ||
25 | /* GLOBAL VARIABLES */ | 30 | /* GLOBAL VARIABLES */ |
26 | #ifdef WANT_ACCESSLIST | 31 | #ifdef WANT_ACCESSLIST |
27 | char *g_accesslist_filename; | 32 | char *g_accesslist_filename = NULL; |
33 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
34 | char *g_accesslist_pipe_add = NULL; | ||
35 | char *g_accesslist_pipe_delete = NULL; | ||
36 | #endif | ||
28 | static pthread_mutex_t g_accesslist_mutex; | 37 | static pthread_mutex_t g_accesslist_mutex; |
29 | 38 | ||
30 | typedef struct { | 39 | /* Accesslists are lock free linked lists. We can not make them locking, because every announce |
31 | ot_hash *list; | 40 | would try to acquire the mutex, making it the most contested mutex in the whole of opentracker, |
32 | size_t size; | 41 | basically creating a central performance choke point. |
33 | } ot_accesslist; | 42 | |
34 | ot_accesslist * g_accesslist = NULL; | 43 | The idea is that updating the list heads happens under the g_accesslist_mutex guard and is |
35 | ot_accesslist * g_accesslist_old = NULL; | 44 | done atomically, while consumers might potentially still hold pointers deeper inside the list. |
45 | |||
46 | Consumers (for now only via accesslist_hashisvalid) will always fetch the list head pointer | ||
47 | that is guaranteed to live for at least five minutes. This should be many orders of magnitudes | ||
48 | more than how long it will be needed by the bsearch done on the list. */ | ||
49 | struct ot_accesslist; | ||
50 | typedef struct ot_accesslist ot_accesslist; | ||
51 | struct ot_accesslist { | ||
52 | ot_hash *list; | ||
53 | size_t size; | ||
54 | ot_time base; | ||
55 | ot_accesslist *next; | ||
56 | }; | ||
57 | static ot_accesslist * _Atomic g_accesslist = NULL; | ||
58 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
59 | static ot_accesslist * _Atomic g_accesslist_add = NULL; | ||
60 | static ot_accesslist * _Atomic g_accesslist_delete = NULL; | ||
61 | #endif | ||
36 | 62 | ||
63 | /* Helpers to work on access lists */ | ||
37 | static int vector_compare_hash(const void *hash1, const void *hash2 ) { | 64 | static int vector_compare_hash(const void *hash1, const void *hash2 ) { |
38 | return memcmp( hash1, hash2, OT_HASH_COMPARE_SIZE ); | 65 | return memcmp( hash1, hash2, OT_HASH_COMPARE_SIZE ); |
39 | } | 66 | } |
40 | 67 | ||
68 | static ot_accesslist * accesslist_free(ot_accesslist *accesslist) { | ||
69 | while (accesslist) { | ||
70 | ot_accesslist * this_accesslist = accesslist; | ||
71 | accesslist = this_accesslist->next; | ||
72 | free(this_accesslist->list); | ||
73 | free(this_accesslist); | ||
74 | } | ||
75 | return NULL; | ||
76 | } | ||
77 | |||
78 | static ot_accesslist * accesslist_make(ot_accesslist *next, size_t size) { | ||
79 | ot_accesslist * accesslist_new = malloc(sizeof(ot_accesslist)); | ||
80 | if (accesslist_new) { | ||
81 | accesslist_new->list = size ? malloc(sizeof(ot_hash) * size) : NULL; | ||
82 | accesslist_new->size = size; | ||
83 | accesslist_new->base = g_now_minutes; | ||
84 | accesslist_new->next = next; | ||
85 | if (size && !accesslist_new->list) { | ||
86 | free(accesslist_new); | ||
87 | accesslist_new = NULL; | ||
88 | } | ||
89 | } | ||
90 | return accesslist_new; | ||
91 | } | ||
92 | |||
93 | /* This must be called with g_accesslist_mutex held. | ||
94 | This will never delete head, because that might still be in use. */ | ||
95 | static void accesslist_clean(ot_accesslist *accesslist) { | ||
96 | while (accesslist && accesslist->next) { | ||
97 | if (accesslist->next->base + 5 < g_now_minutes) | ||
98 | accesslist->next = accesslist_free(accesslist->next); | ||
99 | accesslist = accesslist->next; | ||
100 | } | ||
101 | } | ||
102 | |||
41 | /* Read initial access list */ | 103 | /* Read initial access list */ |
42 | static void accesslist_readfile( void ) { | 104 | static void accesslist_readfile( void ) { |
43 | ot_accesslist * accesslist_new = malloc(sizeof(ot_accesslist)); | 105 | ot_accesslist * accesslist_new; |
44 | ot_hash *info_hash; | 106 | ot_hash *info_hash; |
45 | const char *map, *map_end, *read_offs; | 107 | const char *map, *map_end, *read_offs; |
46 | size_t maplen; | 108 | size_t maplen; |
@@ -54,14 +116,13 @@ static void accesslist_readfile( void ) { | |||
54 | 116 | ||
55 | /* You need at least 41 bytes to pass an info_hash, make enough room | 117 | /* You need at least 41 bytes to pass an info_hash, make enough room |
56 | for the maximum amount of them */ | 118 | for the maximum amount of them */ |
57 | accesslist_new->size = 0; | 119 | accesslist_new = accesslist_make(g_accesslist, maplen / 41); |
58 | info_hash = accesslist_new->list = malloc( ( maplen / 41 ) * 20 ); | 120 | if( !accesslist_new ) { |
59 | if( !accesslist_new->list ) { | ||
60 | fprintf( stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", ( maplen / 41 ) * 20 ); | 121 | fprintf( stderr, "Warning: Not enough memory to allocate %zd bytes for accesslist buffer. May succeed later.\n", ( maplen / 41 ) * 20 ); |
61 | mmap_unmap( map, maplen); | 122 | mmap_unmap( map, maplen); |
62 | free(accesslist_new); | ||
63 | return; | 123 | return; |
64 | } | 124 | } |
125 | info_hash = accesslist_new->list; | ||
65 | 126 | ||
66 | /* No use to scan if there's not enough room for another full info_hash */ | 127 | /* No use to scan if there's not enough room for another full info_hash */ |
67 | map_end = map + maplen - 40; | 128 | map_end = map + maplen - 40; |
@@ -71,18 +132,18 @@ static void accesslist_readfile( void ) { | |||
71 | while( read_offs <= map_end ) { | 132 | while( read_offs <= map_end ) { |
72 | int i; | 133 | int i; |
73 | for( i=0; i<(int)sizeof(ot_hash); ++i ) { | 134 | for( i=0; i<(int)sizeof(ot_hash); ++i ) { |
74 | int eger1 = scan_fromhex( read_offs[ 2*i ] ); | 135 | int eger1 = scan_fromhex( (unsigned char)read_offs[ 2*i ] ); |
75 | int eger2 = scan_fromhex( read_offs[ 1 + 2*i ] ); | 136 | int eger2 = scan_fromhex( (unsigned char)read_offs[ 1 + 2*i ] ); |
76 | if( eger1 < 0 || eger2 < 0 ) | 137 | if( eger1 < 0 || eger2 < 0 ) |
77 | break; | 138 | break; |
78 | (*info_hash)[i] = eger1 * 16 + eger2; | 139 | (*info_hash)[i] = (uint8_t)(eger1 * 16 + eger2); |
79 | } | 140 | } |
80 | 141 | ||
81 | if( i == sizeof(ot_hash) ) { | 142 | if( i == sizeof(ot_hash) ) { |
82 | read_offs += 40; | 143 | read_offs += 40; |
83 | 144 | ||
84 | /* Append accesslist to accesslist vector */ | 145 | /* Append accesslist to accesslist vector */ |
85 | if( read_offs == map_end || scan_fromhex( *read_offs ) < 0 ) | 146 | if( read_offs == map_end || scan_fromhex( (unsigned char)*read_offs ) < 0 ) |
86 | ++info_hash; | 147 | ++info_hash; |
87 | } | 148 | } |
88 | 149 | ||
@@ -100,14 +161,19 @@ static void accesslist_readfile( void ) { | |||
100 | 161 | ||
101 | /* Now exchange the accesslist vector in the least race condition prone way */ | 162 | /* Now exchange the accesslist vector in the least race condition prone way */ |
102 | pthread_mutex_lock(&g_accesslist_mutex); | 163 | pthread_mutex_lock(&g_accesslist_mutex); |
164 | accesslist_new->next = g_accesslist; | ||
165 | g_accesslist = accesslist_new; /* Only now set a new list */ | ||
166 | |||
167 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
168 | /* If we have dynamic accesslists, reloading a new one will always void the add/delete lists. | ||
169 | Insert empty ones at the list head */ | ||
170 | if (g_accesslist_add && (accesslist_new = accesslist_make(g_accesslist_add, 0)) != NULL) | ||
171 | g_accesslist_add = accesslist_new; | ||
172 | if (g_accesslist_delete && (accesslist_new = accesslist_make(g_accesslist_delete, 0)) != NULL) | ||
173 | g_accesslist_delete = accesslist_new; | ||
174 | #endif | ||
103 | 175 | ||
104 | if (g_accesslist_old) { | 176 | accesslist_clean(g_accesslist); |
105 | free(g_accesslist_old->list); | ||
106 | free(g_accesslist_old); | ||
107 | } | ||
108 | |||
109 | g_accesslist_old = g_accesslist; /* Keep a copy for later free */ | ||
110 | g_accesslist = accesslist_new; /* Only now set a new list */ | ||
111 | 177 | ||
112 | pthread_mutex_unlock(&g_accesslist_mutex); | 178 | pthread_mutex_unlock(&g_accesslist_mutex); |
113 | } | 179 | } |
@@ -115,11 +181,25 @@ static void accesslist_readfile( void ) { | |||
115 | int accesslist_hashisvalid( ot_hash hash ) { | 181 | int accesslist_hashisvalid( ot_hash hash ) { |
116 | /* Get working copy of current access list */ | 182 | /* Get working copy of current access list */ |
117 | ot_accesslist * accesslist = g_accesslist; | 183 | ot_accesslist * accesslist = g_accesslist; |
118 | 184 | #ifdef WANT_DYNAMIC_ACCESSLIST | |
185 | ot_accesslist * accesslist_add, * accesslist_delete; | ||
186 | #endif | ||
119 | void * exactmatch = NULL; | 187 | void * exactmatch = NULL; |
120 | 188 | ||
121 | if (accesslist) | 189 | if (accesslist) |
122 | exactmatch = bsearch( hash, accesslist->list, accesslist->size, OT_HASH_COMPARE_SIZE, vector_compare_hash ); | 190 | exactmatch = bsearch( hash, accesslist->list, accesslist->size, OT_HASH_COMPARE_SIZE, vector_compare_hash ); |
191 | |||
192 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
193 | /* If we had no match on the main list, scan the list of dynamically added hashes */ | ||
194 | accesslist_add = g_accesslist_add; | ||
195 | if ((exactmatch == NULL) && accesslist_add) | ||
196 | exactmatch = bsearch( hash, accesslist_add->list, accesslist_add->size, OT_HASH_COMPARE_SIZE, vector_compare_hash ); | ||
197 | |||
198 | /* If we found a matching hash on the main list, scan the list of dynamically deleted hashes */ | ||
199 | accesslist_delete = g_accesslist_delete; | ||
200 | if ((exactmatch != NULL) && accesslist_delete && bsearch( hash, accesslist_add->list, accesslist_add->size, OT_HASH_COMPARE_SIZE, vector_compare_hash )) | ||
201 | exactmatch = NULL; | ||
202 | #endif | ||
123 | 203 | ||
124 | #ifdef WANT_ACCESSLIST_BLACK | 204 | #ifdef WANT_ACCESSLIST_BLACK |
125 | return exactmatch == NULL; | 205 | return exactmatch == NULL; |
@@ -138,6 +218,8 @@ static void * accesslist_worker( void * args ) { | |||
138 | (void)args; | 218 | (void)args; |
139 | 219 | ||
140 | while( 1 ) { | 220 | while( 1 ) { |
221 | if (!g_opentracker_running) | ||
222 | return NULL; | ||
141 | 223 | ||
142 | /* Initial attempt to read accesslist */ | 224 | /* Initial attempt to read accesslist */ |
143 | accesslist_readfile( ); | 225 | accesslist_readfile( ); |
@@ -148,27 +230,156 @@ static void * accesslist_worker( void * args ) { | |||
148 | return NULL; | 230 | return NULL; |
149 | } | 231 | } |
150 | 232 | ||
233 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
234 | static pthread_t thread_adder_id, thread_deleter_id; | ||
235 | static void * accesslist_adddel_worker(char * fifoname, ot_accesslist * _Atomic * adding_to, ot_accesslist * _Atomic * removing_from) { | ||
236 | struct stat st; | ||
237 | |||
238 | if (!stat(fifoname, &st)) { | ||
239 | if (!S_ISFIFO(st.st_mode)) { | ||
240 | fprintf(stderr, "Error when starting dynamic accesslists: Found Non-FIFO file at %s.\nPlease remove it and restart opentracker.\n", fifoname); | ||
241 | return NULL; | ||
242 | } | ||
243 | } else { | ||
244 | int error = mkfifo(fifoname, 0755); | ||
245 | if (error && error != EEXIST) { | ||
246 | fprintf(stderr, "Error when starting dynamic accesslists: Couldn't create FIFO at %s, error: %s\n", fifoname, strerror(errno)); | ||
247 | return NULL; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | while (g_opentracker_running) { | ||
252 | FILE * fifo = fopen(fifoname, "r"); | ||
253 | char *line = NULL; | ||
254 | size_t linecap = 0; | ||
255 | ssize_t linelen; | ||
256 | |||
257 | if (!fifo) { | ||
258 | fprintf(stderr, "Error when reading dynamic accesslists: Couldn't open FIFO at %s, error: %s\n", fifoname, strerror(errno)); | ||
259 | return NULL; | ||
260 | } | ||
261 | |||
262 | while ((linelen = getline(&line, &linecap, fifo)) > 0) { | ||
263 | ot_hash info_hash; | ||
264 | int i; | ||
265 | |||
266 | printf("Got line %*s", (int)linelen, line); | ||
267 | /* We do ignore anything that is not of the form "^[:xdigit:]{40}[^:xdigit:].*" | ||
268 | If there's not enough characters for an info_hash in the line, skip it. */ | ||
269 | if (linelen < 41) | ||
270 | continue; | ||
271 | |||
272 | for( i=0; i<(int)sizeof(ot_hash); ++i ) { | ||
273 | int eger1 = scan_fromhex( (unsigned char)line[ 2*i ] ); | ||
274 | int eger2 = scan_fromhex( (unsigned char)line[ 1 + 2*i ] ); | ||
275 | if( eger1 < 0 || eger2 < 0 ) | ||
276 | break; | ||
277 | ((uint8_t*)info_hash)[i] = (uint8_t)(eger1 * 16 + eger2); | ||
278 | } | ||
279 | printf("parsed info_hash %20s\n", info_hash); | ||
280 | if( i != sizeof(ot_hash) ) | ||
281 | continue; | ||
282 | |||
283 | /* From now on we modify g_accesslist_add and g_accesslist_delete, so prevent the | ||
284 | other worker threads from doing the same */ | ||
285 | pthread_mutex_lock(&g_accesslist_mutex); | ||
286 | |||
287 | /* If the info hash is in the removing_from list, create a new head without that entry */ | ||
288 | if (*removing_from && (*removing_from)->list) { | ||
289 | ot_hash * exactmatch = bsearch( info_hash, (*removing_from)->list, (*removing_from)->size, OT_HASH_COMPARE_SIZE, vector_compare_hash ); | ||
290 | if (exactmatch) { | ||
291 | ptrdiff_t off = exactmatch - (*removing_from)->list; | ||
292 | ot_accesslist * accesslist_new = accesslist_make(*removing_from, (*removing_from)->size - 1); | ||
293 | if (accesslist_new) { | ||
294 | memcpy(accesslist_new->list, (*removing_from)->list, sizeof(ot_hash) * off); | ||
295 | memcpy(accesslist_new->list + off, (*removing_from)->list + off + 1, (*removing_from)->size - off - 1); | ||
296 | *removing_from = accesslist_new; | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | |||
301 | /* Simple case: there's no adding_to list yet, create one with one member */ | ||
302 | if (!*adding_to) { | ||
303 | ot_accesslist * accesslist_new = accesslist_make(NULL, 1); | ||
304 | if (accesslist_new) { | ||
305 | memcpy(accesslist_new->list, info_hash, sizeof(ot_hash)); | ||
306 | *adding_to = accesslist_new; | ||
307 | } | ||
308 | } else { | ||
309 | int exactmatch = 0; | ||
310 | ot_hash * insert_point = binary_search( info_hash, (*adding_to)->list, (*adding_to)->size, OT_HASH_COMPARE_SIZE, sizeof(ot_hash), &exactmatch ); | ||
311 | |||
312 | /* Only if the info hash is not in the adding_to list, create a new head with that entry */ | ||
313 | if (!exactmatch) { | ||
314 | ot_accesslist * accesslist_new = accesslist_make(*adding_to, (*adding_to)->size + 1); | ||
315 | ptrdiff_t off = insert_point - (*adding_to)->list; | ||
316 | if (accesslist_new) { | ||
317 | memcpy(accesslist_new->list, (*adding_to)->list, sizeof(ot_hash) * off); | ||
318 | memcpy(accesslist_new->list + off, info_hash, sizeof(info_hash)); | ||
319 | memcpy(accesslist_new->list + off + 1, (*adding_to)->list + off, (*adding_to)->size - off); | ||
320 | *adding_to = accesslist_new; | ||
321 | } | ||
322 | } | ||
323 | } | ||
324 | |||
325 | pthread_mutex_unlock(&g_accesslist_mutex); | ||
326 | } | ||
327 | |||
328 | fclose(fifo); | ||
329 | } | ||
330 | return NULL; | ||
331 | } | ||
332 | |||
333 | static void * accesslist_adder_worker( void * args ) { | ||
334 | (void)args; | ||
335 | return accesslist_adddel_worker(g_accesslist_pipe_add, &g_accesslist_add, &g_accesslist_delete); | ||
336 | } | ||
337 | static void * accesslist_deleter_worker( void * args ) { | ||
338 | (void)args; | ||
339 | return accesslist_adddel_worker(g_accesslist_pipe_delete, &g_accesslist_delete, &g_accesslist_add); | ||
340 | } | ||
341 | #endif | ||
342 | |||
151 | static pthread_t thread_id; | 343 | static pthread_t thread_id; |
152 | void accesslist_init( ) { | 344 | void accesslist_init( ) { |
153 | pthread_mutex_init(&g_accesslist_mutex, NULL); | 345 | pthread_mutex_init(&g_accesslist_mutex, NULL); |
154 | pthread_create( &thread_id, NULL, accesslist_worker, NULL ); | 346 | pthread_create( &thread_id, NULL, accesslist_worker, NULL ); |
347 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
348 | if (g_accesslist_pipe_add) | ||
349 | pthread_create( &thread_adder_id, NULL, accesslist_adder_worker, NULL ); | ||
350 | if (g_accesslist_pipe_delete) | ||
351 | pthread_create( &thread_deleter_id, NULL, accesslist_deleter_worker, NULL ); | ||
352 | #endif | ||
155 | } | 353 | } |
156 | 354 | ||
157 | void accesslist_deinit( void ) { | 355 | void accesslist_deinit( void ) { |
356 | /* Wake up sleeping worker */ | ||
357 | pthread_kill(thread_id, SIGHUP); | ||
358 | |||
359 | pthread_mutex_lock(&g_accesslist_mutex); | ||
360 | |||
361 | g_accesslist = accesslist_free(g_accesslist); | ||
362 | |||
363 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
364 | g_accesslist_add = accesslist_free(g_accesslist_add); | ||
365 | g_accesslist_delete = accesslist_free(g_accesslist_delete); | ||
366 | #endif | ||
367 | |||
368 | pthread_mutex_unlock(&g_accesslist_mutex); | ||
158 | pthread_cancel( thread_id ); | 369 | pthread_cancel( thread_id ); |
159 | pthread_mutex_destroy(&g_accesslist_mutex); | 370 | pthread_mutex_destroy(&g_accesslist_mutex); |
371 | } | ||
160 | 372 | ||
161 | if (g_accesslist_old) { | 373 | void accesslist_cleanup( void ) { |
162 | free(g_accesslist_old->list); | 374 | pthread_mutex_lock(&g_accesslist_mutex); |
163 | free(g_accesslist_old); | ||
164 | g_accesslist_old = 0; | ||
165 | } | ||
166 | 375 | ||
167 | if (g_accesslist) { | 376 | accesslist_clean(g_accesslist); |
168 | free(g_accesslist->list); | 377 | #if WANT_DYNAMIC_ACCESSLIST |
169 | free(g_accesslist); | 378 | accesslist_clean(g_accesslist_add); |
170 | g_accesslist = 0; | 379 | accesslist_clean(g_accesslist_delete); |
171 | } | 380 | #endif |
381 | |||
382 | pthread_mutex_unlock(&g_accesslist_mutex); | ||
172 | } | 383 | } |
173 | #endif | 384 | #endif |
174 | 385 | ||
diff --git a/ot_accesslist.h b/ot_accesslist.h index b38b91a..281f61b 100644 --- a/ot_accesslist.h +++ b/ot_accesslist.h | |||
@@ -6,19 +6,28 @@ | |||
6 | #ifndef OT_ACCESSLIST_H__ | 6 | #ifndef OT_ACCESSLIST_H__ |
7 | #define OT_ACCESSLIST_H__ | 7 | #define OT_ACCESSLIST_H__ |
8 | 8 | ||
9 | #if defined ( WANT_ACCESSLIST_BLACK ) && defined (WANT_ACCESSLIST_WHITE ) | 9 | #if defined ( WANT_ACCESSLIST_BLACK ) && defined ( WANT_ACCESSLIST_WHITE ) |
10 | # error WANT_ACCESSLIST_BLACK and WANT_ACCESSLIST_WHITE are exclusive. | 10 | # error WANT_ACCESSLIST_BLACK and WANT_ACCESSLIST_WHITE are exclusive. |
11 | #endif | 11 | #endif |
12 | 12 | ||
13 | #if defined ( WANT_ACCESSLIST_BLACK ) || defined (WANT_ACCESSLIST_WHITE ) | 13 | #if defined ( WANT_ACCESSLIST_BLACK ) || defined (WANT_ACCESSLIST_WHITE ) |
14 | #define WANT_ACCESSLIST | 14 | #define WANT_ACCESSLIST |
15 | void accesslist_init( ); | 15 | void accesslist_init( void ); |
16 | void accesslist_deinit( ); | 16 | void accesslist_deinit( void ); |
17 | int accesslist_hashisvalid( ot_hash hash ); | 17 | int accesslist_hashisvalid( ot_hash hash ); |
18 | void accesslist_cleanup( void ); | ||
18 | 19 | ||
19 | extern char *g_accesslist_filename; | 20 | extern char *g_accesslist_filename; |
21 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
22 | extern char *g_accesslist_pipe_add; | ||
23 | extern char *g_accesslist_pipe_delete; | ||
24 | #endif | ||
20 | 25 | ||
21 | #else | 26 | #else |
27 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
28 | # error WANT_DYNAMIC_ACCESSLIST needs either WANT_ACCESSLIST_BLACK or WANT_ACCESSLIST_WHITE | ||
29 | #endif | ||
30 | |||
22 | #define accesslist_init( accesslist_filename ) | 31 | #define accesslist_init( accesslist_filename ) |
23 | #define accesslist_deinit( ) | 32 | #define accesslist_deinit( ) |
24 | #define accesslist_hashisvalid( hash ) 1 | 33 | #define accesslist_hashisvalid( hash ) 1 |
@@ -17,6 +17,7 @@ | |||
17 | #include "ot_vector.h" | 17 | #include "ot_vector.h" |
18 | #include "ot_clean.h" | 18 | #include "ot_clean.h" |
19 | #include "ot_stats.h" | 19 | #include "ot_stats.h" |
20 | #include "ot_accesslist.h" | ||
20 | 21 | ||
21 | /* Returns amount of removed peers */ | 22 | /* Returns amount of removed peers */ |
22 | static ssize_t clean_single_bucket( ot_peer *peers, size_t peer_count, time_t timedout, int *removed_seeders ) { | 23 | static ssize_t clean_single_bucket( ot_peer *peers, size_t peer_count, time_t timedout, int *removed_seeders ) { |
@@ -123,6 +124,9 @@ static void * clean_worker( void * args ) { | |||
123 | usleep( OT_CLEAN_SLEEP ); | 124 | usleep( OT_CLEAN_SLEEP ); |
124 | } | 125 | } |
125 | stats_cleanup(); | 126 | stats_cleanup(); |
127 | #ifdef WANT_ACCESSLIST | ||
128 | accesslist_cleanup(); | ||
129 | #endif | ||
126 | } | 130 | } |
127 | return NULL; | 131 | return NULL; |
128 | } | 132 | } |
@@ -8,8 +8,8 @@ | |||
8 | 8 | ||
9 | #include <sys/uio.h> | 9 | #include <sys/uio.h> |
10 | 10 | ||
11 | void mutex_init( ); | 11 | void mutex_init( void ); |
12 | void mutex_deinit( ); | 12 | void mutex_deinit( void ); |
13 | 13 | ||
14 | ot_vector *mutex_bucket_lock( int bucket ); | 14 | ot_vector *mutex_bucket_lock( int bucket ); |
15 | ot_vector *mutex_bucket_lock_by_hash( ot_hash hash ); | 15 | ot_vector *mutex_bucket_lock_by_hash( ot_hash hash ); |
@@ -17,7 +17,7 @@ ot_vector *mutex_bucket_lock_by_hash( ot_hash hash ); | |||
17 | void mutex_bucket_unlock( int bucket, int delta_torrentcount ); | 17 | void mutex_bucket_unlock( int bucket, int delta_torrentcount ); |
18 | void mutex_bucket_unlock_by_hash( ot_hash hash, int delta_torrentcount ); | 18 | void mutex_bucket_unlock_by_hash( ot_hash hash, int delta_torrentcount ); |
19 | 19 | ||
20 | size_t mutex_get_torrent_count(); | 20 | size_t mutex_get_torrent_count(void); |
21 | 21 | ||
22 | typedef enum { | 22 | typedef enum { |
23 | TASK_STATS_CONNS = 0x0001, | 23 | TASK_STATS_CONNS = 0x0001, |
@@ -293,7 +293,7 @@ static int torrent_statter( ot_torrent *torrent, uintptr_t data ) { | |||
293 | /* Converter function from memory to human readable hex strings */ | 293 | /* Converter function from memory to human readable hex strings */ |
294 | 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;} | 294 | 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;} |
295 | 295 | ||
296 | typedef struct { size_t val; ot_torrent * torrent; } ot_record; | 296 | typedef struct { size_t val; ot_hash hash; } ot_record; |
297 | 297 | ||
298 | /* Fetches stats from tracker */ | 298 | /* Fetches stats from tracker */ |
299 | size_t stats_top_txt( char * reply, int amount ) { | 299 | size_t stats_top_txt( char * reply, int amount ) { |
@@ -311,18 +311,22 @@ size_t stats_top_txt( char * reply, int amount ) { | |||
311 | for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { | 311 | for( bucket=0; bucket<OT_BUCKET_COUNT; ++bucket ) { |
312 | ot_vector *torrents_list = mutex_bucket_lock( bucket ); | 312 | ot_vector *torrents_list = mutex_bucket_lock( bucket ); |
313 | for( j=0; j<torrents_list->size; ++j ) { | 313 | for( j=0; j<torrents_list->size; ++j ) { |
314 | ot_peerlist *peer_list = ( ((ot_torrent*)(torrents_list->data))[j] ).peer_list; | 314 | ot_torrent *torrent = (ot_torrent*)(torrents_list->data) + j; |
315 | int idx = amount - 1; while( (idx >= 0) && ( peer_list->peer_count > top100c[idx].val ) ) --idx; | 315 | idx = amount - 1; |
316 | while( (idx >= 0) && ( torrent->peer_list->peer_count > top100c[idx].val ) ) | ||
317 | --idx; | ||
316 | if ( idx++ != amount - 1 ) { | 318 | if ( idx++ != amount - 1 ) { |
317 | memmove( top100c + idx + 1, top100c + idx, ( amount - 1 - idx ) * sizeof( ot_record ) ); | 319 | memmove( top100c + idx + 1, top100c + idx, ( amount - 1 - idx ) * sizeof( ot_record ) ); |
318 | top100c[idx].val = peer_list->peer_count; | 320 | memcpy( &top100c[idx].hash, &torrent->hash, sizeof(ot_hash)); |
319 | top100c[idx].torrent = (ot_torrent*)(torrents_list->data) + j; | 321 | top100c[idx].val = torrent->peer_list->peer_count; |
320 | } | 322 | } |
321 | idx = amount - 1; while( (idx >= 0) && ( peer_list->seed_count > top100s[idx].val ) ) --idx; | 323 | idx = amount - 1; |
324 | while( (idx >= 0) && ( torrent->peer_list->seed_count > top100s[idx].val ) ) | ||
325 | --idx; | ||
322 | if ( idx++ != amount - 1 ) { | 326 | if ( idx++ != amount - 1 ) { |
323 | memmove( top100s + idx + 1, top100s + idx, ( amount - 1 - idx ) * sizeof( ot_record ) ); | 327 | memmove( top100s + idx + 1, top100s + idx, ( amount - 1 - idx ) * sizeof( ot_record ) ); |
324 | top100s[idx].val = peer_list->seed_count; | 328 | memcpy( &top100s[idx].hash, &torrent->hash, sizeof(ot_hash)); |
325 | top100s[idx].torrent = (ot_torrent*)(torrents_list->data) + j; | 329 | top100s[idx].val = torrent->peer_list->seed_count; |
326 | } | 330 | } |
327 | } | 331 | } |
328 | mutex_bucket_unlock( bucket, 0 ); | 332 | mutex_bucket_unlock( bucket, 0 ); |
@@ -332,12 +336,12 @@ size_t stats_top_txt( char * reply, int amount ) { | |||
332 | 336 | ||
333 | r += sprintf( r, "Top %d torrents by peers:\n", amount ); | 337 | r += sprintf( r, "Top %d torrents by peers:\n", amount ); |
334 | for( idx=0; idx<amount; ++idx ) | 338 | for( idx=0; idx<amount; ++idx ) |
335 | if( top100c[idx].torrent ) | 339 | if( top100c[idx].val ) |
336 | r += sprintf( r, "\t%zd\t%s\n", top100c[idx].val, to_hex( hex_out, top100c[idx].torrent->hash) ); | 340 | r += sprintf( r, "\t%zd\t%s\n", top100c[idx].val, to_hex( hex_out, top100c[idx].hash) ); |
337 | r += sprintf( r, "Top %d torrents by seeds:\n", amount ); | 341 | r += sprintf( r, "Top %d torrents by seeds:\n", amount ); |
338 | for( idx=0; idx<amount; ++idx ) | 342 | for( idx=0; idx<amount; ++idx ) |
339 | if( top100s[idx].torrent ) | 343 | if( top100s[idx].val ) |
340 | r += sprintf( r, "\t%zd\t%s\n", top100s[idx].val, to_hex( hex_out, top100s[idx].torrent->hash) ); | 344 | r += sprintf( r, "\t%zd\t%s\n", top100s[idx].val, to_hex( hex_out, top100s[idx].hash) ); |
341 | 345 | ||
342 | return r - reply; | 346 | return r - reply; |
343 | } | 347 | } |
@@ -40,11 +40,11 @@ enum { | |||
40 | 40 | ||
41 | void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ); | 41 | void stats_issue_event( ot_status_event event, PROTO_FLAG proto, uintptr_t event_data ); |
42 | void stats_deliver( int64 sock, int tasktype ); | 42 | void stats_deliver( int64 sock, int tasktype ); |
43 | void stats_cleanup(); | 43 | void stats_cleanup( void ); |
44 | size_t return_stats_for_tracker( char *reply, int mode, int format ); | 44 | size_t return_stats_for_tracker( char *reply, int mode, int format ); |
45 | size_t stats_return_tracker_version( char *reply ); | 45 | size_t stats_return_tracker_version( char *reply ); |
46 | void stats_init( ); | 46 | void stats_init( void ); |
47 | void stats_deinit( ); | 47 | void stats_deinit( void ); |
48 | 48 | ||
49 | extern const char *g_version_rijndael_c; | 49 | extern const char *g_version_rijndael_c; |
50 | extern const char *g_version_livesync_c; | 50 | extern const char *g_version_livesync_c; |
diff --git a/trackerlogic.h b/trackerlogic.h index 87b9138..ef59179 100644 --- a/trackerlogic.h +++ b/trackerlogic.h | |||
@@ -133,7 +133,6 @@ struct ot_workstruct { | |||
133 | char *peer_id; | 133 | char *peer_id; |
134 | 134 | ||
135 | /* HTTP specific, non static */ | 135 | /* HTTP specific, non static */ |
136 | int keep_alive; | ||
137 | char *request; | 136 | char *request; |
138 | ssize_t request_size; | 137 | ssize_t request_size; |
139 | ssize_t header_size; | 138 | ssize_t header_size; |
@@ -143,6 +142,8 @@ struct ot_workstruct { | |||
143 | /* Entropy state for rand48 function so that threads don't need to acquire mutexes for | 142 | /* Entropy state for rand48 function so that threads don't need to acquire mutexes for |
144 | global random() or arc4random() state, which causes heavy load on linuxes */ | 143 | global random() or arc4random() state, which causes heavy load on linuxes */ |
145 | uint16_t rand48_state[3]; | 144 | uint16_t rand48_state[3]; |
145 | |||
146 | int keep_alive; | ||
146 | }; | 147 | }; |
147 | 148 | ||
148 | /* | 149 | /* |
@@ -163,7 +164,7 @@ struct ot_workstruct { | |||
163 | #error Live logging networks disabled at the moment. | 164 | #error Live logging networks disabled at the moment. |
164 | #endif | 165 | #endif |
165 | 166 | ||
166 | void trackerlogic_init( ); | 167 | void trackerlogic_init( void ); |
167 | void trackerlogic_deinit( void ); | 168 | void trackerlogic_deinit( void ); |
168 | void exerr( char * message ); | 169 | void exerr( char * message ); |
169 | 170 | ||