ngircd.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. /*
  2. * ngIRCd -- The Next Generation IRC Daemon
  3. * Copyright (c)2001-2008 Alexander Barton (alex@barton.de).
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. * Please read the file COPYING, README and AUTHORS for more information.
  10. */
  11. #include "portab.h"
  12. /**
  13. * @file
  14. * The main program, including the C function main() which is called
  15. * by the loader of the operating system.
  16. */
  17. #include "imp.h"
  18. #include <assert.h>
  19. #include <errno.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <signal.h>
  23. #include <string.h>
  24. #include <unistd.h>
  25. #include <time.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <sys/wait.h>
  29. #include <fcntl.h>
  30. #include <pwd.h>
  31. #include <grp.h>
  32. #include "defines.h"
  33. #include "resolve.h"
  34. #include "conn.h"
  35. #include "client.h"
  36. #include "channel.h"
  37. #include "conf.h"
  38. #include "lists.h"
  39. #include "log.h"
  40. #include "parse.h"
  41. #include "irc.h"
  42. #ifdef ZEROCONF
  43. #include "rendezvous.h"
  44. #endif
  45. #include "exp.h"
  46. #include "ngircd.h"
  47. static void Initialize_Signal_Handler PARAMS(( void ));
  48. static void Signal_Handler PARAMS(( int Signal ));
  49. static void Show_Version PARAMS(( void ));
  50. static void Show_Help PARAMS(( void ));
  51. static void Pidfile_Create PARAMS(( pid_t pid ));
  52. static void Pidfile_Delete PARAMS(( void ));
  53. static void Fill_Version PARAMS(( void ));
  54. static void Setup_FDStreams PARAMS(( void ));
  55. static bool NGIRCd_Init PARAMS(( bool ));
  56. /**
  57. * The main() function of ngIRCd.
  58. * Here all starts: this function is called by the operating system loader,
  59. * it is the first portion of code executed of ngIRCd.
  60. * @param argc The number of arguments passed to ngIRCd on the command line.
  61. * @param argv An array containing all the arguments passed to ngIRCd.
  62. * @return Global exit code of ngIRCd, zero on success.
  63. */
  64. GLOBAL int
  65. main( int argc, const char *argv[] )
  66. {
  67. bool ok, configtest = false;
  68. bool NGIRCd_NoDaemon = false;
  69. int i;
  70. size_t n;
  71. umask( 0077 );
  72. NGIRCd_SignalQuit = NGIRCd_SignalRestart = NGIRCd_SignalRehash = false;
  73. NGIRCd_Passive = false;
  74. #ifdef DEBUG
  75. NGIRCd_Debug = false;
  76. #endif
  77. #ifdef SNIFFER
  78. NGIRCd_Sniffer = false;
  79. #endif
  80. strlcpy( NGIRCd_ConfFile, SYSCONFDIR, sizeof( NGIRCd_ConfFile ));
  81. strlcat( NGIRCd_ConfFile, CONFIG_FILE, sizeof( NGIRCd_ConfFile ));
  82. Fill_Version( );
  83. /* Kommandozeile parsen */
  84. for( i = 1; i < argc; i++ )
  85. {
  86. ok = false;
  87. if(( argv[i][0] == '-' ) && ( argv[i][1] == '-' ))
  88. {
  89. /* Lange Option */
  90. if( strcmp( argv[i], "--config" ) == 0 )
  91. {
  92. if( i + 1 < argc )
  93. {
  94. /* Ok, there's an parameter left */
  95. strlcpy( NGIRCd_ConfFile, argv[i + 1], sizeof( NGIRCd_ConfFile ));
  96. /* next parameter */
  97. i++; ok = true;
  98. }
  99. }
  100. if( strcmp( argv[i], "--configtest" ) == 0 )
  101. {
  102. configtest = true;
  103. ok = true;
  104. }
  105. #ifdef DEBUG
  106. if( strcmp( argv[i], "--debug" ) == 0 )
  107. {
  108. NGIRCd_Debug = true;
  109. ok = true;
  110. }
  111. #endif
  112. if( strcmp( argv[i], "--help" ) == 0 )
  113. {
  114. Show_Version( );
  115. puts( "" ); Show_Help( ); puts( "" );
  116. exit( 1 );
  117. }
  118. if( strcmp( argv[i], "--nodaemon" ) == 0 )
  119. {
  120. NGIRCd_NoDaemon = true;
  121. ok = true;
  122. }
  123. if( strcmp( argv[i], "--passive" ) == 0 )
  124. {
  125. NGIRCd_Passive = true;
  126. ok = true;
  127. }
  128. #ifdef SNIFFER
  129. if( strcmp( argv[i], "--sniffer" ) == 0 )
  130. {
  131. NGIRCd_Sniffer = true;
  132. ok = true;
  133. }
  134. #endif
  135. if( strcmp( argv[i], "--version" ) == 0 )
  136. {
  137. Show_Version( );
  138. exit( 1 );
  139. }
  140. }
  141. else if(( argv[i][0] == '-' ) && ( argv[i][1] != '-' ))
  142. {
  143. /* Kurze Option */
  144. for( n = 1; n < strlen( argv[i] ); n++ )
  145. {
  146. ok = false;
  147. #ifdef DEBUG
  148. if (argv[i][n] == 'd') {
  149. NGIRCd_Debug = true;
  150. ok = true;
  151. }
  152. #endif
  153. if (argv[i][n] == 'f') {
  154. if(( ! argv[i][n + 1] ) && ( i + 1 < argc ))
  155. {
  156. /* Ok, next character is a blank */
  157. strlcpy( NGIRCd_ConfFile, argv[i + 1], sizeof( NGIRCd_ConfFile ));
  158. /* go to the following parameter */
  159. i++;
  160. n = strlen( argv[i] );
  161. ok = true;
  162. }
  163. }
  164. if (argv[i][n] == 'h') {
  165. Show_Version();
  166. puts(""); Show_Help(); puts("");
  167. exit(1);
  168. }
  169. if (argv[i][n] == 'n') {
  170. NGIRCd_NoDaemon = true;
  171. ok = true;
  172. }
  173. if (argv[i][n] == 'p') {
  174. NGIRCd_Passive = true;
  175. ok = true;
  176. }
  177. #ifdef SNIFFER
  178. if (argv[i][n] == 's') {
  179. NGIRCd_Sniffer = true;
  180. ok = true;
  181. }
  182. #endif
  183. if (argv[i][n] == 't') {
  184. configtest = true;
  185. ok = true;
  186. }
  187. if (argv[i][n] == 'V') {
  188. Show_Version();
  189. exit(1);
  190. }
  191. if (! ok) {
  192. printf( "%s: invalid option \"-%c\"!\n", PACKAGE_NAME, argv[i][n] );
  193. printf( "Try \"%s --help\" for more information.\n", PACKAGE_NAME );
  194. exit( 1 );
  195. }
  196. }
  197. }
  198. if( ! ok )
  199. {
  200. printf( "%s: invalid option \"%s\"!\n", PACKAGE_NAME, argv[i] );
  201. printf( "Try \"%s --help\" for more information.\n", PACKAGE_NAME );
  202. exit( 1 );
  203. }
  204. }
  205. /* Debug-Level (fuer IRC-Befehl "VERSION") ermitteln */
  206. NGIRCd_DebugLevel[0] = '\0';
  207. #ifdef DEBUG
  208. if( NGIRCd_Debug ) strcpy( NGIRCd_DebugLevel, "1" );
  209. #endif
  210. #ifdef SNIFFER
  211. if( NGIRCd_Sniffer )
  212. {
  213. NGIRCd_Debug = true;
  214. strcpy( NGIRCd_DebugLevel, "2" );
  215. }
  216. #endif
  217. /* Soll nur die Konfigurations ueberprueft und ausgegeben werden? */
  218. if( configtest )
  219. {
  220. Show_Version( ); puts( "" );
  221. exit( Conf_Test( ));
  222. }
  223. while( ! NGIRCd_SignalQuit )
  224. {
  225. /* Initialize global variables */
  226. NGIRCd_Start = time( NULL );
  227. (void)strftime( NGIRCd_StartStr, 64, "%a %b %d %Y at %H:%M:%S (%Z)", localtime( &NGIRCd_Start ));
  228. NGIRCd_SignalRehash = false;
  229. NGIRCd_SignalRestart = false;
  230. NGIRCd_SignalQuit = false;
  231. /* Initialize modules, part I */
  232. Log_Init( ! NGIRCd_NoDaemon );
  233. Conf_Init( );
  234. /* Initialize the "main program": chroot environment, user and
  235. * group ID, ... */
  236. if (!NGIRCd_Init(NGIRCd_NoDaemon)) {
  237. Log(LOG_ALERT, "Fatal: Initialization failed");
  238. exit(1);
  239. }
  240. /* Initialize modules, part II: these functions are eventually
  241. * called with already dropped privileges ... */
  242. Channel_Init( );
  243. Client_Init( );
  244. #ifdef ZEROCONF
  245. Rendezvous_Init( );
  246. #endif
  247. Conn_Init( );
  248. #ifdef DEBUG
  249. /* Redirect stderr handle to "error file" for debugging
  250. * when not running in "no daemon" mode: */
  251. if( ! NGIRCd_NoDaemon ) Log_InitErrorfile( );
  252. #endif
  253. /* Signal-Handler initialisieren */
  254. Initialize_Signal_Handler( );
  255. /* Protokoll- und Server-Identifikation erzeugen. Die vom ngIRCd
  256. * beim PASS-Befehl verwendete Syntax sowie die erweiterten Flags
  257. * sind in doc/Protocol.txt beschrieben. */
  258. #ifdef IRCPLUS
  259. snprintf( NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s:%s", PROTOVER, PROTOIRCPLUS, PACKAGE_NAME, PACKAGE_VERSION, IRCPLUSFLAGS );
  260. #ifdef ZLIB
  261. strcat( NGIRCd_ProtoID, "Z" );
  262. #endif
  263. if( Conf_OperCanMode ) strcat( NGIRCd_ProtoID, "o" );
  264. #else
  265. snprintf( NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s", PROTOVER, PROTOIRC, PACKAGE_NAME, PACKAGE_VERSION );
  266. #endif
  267. strlcat( NGIRCd_ProtoID, " P", sizeof NGIRCd_ProtoID );
  268. #ifdef ZLIB
  269. strlcat( NGIRCd_ProtoID, "Z", sizeof NGIRCd_ProtoID );
  270. #endif
  271. Log( LOG_DEBUG, "Protocol and server ID is \"%s\".", NGIRCd_ProtoID );
  272. /* Vordefinierte Channels anlegen */
  273. Channel_InitPredefined( );
  274. /* Listen-Ports initialisieren */
  275. if( Conn_InitListeners( ) < 1 )
  276. {
  277. Log( LOG_ALERT, "Server isn't listening on a single port!" );
  278. Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
  279. Pidfile_Delete( );
  280. exit( 1 );
  281. }
  282. /* Hauptschleife */
  283. Conn_Handler( );
  284. /* Alles abmelden */
  285. Conn_Exit( );
  286. #ifdef ZEROCONF
  287. Rendezvous_Exit( );
  288. #endif
  289. Client_Exit( );
  290. Channel_Exit( );
  291. Log_Exit( );
  292. }
  293. Pidfile_Delete( );
  294. return 0;
  295. } /* main */
  296. /**
  297. * Generate ngIRCd "version string".
  298. * This string is generated once and then stored in NGIRCd_Version for
  299. * further usage, for example by the IRC command VERSION and the --version
  300. * command line switch.
  301. */
  302. static void
  303. Fill_Version( void )
  304. {
  305. NGIRCd_VersionAddition[0] = '\0';
  306. #ifdef SYSLOG
  307. strlcpy( NGIRCd_VersionAddition, "SYSLOG", sizeof NGIRCd_VersionAddition );
  308. #endif
  309. #ifdef ZLIB
  310. if( NGIRCd_VersionAddition[0] )
  311. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  312. strlcat( NGIRCd_VersionAddition, "ZLIB", sizeof NGIRCd_VersionAddition );
  313. #endif
  314. #ifdef TCPWRAP
  315. if( NGIRCd_VersionAddition[0] )
  316. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  317. strlcat( NGIRCd_VersionAddition, "TCPWRAP", sizeof NGIRCd_VersionAddition );
  318. #endif
  319. #ifdef ZEROCONF
  320. if( NGIRCd_VersionAddition[0] )
  321. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  322. strlcat( NGIRCd_VersionAddition, "ZEROCONF", sizeof NGIRCd_VersionAddition );
  323. #endif
  324. #ifdef IDENTAUTH
  325. if( NGIRCd_VersionAddition[0] )
  326. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  327. strlcat( NGIRCd_VersionAddition, "IDENT", sizeof NGIRCd_VersionAddition );
  328. #endif
  329. #ifdef DEBUG
  330. if( NGIRCd_VersionAddition[0] )
  331. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  332. strlcat( NGIRCd_VersionAddition, "DEBUG", sizeof NGIRCd_VersionAddition );
  333. #endif
  334. #ifdef SNIFFER
  335. if( NGIRCd_VersionAddition[0] )
  336. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  337. strlcat( NGIRCd_VersionAddition, "SNIFFER", sizeof NGIRCd_VersionAddition );
  338. #endif
  339. #ifdef STRICT_RFC
  340. if( NGIRCd_VersionAddition[0] )
  341. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  342. strlcat( NGIRCd_VersionAddition, "RFC", sizeof NGIRCd_VersionAddition );
  343. #endif
  344. #ifdef IRCPLUS
  345. if( NGIRCd_VersionAddition[0] )
  346. strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition );
  347. strlcat( NGIRCd_VersionAddition, "IRCPLUS", sizeof NGIRCd_VersionAddition );
  348. #endif
  349. #ifdef WANT_IPV6
  350. if (NGIRCd_VersionAddition[0])
  351. strlcat(NGIRCd_VersionAddition, "+", sizeof(NGIRCd_VersionAddition));
  352. strlcat(NGIRCd_VersionAddition, "IPv6", sizeof(NGIRCd_VersionAddition));
  353. #endif
  354. if( NGIRCd_VersionAddition[0] )
  355. strlcat( NGIRCd_VersionAddition, "-", sizeof( NGIRCd_VersionAddition ));
  356. strlcat( NGIRCd_VersionAddition, TARGET_CPU, sizeof( NGIRCd_VersionAddition ));
  357. strlcat( NGIRCd_VersionAddition, "/", sizeof( NGIRCd_VersionAddition ));
  358. strlcat( NGIRCd_VersionAddition, TARGET_VENDOR, sizeof( NGIRCd_VersionAddition ));
  359. strlcat( NGIRCd_VersionAddition, "/", sizeof( NGIRCd_VersionAddition ));
  360. strlcat( NGIRCd_VersionAddition, TARGET_OS, sizeof( NGIRCd_VersionAddition ));
  361. snprintf(NGIRCd_Version, sizeof NGIRCd_Version, "%s %s-%s",
  362. PACKAGE_NAME, PACKAGE_VERSION, NGIRCd_VersionAddition);
  363. } /* Fill_Version */
  364. /**
  365. * Reload the server configuration file.
  366. */
  367. GLOBAL void
  368. NGIRCd_Rehash( void )
  369. {
  370. char old_name[CLIENT_ID_LEN];
  371. unsigned old_nicklen;
  372. Log( LOG_NOTICE|LOG_snotice, "Re-reading configuration NOW!" );
  373. NGIRCd_SignalRehash = false;
  374. /* Remember old server name and nick name length */
  375. strlcpy( old_name, Conf_ServerName, sizeof old_name );
  376. old_nicklen = Conf_MaxNickLength;
  377. /* Re-read configuration ... */
  378. if (!Conf_Rehash( ))
  379. return;
  380. /* Close down all listening sockets */
  381. Conn_ExitListeners( );
  382. /* Recover old server name and nick name length: these values can't
  383. * be changed during run-time */
  384. if (strcmp(old_name, Conf_ServerName) != 0 ) {
  385. strlcpy(Conf_ServerName, old_name, sizeof Conf_ServerName);
  386. Log(LOG_ERR, "Can't change \"ServerName\" on runtime! Ignored new name.");
  387. }
  388. if (old_nicklen != Conf_MaxNickLength) {
  389. Conf_MaxNickLength = old_nicklen;
  390. Log(LOG_ERR, "Can't change \"MaxNickLength\" on runtime! Ignored new value.");
  391. }
  392. /* Create new pre-defined channels */
  393. Channel_InitPredefined( );
  394. /* Start listening on sockets */
  395. Conn_InitListeners( );
  396. /* Sync configuration with established connections */
  397. Conn_SyncServerStruct( );
  398. Log( LOG_NOTICE|LOG_snotice, "Re-reading of configuration done." );
  399. } /* NGIRCd_Rehash */
  400. /**
  401. * Initialize the signal handler.
  402. */
  403. static void
  404. Initialize_Signal_Handler( void )
  405. {
  406. /* Signal-Handler initialisieren: einige Signale
  407. * werden ignoriert, andere speziell behandelt. */
  408. #ifdef HAVE_SIGACTION
  409. /* sigaction() ist vorhanden */
  410. struct sigaction saction;
  411. /* Signal-Struktur initialisieren */
  412. memset( &saction, 0, sizeof( saction ));
  413. saction.sa_handler = Signal_Handler;
  414. #ifdef SA_RESTART
  415. saction.sa_flags |= SA_RESTART;
  416. #endif
  417. #ifdef SA_NOCLDWAIT
  418. saction.sa_flags |= SA_NOCLDWAIT;
  419. #endif
  420. /* Signal-Handler einhaengen */
  421. sigaction(SIGINT, &saction, NULL);
  422. sigaction(SIGQUIT, &saction, NULL);
  423. sigaction(SIGTERM, &saction, NULL);
  424. sigaction(SIGHUP, &saction, NULL);
  425. sigaction(SIGCHLD, &saction, NULL);
  426. /* einige Signale ignorieren */
  427. saction.sa_handler = SIG_IGN;
  428. sigaction(SIGPIPE, &saction, NULL);
  429. #else
  430. /* kein sigaction() vorhanden */
  431. /* Signal-Handler einhaengen */
  432. signal(SIGINT, Signal_Handler);
  433. signal(SIGQUIT, Signal_Handler);
  434. signal(SIGTERM, Signal_Handler);
  435. signal(SIGHUP, Signal_Handler);
  436. signal(SIGCHLD, Signal_Handler);
  437. /* einige Signale ignorieren */
  438. signal(SIGPIPE, SIG_IGN);
  439. #endif
  440. } /* Initialize_Signal_Handler */
  441. /**
  442. * Signal handler of ngIRCd.
  443. * This function is called whenever ngIRCd catches a signal sent by the
  444. * user and/or the system to it. For example SIGTERM and SIGHUP.
  445. * @param Signal Number of the signal to handle.
  446. */
  447. static void
  448. Signal_Handler( int Signal )
  449. {
  450. switch( Signal )
  451. {
  452. case SIGTERM:
  453. case SIGINT:
  454. case SIGQUIT:
  455. /* wir soll(t)en uns wohl beenden ... */
  456. NGIRCd_SignalQuit = true;
  457. break;
  458. case SIGHUP:
  459. /* Konfiguration neu einlesen: */
  460. NGIRCd_SignalRehash = true;
  461. break;
  462. case SIGCHLD:
  463. /* Child-Prozess wurde beendet. Zombies vermeiden: */
  464. while( waitpid( -1, NULL, WNOHANG ) > 0);
  465. break;
  466. #ifdef DEBUG
  467. default:
  468. /* unbekanntes bzw. unbehandeltes Signal */
  469. Log( LOG_DEBUG, "Got signal %d! Ignored.", Signal );
  470. #endif
  471. }
  472. } /* Signal_Handler */
  473. /**
  474. * Display copyright and version information of ngIRCd on the console.
  475. */
  476. static void
  477. Show_Version( void )
  478. {
  479. puts( NGIRCd_Version );
  480. puts( "Copyright (c)2001-2008 Alexander Barton (<alex@barton.de>) and Contributors." );
  481. puts( "Homepage: <http://ngircd.barton.de/>\n" );
  482. puts( "This is free software; see the source for copying conditions. There is NO" );
  483. puts( "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." );
  484. } /* Show_Version */
  485. /**
  486. * Display a short help text on the console.
  487. * This help depends on the configuration of the executable and only shows
  488. * options that are actually enabled.
  489. */
  490. static void
  491. Show_Help( void )
  492. {
  493. #ifdef DEBUG
  494. puts( " -d, --debug log extra debug messages" );
  495. #endif
  496. puts( " -f, --config <f> use file <f> as configuration file" );
  497. puts( " -n, --nodaemon don't fork and don't detach from controlling terminal" );
  498. puts( " -p, --passive disable automatic connections to other servers" );
  499. #ifdef SNIFFER
  500. puts( " -s, --sniffer enable network sniffer and display all IRC traffic" );
  501. #endif
  502. puts( " -t, --configtest read, validate and display configuration; then exit" );
  503. puts( " -V, --version output version information and exit" );
  504. puts( " -h, --help display this help and exit" );
  505. } /* Show_Help */
  506. /**
  507. * Delete the file containing the process ID (PID).
  508. */
  509. static void
  510. Pidfile_Delete( void )
  511. {
  512. /* Pidfile configured? */
  513. if( ! Conf_PidFile[0] ) return;
  514. #ifdef DEBUG
  515. Log( LOG_DEBUG, "Removing PID file (%s) ...", Conf_PidFile );
  516. #endif
  517. if( unlink( Conf_PidFile ))
  518. Log( LOG_ERR, "Error unlinking PID file (%s): %s", Conf_PidFile, strerror( errno ));
  519. } /* Pidfile_Delete */
  520. /**
  521. * Create the file containing the process ID of ngIRCd ("PID file").
  522. * @param pid The process ID to be stored in this file.
  523. */
  524. static void
  525. Pidfile_Create(pid_t pid)
  526. {
  527. int pidfd;
  528. char pidbuf[64];
  529. int len;
  530. /* Pidfile configured? */
  531. if( ! Conf_PidFile[0] ) return;
  532. #ifdef DEBUG
  533. Log( LOG_DEBUG, "Creating PID file (%s) ...", Conf_PidFile );
  534. #endif
  535. pidfd = open( Conf_PidFile, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
  536. if ( pidfd < 0 ) {
  537. Log( LOG_ERR, "Error writing PID file (%s): %s", Conf_PidFile, strerror( errno ));
  538. return;
  539. }
  540. len = snprintf(pidbuf, sizeof pidbuf, "%ld\n", (long)pid);
  541. if (len < 0 || len >= (int)sizeof pidbuf) {
  542. Log( LOG_ERR, "Error converting pid");
  543. return;
  544. }
  545. if (write(pidfd, pidbuf, (size_t)len) != (ssize_t)len)
  546. Log( LOG_ERR, "Can't write PID file (%s): %s", Conf_PidFile, strerror( errno ));
  547. if( close(pidfd) != 0 )
  548. Log( LOG_ERR, "Error closing PID file (%s): %s", Conf_PidFile, strerror( errno ));
  549. } /* Pidfile_Create */
  550. /**
  551. * Redirect stdin, stdout and stderr to apropriate file handles.
  552. */
  553. static void
  554. Setup_FDStreams( void )
  555. {
  556. int fd;
  557. /* Test if we can open /dev/null for reading and writing. If not
  558. * we are most probably chrooted already and the server has been
  559. * restarted. So we simply don't try to redirect stdXXX ... */
  560. fd = open( "/dev/null", O_RDWR );
  561. if ( fd < 0 ) {
  562. Log(LOG_WARNING, "Could not open /dev/null: %s", strerror(errno));
  563. return;
  564. }
  565. fflush(stdout);
  566. fflush(stderr);
  567. /* Create new stdin(0), stdout(1) and stderr(2) descriptors */
  568. dup2( fd, 0 ); dup2( fd, 1 ); dup2( fd, 2 );
  569. /* Close newly opened file descriptor if not stdin/out/err */
  570. if( fd > 2 ) close( fd );
  571. } /* Setup_FDStreams */
  572. static bool
  573. NGIRCd_getNobodyID(uid_t *uid, gid_t *gid )
  574. {
  575. struct passwd *pwd;
  576. pwd = getpwnam("nobody");
  577. if (!pwd) return false;
  578. if ( !pwd->pw_uid || !pwd->pw_gid)
  579. return false;
  580. *uid = pwd->pw_uid;
  581. *gid = pwd->pw_gid;
  582. endpwent();
  583. return true;
  584. }
  585. static bool
  586. NGIRCd_Init( bool NGIRCd_NoDaemon )
  587. {
  588. static bool initialized;
  589. bool chrooted = false;
  590. struct passwd *pwd;
  591. struct group *grp;
  592. int real_errno;
  593. pid_t pid;
  594. if (initialized)
  595. return true;
  596. if( Conf_Chroot[0] ) {
  597. if( chdir( Conf_Chroot ) != 0 ) {
  598. Log( LOG_ERR, "Can't chdir() in ChrootDir (%s): %s", Conf_Chroot, strerror( errno ));
  599. return false;
  600. }
  601. if( chroot( Conf_Chroot ) != 0 ) {
  602. if (errno != EPERM) {
  603. Log( LOG_ERR, "Can't change root directory to \"%s\": %s",
  604. Conf_Chroot, strerror( errno ));
  605. return false;
  606. }
  607. } else {
  608. chrooted = true;
  609. Log( LOG_INFO, "Changed root and working directory to \"%s\".", Conf_Chroot );
  610. }
  611. }
  612. if (Conf_UID == 0) {
  613. Log(LOG_INFO, "ServerUID must not be 0, using \"nobody\" instead.", Conf_UID);
  614. if (! NGIRCd_getNobodyID(&Conf_UID, &Conf_GID)) {
  615. Log(LOG_WARNING, "Could not get user/group ID of user \"nobody\": %s",
  616. errno ? strerror(errno) : "not found" );
  617. return false;
  618. }
  619. }
  620. if (getgid() != Conf_GID) {
  621. /* Change group ID */
  622. if (setgid(Conf_GID) != 0) {
  623. real_errno = errno;
  624. Log( LOG_ERR, "Can't change group ID to %u: %s", Conf_GID, strerror( errno ));
  625. if (real_errno != EPERM)
  626. return false;
  627. }
  628. }
  629. if (getuid() != Conf_UID) {
  630. /* Change user ID */
  631. if (setuid(Conf_UID) != 0) {
  632. real_errno = errno;
  633. Log(LOG_ERR, "Can't change user ID to %u: %s", Conf_UID, strerror(errno));
  634. if (real_errno != EPERM)
  635. return false;
  636. }
  637. }
  638. initialized = true;
  639. /* Normally a child process is forked which isn't any longer
  640. * connected to ther controlling terminal. Use "--nodaemon"
  641. * to disable this "daemon mode" (useful for debugging). */
  642. if ( ! NGIRCd_NoDaemon ) {
  643. pid = fork( );
  644. if( pid > 0 ) {
  645. /* "Old" process: exit. */
  646. exit( 0 );
  647. }
  648. if( pid < 0 ) {
  649. /* Error!? */
  650. fprintf( stderr, "%s: Can't fork: %s!\nFatal error, exiting now ...\n",
  651. PACKAGE_NAME, strerror( errno ));
  652. exit( 1 );
  653. }
  654. /* New child process */
  655. (void)setsid( );
  656. chdir( "/" );
  657. /* Detach stdin, stdout and stderr */
  658. Setup_FDStreams( );
  659. }
  660. pid = getpid();
  661. Pidfile_Create( pid );
  662. /* Check UID/GID we are running as, can be different from values
  663. * configured (e. g. if we were already started with a UID>0. */
  664. Conf_UID = getuid();
  665. Conf_GID = getgid();
  666. pwd = getpwuid( Conf_UID );
  667. grp = getgrgid( Conf_GID );
  668. Log( LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.",
  669. pwd ? pwd->pw_name : "unknown", Conf_UID,
  670. grp ? grp->gr_name : "unknown", Conf_GID, pid);
  671. if ( chrooted ) {
  672. Log( LOG_INFO, "Running chrooted, chrootdir \"%s\".", Conf_Chroot );
  673. return true;
  674. } else {
  675. Log( LOG_INFO, "Not running chrooted." );
  676. }
  677. /* Change working directory to home directory of the user
  678. * we are running as (only when running in daemon mode and not in chroot) */
  679. if ( pwd ) {
  680. if (!NGIRCd_NoDaemon ) {
  681. if( chdir( pwd->pw_dir ) == 0 )
  682. Log( LOG_DEBUG, "Changed working directory to \"%s\" ...", pwd->pw_dir );
  683. else
  684. Log( LOG_INFO, "Notice: Can't change working directory to \"%s\": %s",
  685. pwd->pw_dir, strerror( errno ));
  686. }
  687. } else {
  688. Log( LOG_ERR, "Can't get user informaton for UID %d!?", Conf_UID );
  689. }
  690. return true;
  691. }
  692. /* -eof- */