Make chunked transfers use gzip also
[opentracker.git] / opentracker.c
blob596c2a7f60a43f32ee5c7f6e653f64c136abde02
1 /* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3 Some of the stuff below is stolen from Fefes example libowfat httpd.
5 $Id$ */
7 /* System */
8 #include <stdlib.h>
9 #include <string.h>
10 #include <arpa/inet.h>
11 #include <sys/socket.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <stdio.h>
16 #include <pwd.h>
17 #include <ctype.h>
18 #include <pthread.h>
19 #ifdef WANT_SYSLOGS
20 #include <syslog.h>
21 #endif
23 /* Libowfat */
24 #include "socket.h"
25 #include "io.h"
26 #include "iob.h"
27 #include "byte.h"
28 #include "scan.h"
29 #include "ip6.h"
31 /* Opentracker */
32 #include "trackerlogic.h"
33 #include "ot_mutex.h"
34 #include "ot_http.h"
35 #include "ot_udp.h"
36 #include "ot_accesslist.h"
37 #include "ot_stats.h"
38 #include "ot_livesync.h"
40 /* Globals */
41 time_t g_now_seconds;
42 char * g_redirecturl;
43 uint32_t g_tracker_id;
44 volatile int g_opentracker_running = 1;
45 int g_self_pipe[2];
47 static char * g_serverdir;
48 static char * g_serveruser;
49 static unsigned int g_udp_workers;
51 static void panic( const char *routine ) __attribute__ ((noreturn));
52 static void panic( const char *routine ) {
53 fprintf( stderr, "%s: %s\n", routine, strerror(errno) );
54 exit( 111 );
57 static void signal_handler( int s ) {
58 if( s == SIGINT ) {
59 /* Any new interrupt signal quits the application */
60 signal( SIGINT, SIG_DFL);
62 /* Tell all other threads to not acquire any new lock on a bucket
63 but cancel their operations and return */
64 g_opentracker_running = 0;
66 trackerlogic_deinit();
68 #ifdef WANT_SYSLOGS
69 closelog();
70 #endif
72 exit( 0 );
76 static void defaul_signal_handlers( void ) {
77 sigset_t signal_mask;
78 sigemptyset(&signal_mask);
79 sigaddset (&signal_mask, SIGPIPE);
80 sigaddset (&signal_mask, SIGHUP);
81 sigaddset (&signal_mask, SIGINT);
82 sigaddset (&signal_mask, SIGALRM);
83 pthread_sigmask (SIG_BLOCK, &signal_mask, NULL);
86 static void install_signal_handlers( void ) {
87 struct sigaction sa;
88 sigset_t signal_mask;
89 sigemptyset(&signal_mask);
91 sa.sa_handler = signal_handler;
92 sigemptyset(&sa.sa_mask);
93 sa.sa_flags = SA_RESTART;
94 if ((sigaction(SIGINT, &sa, NULL) == -1) || (sigaction(SIGALRM, &sa, NULL) == -1) )
95 panic( "install_signal_handlers" );
97 sigaddset (&signal_mask, SIGINT);
98 pthread_sigmask (SIG_UNBLOCK, &signal_mask, NULL);
101 static void usage( char *name ) {
102 fprintf( stderr, "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip[/bits]] [-f config] [-s livesyncport]"
103 #ifdef WANT_ACCESSLIST_BLACK
104 " [-b blacklistfile]"
105 #elif defined ( WANT_ACCESSLIST_WHITE )
106 " [-w whitelistfile]"
107 #endif
108 "\n", name );
111 #define HELPLINE(opt,desc) fprintf(stderr, "\t%-10s%s\n",opt,desc)
112 static void help( char *name ) {
113 usage( name );
115 HELPLINE("-f config","include and execute the config file");
116 HELPLINE("-i ip","specify ip to bind to with next -[pP] (default: any, overrides preceeding ones)");
117 HELPLINE("-p port","do bind to tcp port (default: 6969, you may specify more than one)");
118 HELPLINE("-P port","do bind to udp port (default: 6969, you may specify more than one)");
119 HELPLINE("-r redirecturl","specify url where / should be redirected to (default none)");
120 HELPLINE("-d dir","specify directory to try to chroot to (default: \".\")");
121 HELPLINE("-u user","specify user under whose privileges opentracker should run (default: \"nobody\")");
122 HELPLINE("-A ip[/bits]","bless an ip address or net as admin address (e.g. to allow syncs from this address)");
123 #ifdef WANT_ACCESSLIST_BLACK
124 HELPLINE("-b file","specify blacklist file.");
125 #elif defined( WANT_ACCESSLIST_WHITE )
126 HELPLINE("-w file","specify whitelist file.");
127 #endif
129 fprintf( stderr, "\nExample: ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -f ./opentracker.conf -i 10.1.1.23 -p 2710 -p 80\n" );
130 fprintf( stderr, " Here -i 127.0.0.1 selects the ip address for the next -p 6969 and -P 6969.\n");
131 fprintf( stderr, " If no port is bound from config file or command line, the last address given\n");
132 fprintf( stderr, " (or ::1 if none is set) will be used on port 6969.\n");
134 #undef HELPLINE
136 static ssize_t header_complete( char * request, ssize_t byte_count ) {
137 ssize_t i = 0, state = 0;
139 for( i=1; i < byte_count; i+=2 )
140 if( request[i] <= 13 ) {
141 i--;
142 for( state = 0 ; i < byte_count; ++i ) {
143 char c = request[i];
144 if( c == '\r' || c == '\n' )
145 state = ( state >> 2 ) | ( ( c << 6 ) & 0xc0 );
146 else
147 break;
148 if( state >= 0xa0 || state == 0x99 ) return i + 1;
151 return 0;
154 static void handle_dead( const int64 sock ) {
155 struct http_data* cookie=io_getcookie( sock );
156 if( cookie ) {
157 size_t i;
158 for ( i = 0; i < cookie->batches; ++i)
159 iob_reset( cookie->batch + i );
160 free( cookie->batch );
161 array_reset( &cookie->request );
162 if( cookie->flag & STRUCT_HTTP_FLAG_WAITINGFORTASK )
163 mutex_workqueue_canceltask( sock );
164 free( cookie );
166 io_close( sock );
169 static void handle_read( const int64 sock, struct ot_workstruct *ws ) {
170 struct http_data* cookie = io_getcookie( sock );
171 ssize_t byte_count = io_tryread( sock, ws->inbuf, G_INBUF_SIZE );
173 if( byte_count == 0 || byte_count == -3 ) {
174 handle_dead( sock );
175 return;
178 if( byte_count == -1)
179 return;
181 /* If we get the whole request in one packet, handle it without copying */
182 if( !array_start( &cookie->request ) ) {
183 if( ( ws->header_size = header_complete( ws->inbuf, byte_count ) ) ) {
184 ws->request = ws->inbuf;
185 ws->request_size = byte_count;
186 http_handle_request( sock, ws );
187 } else
188 array_catb( &cookie->request, ws->inbuf, (size_t)byte_count );
189 return;
192 array_catb( &cookie->request, ws->inbuf, byte_count );
193 if( array_failed( &cookie->request ) || array_bytes( &cookie->request ) > 8192 ) {
194 http_issue_error( sock, ws, CODE_HTTPERROR_500 );
195 return;
198 while( ( ws->header_size = header_complete( array_start( &cookie->request ), array_bytes( &cookie->request ) ) ) ) {
199 ws->request = array_start( &cookie->request );
200 ws->request_size = array_bytes( &cookie->request );
201 http_handle_request( sock, ws );
202 #ifdef WANT_KEEPALIVE
203 if( !ws->keep_alive )
204 #endif
205 return;
209 static void handle_write( const int64 sock ) {
210 struct http_data* cookie=io_getcookie( sock );
211 size_t i;
212 int chunked = 0;
214 /* Look for the first io_batch still containing bytes to write */
215 if( cookie ) {
216 if( cookie->flag & STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER )
217 chunked = 1;
219 for( i = 0; i < cookie->batches; ++i ) {
220 fprintf(stderr, "handle_write inspects batch %d of %d (bytes left: %d)\n", i, cookie->batches, cookie->batch[i].bytesleft);
221 if( cookie->batch[i].bytesleft ) {
222 int64 res = iob_send( sock, cookie->batch + i );
224 fprintf(stderr, "handle_write yields res %lld when trying to iob_send\n", res);
225 if( res == -3 ) {
226 handle_dead( sock );
227 return;
230 if( !cookie->batch[i].bytesleft )
231 continue;
233 if( res == -1 || res > 0 || i < cookie->batches - 1 )
234 return;
239 /* In a chunked transfer after all batches accumulated have been sent, wait for the next one */
240 if( chunked ) {
241 fprintf( stderr, "handle_write is STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER => dont want write on sock %lld\n", sock);
242 io_dontwantwrite( sock );
243 } else {
244 fprintf( stderr, "handle_write is STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER => handle dead on sock %lld\n", sock);
245 handle_dead( sock );
249 static void handle_accept( const int64 serversocket ) {
250 struct http_data *cookie;
251 int64 sock;
252 ot_ip6 ip;
253 uint16 port;
254 tai6464 t;
256 while( ( sock = socket_accept6( serversocket, ip, &port, NULL ) ) != -1 ) {
258 /* Put fd into a non-blocking mode */
259 io_nonblock( sock );
261 if( !io_fd( sock ) ||
262 !( cookie = (struct http_data*)malloc( sizeof(struct http_data) ) ) ) {
263 io_close( sock );
264 continue;
266 memset(cookie, 0, sizeof( struct http_data ) );
267 memcpy(cookie->ip,ip,sizeof(ot_ip6));
269 io_setcookie( sock, cookie );
270 io_wantread( sock );
272 stats_issue_event( EVENT_ACCEPT, FLAG_TCP, (uintptr_t)ip);
274 /* That breaks taia encapsulation. But there is no way to take system
275 time this often in FreeBSD and libowfat does not allow to set unix time */
276 taia_uint( &t, 0 ); /* Clear t */
277 tai_unix( &(t.sec), (g_now_seconds + OT_CLIENT_TIMEOUT) );
278 io_timeout( sock, t );
280 io_eagain(serversocket);
283 static void * server_mainloop( void * args ) {
284 struct ot_workstruct ws;
285 time_t next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
286 struct iovec *iovector;
287 int iovec_entries, is_partial;
289 (void)args;
291 /* Initialize our "thread local storage" */
292 ws.inbuf = malloc( G_INBUF_SIZE );
293 ws.outbuf = malloc( G_OUTBUF_SIZE );
294 #ifdef _DEBUG_HTTPERROR
295 ws.debugbuf= malloc( G_DEBUGBUF_SIZE );
296 #endif
298 if( !ws.inbuf || !ws.outbuf )
299 panic( "Initializing worker failed" );
301 #ifdef WANT_ARC4RANDOM
302 arc4random_buf(&ws.rand48_state[0], 3 * sizeof(uint16_t));
303 #else
304 ws.rand48_state[0] = (uint16_t)random();
305 ws.rand48_state[1] = (uint16_t)random();
306 ws.rand48_state[2] = (uint16_t)random();
307 #endif
309 for( ; ; ) {
310 int64 sock;
312 io_wait();
314 while( ( sock = io_canread( ) ) != -1 ) {
315 const void *cookie = io_getcookie( sock );
316 if( (intptr_t)cookie == FLAG_TCP )
317 handle_accept( sock );
318 else if( (intptr_t)cookie == FLAG_UDP )
319 handle_udp6( sock, &ws );
320 else if( (intptr_t)cookie == FLAG_SELFPIPE )
321 io_tryread( sock, ws.inbuf, G_INBUF_SIZE );
322 else
323 handle_read( sock, &ws );
326 while( ( sock = mutex_workqueue_popresult( &iovec_entries, &iovector, &is_partial ) ) != -1 )
327 http_sendiovecdata( sock, &ws, iovec_entries, iovector, is_partial );
329 while( ( sock = io_canwrite( ) ) != -1 )
330 handle_write( sock );
332 if( g_now_seconds > next_timeout_check ) {
333 while( ( sock = io_timeouted() ) != -1 )
334 handle_dead( sock );
335 next_timeout_check = g_now_seconds + OT_CLIENT_TIMEOUT_CHECKINTERVAL;
338 livesync_ticker();
340 return 0;
343 static int64_t ot_try_bind( ot_ip6 ip, uint16_t port, PROTO_FLAG proto ) {
344 int64 sock = proto == FLAG_TCP ? socket_tcp6( ) : socket_udp6( );
346 #ifdef _DEBUG
348 char *protos[] = {"TCP","UDP","UDP mcast"};
349 char _debug[512];
350 int off = snprintf( _debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto] );
351 off += fmt_ip6c( _debug+off, ip);
352 snprintf( _debug + off, sizeof(_debug)-off, "]:%d...", port);
353 fputs( _debug, stderr );
355 #endif
357 if( socket_bind6_reuse( sock, ip, port, 0 ) == -1 )
358 panic( "socket_bind6_reuse" );
360 if( ( proto == FLAG_TCP ) && ( socket_listen( sock, SOMAXCONN) == -1 ) )
361 panic( "socket_listen" );
363 if( !io_fd( sock ) )
364 panic( "io_fd" );
366 io_setcookie( sock, (void*)proto );
368 if( (proto == FLAG_UDP) && g_udp_workers ) {
369 io_block( sock );
370 udp_init( sock, g_udp_workers );
371 } else
372 io_wantread( sock );
374 #ifdef _DEBUG
375 fputs( " success.\n", stderr);
376 #endif
378 return sock;
381 char * set_config_option( char **option, char *value ) {
382 #ifdef _DEBUG
383 fprintf( stderr, "Setting config option: %s\n", value );
384 #endif
385 while( isspace(*value) ) ++value;
386 free( *option );
387 return *option = strdup( value );
390 static int scan_ip6_port( const char *src, ot_ip6 ip, uint16 *port ) {
391 const char *s = src;
392 int off, bracket = 0;
393 while( isspace(*s) ) ++s;
394 if( *s == '[' ) ++s, ++bracket; /* for v6 style notation */
395 if( !(off = scan_ip6( s, ip ) ) )
396 return 0;
397 s += off;
398 if( bracket && *s == ']' ) ++s;
399 if( *s == 0 || isspace(*s)) return s-src;
400 if( !ip6_isv4mapped(ip)) {
401 if( *s != ':' && *s != '.' ) return 0;
402 if( !bracket && *(s) == ':' ) return 0;
403 s++;
404 } else {
405 if( *(s++) != ':' ) return 0;
407 if( !(off = scan_ushort (s, port ) ) )
408 return 0;
409 return off+s-src;
412 static int scan_ip6_net( const char *src, ot_net *net) {
413 const char *s = src;
414 int off;
415 while( isspace(*s) ) ++s;
416 if( !(off = scan_ip6( s, net->address ) ) )
417 return 0;
418 s += off;
419 if(*s!='/')
420 net->bits = 128;
421 else {
422 s++;
423 if( !(off = scan_int (s, &net->bits ) ) )
424 return 0;
425 if( ip6_isv4mapped(net->address))
426 net->bits += 96;
427 if(net->bits > 128)
428 return 0;
429 s += off;
431 return off+s-src;
434 int parse_configfile( char * config_filename ) {
435 FILE * accesslist_filehandle;
436 char inbuf[512];
437 ot_ip6 tmpip;
438 #if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE)
439 ot_net tmpnet;
440 #endif
441 int bound = 0;
443 accesslist_filehandle = fopen( config_filename, "r" );
445 if( accesslist_filehandle == NULL ) {
446 fprintf( stderr, "Warning: Can't open config file: %s.", config_filename );
447 return 0;
450 while( fgets( inbuf, sizeof(inbuf), accesslist_filehandle ) ) {
451 char *p = inbuf;
452 size_t strl;
454 /* Skip white spaces */
455 while(isspace(*p)) ++p;
457 /* Ignore comments and empty lines */
458 if((*p=='#')||(*p=='\n')||(*p==0)) continue;
460 /* consume trailing new lines and spaces */
461 strl = strlen(p);
462 while( strl && isspace(p[strl-1]))
463 p[--strl] = 0;
465 /* Scan for commands */
466 if(!byte_diff(p,15,"tracker.rootdir" ) && isspace(p[15])) {
467 set_config_option( &g_serverdir, p+16 );
468 } else if(!byte_diff(p,12,"tracker.user" ) && isspace(p[12])) {
469 set_config_option( &g_serveruser, p+13 );
470 } else if(!byte_diff(p,14,"listen.tcp_udp" ) && isspace(p[14])) {
471 uint16_t tmpport = 6969;
472 if( !scan_ip6_port( p+15, tmpip, &tmpport )) goto parse_error;
473 ot_try_bind( tmpip, tmpport, FLAG_TCP ); ++bound;
474 ot_try_bind( tmpip, tmpport, FLAG_UDP ); ++bound;
475 } else if(!byte_diff(p,10,"listen.tcp" ) && isspace(p[10])) {
476 uint16_t tmpport = 6969;
477 if( !scan_ip6_port( p+11, tmpip, &tmpport )) goto parse_error;
478 ot_try_bind( tmpip, tmpport, FLAG_TCP );
479 ++bound;
480 } else if(!byte_diff(p, 10, "listen.udp" ) && isspace(p[10])) {
481 uint16_t tmpport = 6969;
482 if( !scan_ip6_port( p+11, tmpip, &tmpport )) goto parse_error;
483 ot_try_bind( tmpip, tmpport, FLAG_UDP );
484 ++bound;
485 } else if(!byte_diff(p,18,"listen.udp.workers" ) && isspace(p[18])) {
486 char *value = p + 18;
487 while( isspace(*value) ) ++value;
488 scan_uint( value, &g_udp_workers );
489 #ifdef WANT_ACCESSLIST_WHITE
490 } else if(!byte_diff(p, 16, "access.whitelist" ) && isspace(p[16])) {
491 set_config_option( &g_accesslist_filename, p+17 );
492 #elif defined( WANT_ACCESSLIST_BLACK )
493 } else if(!byte_diff(p, 16, "access.blacklist" ) && isspace(p[16])) {
494 set_config_option( &g_accesslist_filename, p+17 );
495 #endif
496 #ifdef WANT_DYNAMIC_ACCESSLIST
497 } else if(!byte_diff(p, 15, "access.fifo_add" ) && isspace(p[15])) {
498 set_config_option( &g_accesslist_pipe_add, p+16 );
499 } else if(!byte_diff(p, 18, "access.fifo_delete" ) && isspace(p[18])) {
500 set_config_option( &g_accesslist_pipe_delete, p+19 );
501 #endif
502 #ifdef WANT_RESTRICT_STATS
503 } else if(!byte_diff(p, 12, "access.stats" ) && isspace(p[12])) {
504 if( !scan_ip6_net( p+13, &tmpnet )) goto parse_error;
505 accesslist_bless_net( &tmpnet, OT_PERMISSION_MAY_STAT );
506 #endif
507 } else if(!byte_diff(p, 17, "access.stats_path" ) && isspace(p[17])) {
508 set_config_option( &g_stats_path, p+18 );
509 #ifdef WANT_IP_FROM_PROXY
510 } else if(!byte_diff(p, 12, "access.proxy" ) && isspace(p[12])) {
511 if( !scan_ip6_net( p+13, &tmpnet )) goto parse_error;
512 accesslist_bless_net( &tmpnet, OT_PERMISSION_MAY_PROXY );
513 #endif
514 } else if(!byte_diff(p, 20, "tracker.redirect_url" ) && isspace(p[20])) {
515 set_config_option( &g_redirecturl, p+21 );
516 #ifdef WANT_SYNC_LIVE
517 } else if(!byte_diff(p, 24, "livesync.cluster.node_ip" ) && isspace(p[24])) {
518 if( !scan_ip6_net( p+25, &tmpnet )) goto parse_error;
519 accesslist_bless_net( &tmpnet, OT_PERMISSION_MAY_LIVESYNC );
520 } else if(!byte_diff(p, 23, "livesync.cluster.listen" ) && isspace(p[23])) {
521 uint16_t tmpport = LIVESYNC_PORT;
522 if( !scan_ip6_port( p+24, tmpip, &tmpport )) goto parse_error;
523 livesync_bind_mcast( tmpip, tmpport );
524 #endif
525 } else
526 fprintf( stderr, "Unhandled line in config file: %s\n", inbuf );
527 continue;
528 parse_error:
529 fprintf( stderr, "Parse error in config file: %s\n", inbuf);
531 fclose( accesslist_filehandle );
532 return bound;
535 void load_state(const char * const state_filename ) {
536 FILE * state_filehandle;
537 char inbuf[512];
538 ot_hash infohash;
539 unsigned long long base, downcount;
540 int consumed;
542 state_filehandle = fopen( state_filename, "r" );
544 if( state_filehandle == NULL ) {
545 fprintf( stderr, "Warning: Can't open config file: %s.", state_filename );
546 return;
549 /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */
550 while( fgets( inbuf, sizeof(inbuf), state_filehandle ) ) {
551 int i;
552 for( i=0; i<(int)sizeof(ot_hash); ++i ) {
553 int eger = 16 * scan_fromhex( inbuf[ 2*i ] ) + scan_fromhex( inbuf[ 1 + 2*i ] );
554 if( eger < 0 )
555 continue;
556 infohash[i] = eger;
559 if( i != (int)sizeof(ot_hash) ) continue;
560 i *= 2;
562 if( inbuf[ i++ ] != ':' || !( consumed = scan_ulonglong( inbuf+i, &base ) ) ) continue;
563 i += consumed;
564 if( inbuf[ i++ ] != ':' || !( consumed = scan_ulonglong( inbuf+i, &downcount ) ) ) continue;
565 add_torrent_from_saved_state( infohash, base, downcount );
568 fclose( state_filehandle );
571 int drop_privileges ( const char * const serveruser, const char * const serverdir ) {
572 struct passwd *pws = NULL;
574 #ifdef _DEBUG
575 if( !geteuid() )
576 fprintf( stderr, "Dropping to user %s.\n", serveruser );
577 if( serverdir )
578 fprintf( stderr, "ch%s'ing to directory %s.\n", geteuid() ? "dir" : "root", serverdir );
579 #endif
581 /* Grab pws entry before chrooting */
582 pws = getpwnam( serveruser );
583 endpwent();
585 if( geteuid() == 0 ) {
586 /* Running as root: chroot and drop privileges */
587 if( serverdir && chroot( serverdir ) ) {
588 fprintf( stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno) );
589 return -1;
592 if(chdir("/"))
593 panic("chdir() failed after chrooting: ");
595 /* If we can't find server user, revert to nobody's default uid */
596 if( !pws ) {
597 fprintf( stderr, "Warning: Could not get password entry for %s. Reverting to uid -2.\n", serveruser );
598 if (setegid( (gid_t)-2 ) || setgid( (gid_t)-2 ) || setuid( (uid_t)-2 ) || seteuid( (uid_t)-2 ))
599 panic("Could not set uid to value -2");
601 else {
602 if (setegid( pws->pw_gid ) || setgid( pws->pw_gid ) || setuid( pws->pw_uid ) || seteuid( pws->pw_uid ))
603 panic("Could not set uid to specified value");
606 if( geteuid() == 0 || getegid() == 0 )
607 panic("Still running with root privileges?!");
609 else {
610 /* Normal user, just chdir() */
611 if( serverdir && chdir( serverdir ) ) {
612 fprintf( stderr, "Could not chroot to %s, because: %s\n", serverdir, strerror(errno) );
613 return -1;
617 return 0;
620 /* Maintain our copy of the clock. time() on BSDs is very expensive. */
621 static void *time_caching_worker(void*args) {
622 (void)args;
623 while (1) {
624 g_now_seconds = time(NULL);
625 sleep(5);
629 int main( int argc, char **argv ) {
630 ot_ip6 serverip;
631 ot_net tmpnet;
632 int bound = 0, scanon = 1;
633 uint16_t tmpport;
634 char * statefile = 0;
635 pthread_t thread_id; /* time cacher */
637 memset( serverip, 0, sizeof(ot_ip6) );
638 #ifdef WANT_V4_ONLY
639 serverip[10]=serverip[11]=-1;
640 #endif
642 #ifdef WANT_DEV_RANDOM
643 srandomdev();
644 #else
645 srandom( time(NULL) );
646 #endif
648 while( scanon ) {
649 switch( getopt( argc, argv, ":i:p:A:P:d:u:r:s:f:l:v"
650 #ifdef WANT_ACCESSLIST_BLACK
651 "b:"
652 #elif defined( WANT_ACCESSLIST_WHITE )
653 "w:"
654 #endif
655 "h" ) ) {
656 case -1 : scanon = 0; break;
657 case 'i':
658 if( !scan_ip6( optarg, serverip )) { usage( argv[0] ); exit( 1 ); }
659 break;
660 #ifdef WANT_ACCESSLIST_BLACK
661 case 'b': set_config_option( &g_accesslist_filename, optarg); break;
662 #elif defined( WANT_ACCESSLIST_WHITE )
663 case 'w': set_config_option( &g_accesslist_filename, optarg); break;
664 #endif
665 case 'p':
666 if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); }
667 ot_try_bind( serverip, tmpport, FLAG_TCP ); bound++; break;
668 case 'P':
669 if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); }
670 ot_try_bind( serverip, tmpport, FLAG_UDP ); bound++; break;
671 #ifdef WANT_SYNC_LIVE
672 case 's':
673 if( !scan_ushort( optarg, &tmpport)) { usage( argv[0] ); exit( 1 ); }
674 livesync_bind_mcast( serverip, tmpport); break;
675 #endif
676 case 'd': set_config_option( &g_serverdir, optarg ); break;
677 case 'u': set_config_option( &g_serveruser, optarg ); break;
678 case 'r': set_config_option( &g_redirecturl, optarg ); break;
679 case 'l': statefile = optarg; break;
680 case 'A':
681 if( !scan_ip6_net( optarg, &tmpnet )) { usage( argv[0] ); exit( 1 ); }
682 accesslist_bless_net( &tmpnet, 0xffff ); /* Allow everything for now */
683 break;
684 case 'f': bound += parse_configfile( optarg ); break;
685 case 'h': help( argv[0] ); exit( 0 );
686 case 'v': {
687 char buffer[8192];
688 stats_return_tracker_version( buffer );
689 fputs( buffer, stderr );
690 exit( 0 );
692 default:
693 case '?': usage( argv[0] ); exit( 1 );
697 /* Bind to our default tcp/udp ports */
698 if( !bound) {
699 ot_try_bind( serverip, 6969, FLAG_TCP );
700 ot_try_bind( serverip, 6969, FLAG_UDP );
703 #ifdef WANT_SYSLOGS
704 openlog( "opentracker", 0, LOG_USER );
705 setlogmask(LOG_UPTO(LOG_INFO));
706 #endif
708 if( drop_privileges( g_serveruser ? g_serveruser : "nobody", g_serverdir ) == -1 )
709 panic( "drop_privileges failed, exiting. Last error");
711 g_now_seconds = time( NULL );
712 pthread_create( &thread_id, NULL, time_caching_worker, NULL);
714 /* Create our self pipe which allows us to interrupt mainloops
715 io_wait in case some data is available to send out */
716 if( pipe( g_self_pipe ) == -1 )
717 panic( "selfpipe failed: " );
718 if( !io_fd( g_self_pipe[0] ) )
719 panic( "selfpipe io_fd failed: " );
720 if( !io_fd( g_self_pipe[1] ) )
721 panic( "selfpipe io_fd failed: " );
722 io_setcookie( g_self_pipe[0], (void*)FLAG_SELFPIPE );
723 io_wantread( g_self_pipe[0] );
725 defaul_signal_handlers( );
726 /* Init all sub systems. This call may fail with an exit() */
727 trackerlogic_init( );
729 #ifdef _DEBUG_RANDOMTORRENTS
730 trackerlogic_add_random_torrents(1024*1024*1);
731 #endif
733 if( statefile )
734 load_state( statefile );
736 install_signal_handlers( );
738 if( !g_udp_workers )
739 udp_init( -1, 0 );
741 server_mainloop( 0 );
743 return 0;
746 const char *g_version_opentracker_c = "$Source$: $Revision$\n";