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.
10 #include <arpa/inet.h>
11 #include <sys/socket.h>
32 #include "trackerlogic.h"
36 #include "ot_accesslist.h"
38 #include "ot_livesync.h"
43 uint32_t g_tracker_id
;
44 volatile int g_opentracker_running
= 1;
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
) );
57 static void signal_handler( int s
) {
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();
76 static void defaul_signal_handlers( void ) {
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 ) {
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]"
111 #define HELPLINE(opt,desc) fprintf(stderr, "\t%-10s%s\n",opt,desc)
112 static void help( char *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.");
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");
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 ) {
142 for( state
= 0 ; i
< byte_count
; ++i
) {
144 if( c
== '\r' || c
== '\n' )
145 state
= ( state
>> 2 ) | ( ( c
<< 6 ) & 0xc0 );
148 if( state
>= 0xa0 || state
== 0x99 ) return i
+ 1;
154 static void handle_dead( const int64 sock
) {
155 struct http_data
* cookie
=io_getcookie( sock
);
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
);
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 ) {
178 if( byte_count
== -1)
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
);
188 array_catb( &cookie
->request
, ws
->inbuf
, (size_t)byte_count
);
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
);
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
)
209 static void handle_write( const int64 sock
) {
210 struct http_data
* cookie
=io_getcookie( sock
);
214 /* Look for the first io_batch still containing bytes to write */
216 if( cookie
->flag
& STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER
)
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
);
230 if( !cookie
->batch
[i
].bytesleft
)
233 if( res
== -1 || res
> 0 || i
< cookie
->batches
- 1 )
239 /* In a chunked transfer after all batches accumulated have been sent, wait for the next one */
241 fprintf( stderr
, "handle_write is STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER => dont want write on sock %lld\n", sock
);
242 io_dontwantwrite( sock
);
244 fprintf( stderr
, "handle_write is STRUCT_HTTP_FLAG_CHUNKED_IN_TRANSFER => handle dead on sock %lld\n", sock
);
249 static void handle_accept( const int64 serversocket
) {
250 struct http_data
*cookie
;
256 while( ( sock
= socket_accept6( serversocket
, ip
, &port
, NULL
) ) != -1 ) {
258 /* Put fd into a non-blocking mode */
261 if( !io_fd( sock
) ||
262 !( cookie
= (struct http_data
*)malloc( sizeof(struct http_data
) ) ) ) {
266 memset(cookie
, 0, sizeof( struct http_data
) );
267 memcpy(cookie
->ip
,ip
,sizeof(ot_ip6
));
269 io_setcookie( sock
, cookie
);
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
;
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
);
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));
304 ws
.rand48_state
[0] = (uint16_t)random();
305 ws
.rand48_state
[1] = (uint16_t)random();
306 ws
.rand48_state
[2] = (uint16_t)random();
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
);
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 )
335 next_timeout_check
= g_now_seconds
+ OT_CLIENT_TIMEOUT_CHECKINTERVAL
;
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( );
348 char *protos
[] = {"TCP","UDP","UDP mcast"};
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
);
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" );
366 io_setcookie( sock
, (void*)proto
);
368 if( (proto
== FLAG_UDP
) && g_udp_workers
) {
370 udp_init( sock
, g_udp_workers
);
375 fputs( " success.\n", stderr
);
381 char * set_config_option( char **option
, char *value
) {
383 fprintf( stderr
, "Setting config option: %s\n", value
);
385 while( isspace(*value
) ) ++value
;
387 return *option
= strdup( value
);
390 static int scan_ip6_port( const char *src
, ot_ip6 ip
, uint16
*port
) {
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
) ) )
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;
405 if( *(s
++) != ':' ) return 0;
407 if( !(off
= scan_ushort (s
, port
) ) )
412 static int scan_ip6_net( const char *src
, ot_net
*net
) {
415 while( isspace(*s
) ) ++s
;
416 if( !(off
= scan_ip6( s
, net
->address
) ) )
423 if( !(off
= scan_int (s
, &net
->bits
) ) )
425 if( ip6_isv4mapped(net
->address
))
434 int parse_configfile( char * config_filename
) {
435 FILE * accesslist_filehandle
;
438 #if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE)
443 accesslist_filehandle
= fopen( config_filename
, "r" );
445 if( accesslist_filehandle
== NULL
) {
446 fprintf( stderr
, "Warning: Can't open config file: %s.", config_filename
);
450 while( fgets( inbuf
, sizeof(inbuf
), accesslist_filehandle
) ) {
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 */
462 while( strl
&& isspace(p
[strl
-1]))
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
);
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
);
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 );
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 );
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
);
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
);
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
);
526 fprintf( stderr
, "Unhandled line in config file: %s\n", inbuf
);
529 fprintf( stderr
, "Parse error in config file: %s\n", inbuf
);
531 fclose( accesslist_filehandle
);
535 void load_state(const char * const state_filename
) {
536 FILE * state_filehandle
;
539 unsigned long long base
, downcount
;
542 state_filehandle
= fopen( state_filename
, "r" );
544 if( state_filehandle
== NULL
) {
545 fprintf( stderr
, "Warning: Can't open config file: %s.", state_filename
);
549 /* We do ignore anything that is not of the form "^[:xdigit:]:\d+:\d+" */
550 while( fgets( inbuf
, sizeof(inbuf
), state_filehandle
) ) {
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
] );
559 if( i
!= (int)sizeof(ot_hash
) ) continue;
562 if( inbuf
[ i
++ ] != ':' || !( consumed
= scan_ulonglong( inbuf
+i
, &base
) ) ) continue;
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
;
576 fprintf( stderr
, "Dropping to user %s.\n", serveruser
);
578 fprintf( stderr
, "ch%s'ing to directory %s.\n", geteuid() ? "dir" : "root", serverdir
);
581 /* Grab pws entry before chrooting */
582 pws
= getpwnam( serveruser
);
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
) );
593 panic("chdir() failed after chrooting: ");
595 /* If we can't find server user, revert to nobody's default uid */
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");
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?!");
610 /* Normal user, just chdir() */
611 if( serverdir
&& chdir( serverdir
) ) {
612 fprintf( stderr
, "Could not chroot to %s, because: %s\n", serverdir
, strerror(errno
) );
620 /* Maintain our copy of the clock. time() on BSDs is very expensive. */
621 static void *time_caching_worker(void*args
) {
624 g_now_seconds
= time(NULL
);
629 int main( int argc
, char **argv
) {
632 int bound
= 0, scanon
= 1;
634 char * statefile
= 0;
635 pthread_t thread_id
; /* time cacher */
637 memset( serverip
, 0, sizeof(ot_ip6
) );
639 serverip
[10]=serverip
[11]=-1;
642 #ifdef WANT_DEV_RANDOM
645 srandom( time(NULL
) );
649 switch( getopt( argc
, argv
, ":i:p:A:P:d:u:r:s:f:l:v"
650 #ifdef WANT_ACCESSLIST_BLACK
652 #elif defined( WANT_ACCESSLIST_WHITE )
656 case -1 : scanon
= 0; break;
658 if( !scan_ip6( optarg
, serverip
)) { usage( argv
[0] ); exit( 1 ); }
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;
666 if( !scan_ushort( optarg
, &tmpport
)) { usage( argv
[0] ); exit( 1 ); }
667 ot_try_bind( serverip
, tmpport
, FLAG_TCP
); bound
++; break;
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
673 if( !scan_ushort( optarg
, &tmpport
)) { usage( argv
[0] ); exit( 1 ); }
674 livesync_bind_mcast( serverip
, tmpport
); break;
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;
681 if( !scan_ip6_net( optarg
, &tmpnet
)) { usage( argv
[0] ); exit( 1 ); }
682 accesslist_bless_net( &tmpnet
, 0xffff ); /* Allow everything for now */
684 case 'f': bound
+= parse_configfile( optarg
); break;
685 case 'h': help( argv
[0] ); exit( 0 );
688 stats_return_tracker_version( buffer
);
689 fputs( buffer
, stderr
);
693 case '?': usage( argv
[0] ); exit( 1 );
697 /* Bind to our default tcp/udp ports */
699 ot_try_bind( serverip
, 6969, FLAG_TCP
);
700 ot_try_bind( serverip
, 6969, FLAG_UDP
);
704 openlog( "opentracker", 0, LOG_USER
);
705 setlogmask(LOG_UPTO(LOG_INFO
));
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);
734 load_state( statefile
);
736 install_signal_handlers( );
741 server_mainloop( 0 );
746 const char *g_version_opentracker_c
= "$Source$: $Revision$\n";