Updated init script.
[dtbartle/bnbt.git] / bnbt.cpp
1 /***\r
2 *\r
3 * BNBT Beta 8.0 - A C++ BitTorrent Tracker\r
4 * Copyright (C) 2003-2004 Trevor Hogan\r
5 *\r
6 * CBTT variations (C) 2003-2005 Harold Feit\r
7 *\r
8 * This library is free software; you can redistribute it and/or\r
9 * modify it under the terms of the GNU Lesser General Public\r
10 * License as published by the Free Software Foundation; either\r
11 * version 2.1 of the License, or (at your option) any later version.\r
12 *\r
13 * This library is distributed in the hope that it will be useful,\r
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
16 * Lesser General Public License for more details.\r
17 *\r
18 * You should have received a copy of the GNU Lesser General Public\r
19 * License along with this library; if not, write to the Free Software\r
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
21 *\r
22 ***/\r
23 \r
24 #include <signal.h>\r
25 \r
26 #include "bnbt.h"\r
27 #include "bnbt_mysql.h"\r
28 #include "config.h"\r
29 #include "link.h"\r
30 #include "server.h"\r
31 \r
32 #include <sys/types.h>\r
33 #include <sys/stat.h>\r
34 #include <pwd.h>\r
35 #include <fcntl.h>\r
36 \r
37 #ifdef WIN32\r
38  #include "util_ntservice.h"\r
39 #endif\r
40 \r
41 #include "util.h"\r
42 \r
43 #ifndef WIN32\r
44  int GetLastError( ) { return errno; }\r
45 #endif\r
46 \r
47 time_t giStartTime;\r
48 \r
49 unsigned long GetTime( )\r
50 {\r
51         return (unsigned long)( time( NULL ) - giStartTime );\r
52 }\r
53 \r
54 unsigned long GetRealTime( )\r
55 {\r
56         return (unsigned long)( time( NULL ) );\r
57 }\r
58 \r
59 CServer *gpServer;\r
60 CLink *gpLink;\r
61 CLinkServer *gpLinkServer;\r
62 CMutex gmtxOutput;\r
63 string gstrErrorLogDir;\r
64 string gstrErrorLogFilePattern;\r
65 string gstrErrorLogFile;\r
66 FILE *gpErrorLog;\r
67 string gstrAccessLogDir;\r
68 string gstrAccessLogFilePattern;\r
69 string gstrAccessLogFile;\r
70 FILE *gpAccessLog;\r
71 unsigned long giErrorLogCount;\r
72 unsigned long giAccessLogCount;\r
73 int giFlushInterval;\r
74 bool gbDebug;\r
75 unsigned int giMaxConns;\r
76 unsigned int giMaxRecvSize;\r
77 string gstrStyle;\r
78 string gstrCharSet;\r
79 string gstrRealm;\r
80 bool gbDaemonize = true;\r
81 \r
82 // TCP window size\r
83 int giSO_SNDBUF;\r
84 int giSO_RECBUF;\r
85 \r
86 void sigCatcher( int sig )\r
87 {\r
88         signal( SIGABRT, sigCatcher );\r
89         signal( SIGINT, sigCatcher );\r
90         // 2006/12/04 and SIGTERM and SIGHUP\r
91         signal( SIGTERM, sigCatcher );\r
92 #ifndef WIN32\r
93         signal( SIGHUP, sigCatcher );\r
94 #endif\r
95 \r
96         if( gpServer )\r
97         {\r
98                 if( gpServer->isDying( ) )\r
99                         exit( 1 );\r
100                 else\r
101                         gpServer->Kill( );\r
102         }\r
103         else\r
104                 exit( 1 );\r
105 }\r
106 \r
107 int main( int argc, char *argv[] )\r
108 {\r
109 #ifdef WIN32\r
110         if( argv[0] )\r
111         {\r
112                 char *szEndPos = strrchr( argv[0], '\\' );\r
113 \r
114                 if( szEndPos )\r
115                 {\r
116                         char *szEXEPath = new char[szEndPos - argv[0] + 1];\r
117                         memcpy( szEXEPath, argv[0], szEndPos - argv[0] );\r
118                         szEXEPath[szEndPos - argv[0]] = '\0';\r
119 \r
120                         SetCurrentDirectory( szEXEPath );\r
121 \r
122                         delete [] szEXEPath;\r
123                 }\r
124         }\r
125 \r
126         if( argc > 1 )\r
127         {\r
128                 CFG_Open( CFG_FILE );\r
129                 #define BNBT_SERVICE_NAME const_cast<LPSTR> (CFG_GetString( "cbtt_service_name", "CBTT Service" ).c_str())\r
130                 CFG_Close( CFG_FILE );\r
131 \r
132                 printf( "Service name ");\r
133                 printf( BNBT_SERVICE_NAME );\r
134                 printf( "\n" ); \r
135 \r
136                 if( _stricmp( argv[1], "-i" ) == 0 )\r
137                 {\r
138                         // install service\r
139 \r
140                         if( UTIL_NTServiceTest( ) )\r
141                                 printf( "BNBT Service is already installed!\n" );\r
142                         else\r
143                         {\r
144                                 if( UTIL_NTServiceInstall( ) )\r
145                                         printf( "BNBT Service installed.\n" );\r
146                                 else\r
147                                         printf( "BNBT Service failed to install (error %d).\n", GetLastError( ) );\r
148                         }\r
149 \r
150                         return 0;\r
151                 }\r
152                 else if( _stricmp( argv[1], "-u" ) == 0 )\r
153                 {\r
154                         // uninstall service\r
155 \r
156                         if( !UTIL_NTServiceTest( ) )\r
157                                 printf( "BNBT Service is not installed!\n" );\r
158                         else\r
159                         {\r
160                                 if( UTIL_NTServiceUninstall( ) )\r
161                                         printf( "BNBT Service uninstalled.\n" );\r
162                                 else\r
163                                         printf( "BNBT Service failed to uninstall (error %d).\n", GetLastError( ) );\r
164                         }\r
165 \r
166                         return 0;\r
167                 }\r
168                 else if( _stricmp( argv[1], "-start" ) == 0 )\r
169                 {\r
170                         // start\r
171 \r
172                         if( !UTIL_NTServiceTest( ) )\r
173                                 printf( "BNBT Service is not installed!\n" );\r
174                         else\r
175                         {\r
176                                 printf( "Starting BNBT Service.\n" );\r
177 \r
178                                 if( !UTIL_NTServiceStart( ) )\r
179                                         printf( "BNBT Service failed to start (error %d).\n", GetLastError( ) );\r
180                         }\r
181 \r
182                         return 0;\r
183                 }\r
184                 else if( _stricmp( argv[1], "-stop" ) == 0 )\r
185                 {\r
186                         // stop\r
187 \r
188                         if( !UTIL_NTServiceTest( ) )\r
189                                 printf( "BNBT Service is not installed!\n" );\r
190                         else\r
191                         {\r
192                                 printf( "Stopping BNBT Service.\n" );\r
193 \r
194                                 if( !UTIL_NTServiceStop( ) )\r
195                                         printf( "BNBT Service failed to stop (error %d).\n", GetLastError( ) );\r
196                         }\r
197 \r
198                         return 0;\r
199                 }\r
200                 else if( _stricmp( argv[1], "-s" ) == 0 )\r
201                 {\r
202                         // internal start\r
203 \r
204                         SERVICE_TABLE_ENTRY st[] = {\r
205                                 { BNBT_SERVICE_NAME, NTServiceMain },\r
206                                 { NULL, NULL }\r
207                         };\r
208 \r
209                         StartServiceCtrlDispatcher( st );\r
210 \r
211                         return 0;\r
212                 }\r
213         }\r
214 #else\r
215         // disable SIGPIPE since some systems like OS X don't define MSG_NOSIGNAL\r
216         signal( SIGPIPE, SIG_IGN );\r
217 \r
218     if(argc > 1)\r
219     {\r
220         if(strcmp(argv[1], "-f") == 0)\r
221         {\r
222             gbDaemonize = false;\r
223         }\r
224     }\r
225 #endif\r
226 \r
227         // catch SIGABRT and SIGINT\r
228         // 2006/12/04 and SIGTERM and SIGHUP\r
229 \r
230         signal( SIGABRT, sigCatcher );\r
231         signal( SIGINT, sigCatcher );\r
232         signal( SIGTERM, sigCatcher );\r
233 #ifndef WIN32\r
234         signal( SIGHUP, sigCatcher );\r
235 #endif\r
236 \r
237         return bnbtmain( );\r
238 }\r
239 \r
240 #ifndef WIN32\r
241 \r
242 #define EXIT_SUCCESS 0\r
243 #define EXIT_FAILURE 1\r
244 \r
245 static void sigDaemon(int signum)\r
246 {\r
247     switch(signum)\r
248     {\r
249         case SIGUSR1:\r
250             exit(EXIT_SUCCESS);\r
251             break;\r
252         case SIGALRM:\r
253         case SIGCHLD:\r
254             exit(EXIT_FAILURE);\r
255             break;\r
256     }\r
257 }\r
258 \r
259 static void daemonize( const char *lockfile )\r
260 {\r
261     pid_t pid, sid, parent;\r
262     int lfp = -1;\r
263 \r
264     /* already a daemon */\r
265     if ( getppid() == 1 ) return;\r
266 \r
267     /* Create the lock file as the current user */\r
268     if ( lockfile && lockfile[0] ) {\r
269         lfp = open(lockfile,O_RDWR|O_CREAT,0640);\r
270         if ( lfp < 0 ) {\r
271             UTIL_LogPrint( "unable to create lock file %s, code=%d (%s)\n",\r
272                     lockfile, errno, strerror(errno) );\r
273             exit(EXIT_FAILURE);\r
274         }\r
275     }\r
276 \r
277     /* Drop user if there is one, and we were run as root */\r
278     if ( getuid() == 0 || geteuid() == 0 ) {\r
279         struct passwd *pw = getpwnam(RUN_AS_USER);\r
280         if ( pw ) {\r
281             UTIL_LogPrint( "setting user to " RUN_AS_USER "\n" );\r
282             setuid( pw->pw_uid );\r
283         }\r
284     }\r
285 \r
286     /* Trap signals that we expect to recieve */\r
287     signal(SIGCHLD, sigDaemon);\r
288     signal(SIGUSR1, sigDaemon);\r
289     signal(SIGALRM, sigDaemon);\r
290 \r
291     /* Fork off the parent process */\r
292     pid = fork();\r
293     if (pid < 0) {\r
294         UTIL_LogPrint( "unable to fork daemon, code=%d (%s)\n",\r
295                 errno, strerror(errno) );\r
296         exit(EXIT_FAILURE);\r
297     }\r
298     /* If we got a good PID, then we can exit the parent process. */\r
299     if (pid > 0) {\r
300 \r
301         /* Wait for confirmation from the child via SIGTERM or SIGCHLD, \r
302 or\r
303            for two seconds to elapse (SIGALRM).  pause() should not \r
304 return. */\r
305         alarm(2);\r
306         pause();\r
307 \r
308         exit(EXIT_FAILURE);\r
309     }\r
310 \r
311     /* At this point we are executing as the child process */\r
312     parent = getppid();\r
313 \r
314     /* Cancel certain signals */\r
315     signal(SIGCHLD,SIG_DFL); /* A child process dies */\r
316     signal(SIGTSTP,SIG_IGN); /* Various TTY signals */\r
317     signal(SIGTTOU,SIG_IGN);\r
318     signal(SIGTTIN,SIG_IGN);\r
319     signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */\r
320     signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */\r
321 \r
322     /* Change the file mode mask */\r
323     umask(0);\r
324 \r
325     /* Create a new SID for the child process */\r
326     sid = setsid();\r
327     if (sid < 0) {\r
328         UTIL_LogPrint( "unable to create a new session, code %d (%s)\n",\r
329                 errno, strerror(errno) );\r
330         exit(EXIT_FAILURE);\r
331     }\r
332 \r
333     /* Change the current working directory.  This prevents the current\r
334        directory from being locked; hence not being able to remove it. \r
335 */\r
336     if ((chdir("/")) < 0) {\r
337         UTIL_LogPrint( "unable to change directory to %s, code %d (%s)\n",\r
338                 "/", errno, strerror(errno) );\r
339         exit(EXIT_FAILURE);\r
340     }\r
341 \r
342     /* Redirect standard files to /dev/null */\r
343     freopen( "/dev/null", "r", stdin);\r
344     freopen( "/dev/null", "w", stdout);\r
345     freopen( "/dev/null", "w", stderr);\r
346 \r
347     /* Tell the parent process that we are A-okay */\r
348     kill( parent, SIGUSR1 );\r
349 }\r
350 \r
351 #endif // WIN32\r
352 \r
353 int bnbtmain( )\r
354 {\r
355         gmtxOutput.Initialize( );\r
356 \r
357         srand( time( NULL ) );\r
358 \r
359         giStartTime = time( NULL );\r
360 \r
361         CFG_Open( CFG_FILE );\r
362         CFG_SetDefaults( );\r
363         CFG_Close( CFG_FILE );\r
364 \r
365         gstrErrorLogFilePattern = CFG_GetString( "bnbt_error_log_file_pattern", "%Y-%m-%de.log" );\r
366         gstrErrorLogDir = CFG_GetString( "bnbt_error_log_dir", string( ) );\r
367 \r
368         if( !gstrErrorLogDir.empty( ) && gstrErrorLogDir[gstrErrorLogDir.size( ) - 1] != PATH_SEP )\r
369                 gstrErrorLogDir += PATH_SEP;\r
370 \r
371         gpErrorLog = NULL;\r
372         gstrAccessLogFilePattern = CFG_GetString( "bnbt_access_log_file_pattern", "%Y-%m-%d.log" );\r
373         gstrAccessLogDir = CFG_GetString( "bnbt_access_log_dir", string( ) );\r
374 \r
375         if( !gstrAccessLogDir.empty( ) && gstrAccessLogDir[gstrAccessLogDir.size( ) - 1] != PATH_SEP )\r
376                 gstrAccessLogDir += PATH_SEP;\r
377 \r
378         gpAccessLog = NULL;\r
379         giErrorLogCount = 0;\r
380         giAccessLogCount = 0;\r
381         giFlushInterval = CFG_GetInt( "bnbt_flush_interval", 100 );\r
382         gbDebug = CFG_GetInt( "bnbt_debug", 1 ) == 0 ? false : true;\r
383         giMaxConns = CFG_GetInt( "bnbt_max_conns", 64 );\r
384         giMaxRecvSize = CFG_GetInt( "bnbt_max_recv_size", 524288 );\r
385         gstrStyle = CFG_GetString( "bnbt_style_sheet", string( ) );\r
386         gstrCharSet = CFG_GetString( "bnbt_charset", "iso-8859-1" );\r
387         gstrRealm = CFG_GetString( "bnbt_realm", "BNBT" );\r
388 \r
389 #ifndef WIN32\r
390     // print initial newline\r
391     printf("\n");\r
392 #endif\r
393 \r
394         // start winsock\r
395         \r
396         // TCP window size\r
397         giSO_RECBUF = CFG_GetInt( "xbnbt_so_recbuf", 128 ) * 1024;\r
398         giSO_SNDBUF = CFG_GetInt( "xbnbt_so_sndbuf", 128 ) * 1024;\r
399 \r
400 #ifdef WIN32\r
401         WSADATA wsaData;\r
402 \r
403         int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );\r
404 \r
405         if ( iResult != NO_ERROR )\r
406         {\r
407                 UTIL_LogPrint( "error - unable to start winsock (error %d)\n", GetLastError( ) );\r
408 \r
409                 if( gpAccessLog )\r
410                         fclose( gpAccessLog );\r
411 \r
412                 if( gpErrorLog )\r
413                         fclose( gpErrorLog );\r
414 \r
415                 gmtxOutput.Destroy( );\r
416 \r
417                 return 1;\r
418         }\r
419 #endif\r
420 \r
421         // start mysql\r
422 \r
423 #ifdef BNBT_MYSQL\r
424         if( !( gpMySQL = mysql_init( NULL ) ) )\r
425         {\r
426                 UTIL_LogPrint( "mysql error - %s\n", mysql_error( gpMySQL ) );\r
427 \r
428                 if( gpAccessLog )\r
429                         fclose( gpAccessLog );\r
430 \r
431                 if( gpErrorLog )\r
432                         fclose( gpErrorLog );\r
433 \r
434                 gmtxOutput.Destroy( );\r
435 \r
436                 return 1;\r
437         }\r
438 \r
439         gstrMySQLHost = CFG_GetString( "mysql_host", string( ) );\r
440         gstrMySQLDatabase = CFG_GetString( "mysql_database", "bnbt" );\r
441         gstrMySQLUser = CFG_GetString( "mysql_user", string( ) );\r
442         gstrMySQLPassword = CFG_GetString( "mysql_password", string( ) );\r
443         giMySQLPort = CFG_GetInt( "mysql_port", 0 );\r
444 \r
445         if( !( mysql_real_connect( gpMySQL, gstrMySQLHost.c_str( ), gstrMySQLUser.c_str( ), gstrMySQLPassword.c_str( ), gstrMySQLDatabase.c_str( ), giMySQLPort, NULL, 0 ) ) )\r
446         {\r
447                 UTIL_LogPrint( "mysql error - %s\n", mysql_error( gpMySQL ) );\r
448 \r
449                 if( gpAccessLog )\r
450                         fclose( gpAccessLog );\r
451 \r
452                 if( gpErrorLog )\r
453                         fclose( gpErrorLog );\r
454 \r
455                 gmtxOutput.Destroy( );\r
456 \r
457                 return 1;\r
458         }\r
459 \r
460         UTIL_LogPrint( "mysql - connected\n" );\r
461 #endif\r
462 \r
463         gpServer = new CServer( );\r
464         gpLink = NULL;\r
465         gpLinkServer = NULL;\r
466 \r
467         if( CFG_GetInt( "bnbt_tlink_server", 0 ) != 0 )\r
468                 gpLinkServer = new CLinkServer( );\r
469         else\r
470         {\r
471                 if( !CFG_GetString( "bnbt_tlink_connect", string( ) ).empty( ) )\r
472                 {\r
473 #ifdef WIN32\r
474                         if( _beginthread( ( void (*)(void *) )StartLink, 0, NULL ) == -1 )\r
475                                 UTIL_LogPrint( "error - unable to spawn link thread\n" );\r
476 #else\r
477                         pthread_t thread;\r
478 \r
479                         // set detached state since we don't need to join with any threads\r
480 \r
481                         pthread_attr_t attr;\r
482                         pthread_attr_init( &attr );\r
483                         pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );\r
484 \r
485                         int c = pthread_create( &thread, &attr, ( void * (*)(void *) )StartLink, NULL );\r
486 \r
487                         if( c != 0 )\r
488                                 UTIL_LogPrint( "error - unable to spawn link thread (error %s)\n", strerror( c ) );\r
489 #endif\r
490                 }\r
491         }\r
492 \r
493 #ifndef WIN32\r
494     // daemonize\r
495     if(gbDaemonize)\r
496     {\r
497         daemonize("/var/lock/" DAEMON_NAME);\r
498     }\r
499 #endif\r
500 \r
501         while( 1 )\r
502         {\r
503                 if( gpServer->Update( true ) )\r
504                 {\r
505                         delete gpServer;\r
506 \r
507                         gpServer = NULL;\r
508 \r
509                         if( gpLinkServer )\r
510                         {\r
511                                 delete gpLinkServer;\r
512 \r
513                                 gpLinkServer = NULL;\r
514                         }\r
515 \r
516                         break;\r
517                 }\r
518 \r
519                 if( gpLinkServer )\r
520                         gpLinkServer->Update( );\r
521         }\r
522 \r
523         // wait for the link or it might make a big mess\r
524 \r
525         if( gpLink )\r
526                 gpLink->Kill( );\r
527 \r
528         unsigned long iStart = GetTime( );\r
529 \r
530         while( gpLink )\r
531         {\r
532                 UTIL_LogPrint( "server - waiting for link to disconnect\n" );\r
533 \r
534                 MILLISLEEP( 1000 );\r
535 \r
536                 if( GetTime( ) - iStart > 60 )\r
537                 {\r
538                         UTIL_LogPrint( "server - waited 60 seconds, exiting anyway\n" );\r
539 \r
540                         break;\r
541                 }\r
542         }\r
543 \r
544 #ifdef BNBT_MYSQL\r
545         mysql_close( gpMySQL );\r
546 #endif\r
547 \r
548         if( gpAccessLog )\r
549                 fclose( gpAccessLog );\r
550 \r
551         if( gpErrorLog )\r
552                 fclose( gpErrorLog );\r
553 \r
554         gmtxOutput.Destroy( );\r
555 \r
556 #ifdef WIN32\r
557         WSACleanup( );\r
558 #endif\r
559 \r
560         return 0;\r
561 }\r