]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * IRC - Internet Relay Chat, ircd/ircd.c | |
3 | * Copyright (C) 1990 Jarkko Oikarinen and | |
4 | * University of Oulu, Computing Center | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 1, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | /** @file | |
21 | * @brief Entry point and other initialization functions for the daemon. | |
22 | * @version $Id: ircd.c,v 1.91.2.3 2006/05/08 01:55:08 entrope Exp $ | |
23 | */ | |
24 | #include "config.h" | |
25 | ||
26 | #include "ircd.h" | |
27 | #include "IPcheck.h" | |
28 | #include "class.h" | |
29 | #include "client.h" | |
30 | #include "crule.h" | |
31 | #include "destruct_event.h" | |
32 | #include "hash.h" | |
33 | #include "ircd_alloc.h" | |
34 | #include "ircd_events.h" | |
35 | #include "ircd_features.h" | |
36 | #include "ircd_log.h" | |
37 | #include "ircd_reply.h" | |
38 | #include "ircd_signal.h" | |
39 | #include "ircd_string.h" | |
40 | #include "ircd_crypt.h" | |
41 | #include "jupe.h" | |
42 | #include "list.h" | |
43 | #include "match.h" | |
44 | #include "motd.h" | |
45 | #include "msg.h" | |
46 | #include "numeric.h" | |
47 | #include "numnicks.h" | |
48 | #include "opercmds.h" | |
49 | #include "parse.h" | |
50 | #include "res.h" | |
51 | #include "s_auth.h" | |
52 | #include "s_bsd.h" | |
53 | #include "s_conf.h" | |
54 | #include "s_debug.h" | |
55 | #include "s_misc.h" | |
56 | #include "s_stats.h" | |
57 | #include "send.h" | |
58 | #include "sys.h" | |
59 | #include "uping.h" | |
60 | #include "userload.h" | |
61 | #include "version.h" | |
62 | #include "whowas.h" | |
63 | ||
64 | /* #include <assert.h> -- Now using assert in ircd_log.h */ | |
65 | #include <errno.h> | |
66 | #include <fcntl.h> | |
67 | #include <netdb.h> | |
68 | #include <pwd.h> | |
69 | #include <stdio.h> | |
70 | #include <stdlib.h> | |
71 | #include <string.h> | |
72 | #include <sys/time.h> | |
73 | #ifdef HAVE_SYS_RESOURCE_H | |
74 | #include <sys/resource.h> | |
75 | #endif | |
76 | #include <sys/socket.h> | |
77 | #include <sys/stat.h> | |
78 | #include <sys/types.h> | |
79 | #include <unistd.h> | |
80 | ||
81 | ||
82 | ||
83 | /*---------------------------------------------------------------------------- | |
84 | * External stuff | |
85 | *--------------------------------------------------------------------------*/ | |
86 | extern void init_counters(void); | |
87 | extern void mem_dbg_initialise(void); | |
88 | ||
89 | /*---------------------------------------------------------------------------- | |
90 | * Constants / Enums | |
91 | *--------------------------------------------------------------------------*/ | |
92 | enum { | |
93 | BOOT_DEBUG = 1, /**< Enable debug output. */ | |
94 | BOOT_TTY = 2, /**< Stay connected to TTY. */ | |
95 | BOOT_CHKCONF = 4 /**< Exit after reading configuration file. */ | |
96 | }; | |
97 | ||
98 | ||
99 | /*---------------------------------------------------------------------------- | |
100 | * Global data (YUCK!) | |
101 | *--------------------------------------------------------------------------*/ | |
102 | struct Client me; /**< That's me */ | |
103 | struct Connection me_con; /**< That's me too */ | |
104 | struct Client *GlobalClientList = &me; /**< Pointer to beginning of | |
105 | Client list */ | |
106 | time_t TSoffset = 0; /**< Offset of timestamps to system clock */ | |
107 | int GlobalRehashFlag = 0; /**< do a rehash if set */ | |
108 | int GlobalRestartFlag = 0; /**< do a restart if set */ | |
109 | time_t CurrentTime; /**< Updated every time we leave select() */ | |
110 | ||
111 | char *configfile = CPATH; /**< Server configuration file */ | |
112 | int debuglevel = -1; /**< Server debug level */ | |
113 | char *debugmode = ""; /**< Server debug level */ | |
114 | static char *dpath = DPATH; /**< Working directory for daemon */ | |
115 | static char *dbg_client; /**< Client specifier for chkconf */ | |
116 | ||
117 | static struct Timer connect_timer; /**< timer structure for try_connections() */ | |
118 | static struct Timer ping_timer; /**< timer structure for check_pings() */ | |
119 | static struct Timer destruct_event_timer; /**< timer structure for exec_expired_destruct_events() */ | |
120 | ||
121 | /** Daemon information. */ | |
122 | static struct Daemon thisServer = { 0, 0, 0, 0, 0, 0, -1 }; | |
123 | ||
124 | /** Non-zero until we want to exit. */ | |
125 | int running = 1; | |
126 | ||
127 | ||
128 | /*---------------------------------------------------------------------------- | |
129 | * API: server_die | |
130 | *--------------------------------------------------------------------------*/ | |
131 | /** Terminate the server with a message. | |
132 | * @param[in] message Message to log and send to operators. | |
133 | */ | |
134 | void server_die(const char *message) | |
135 | { | |
136 | /* log_write will send out message to both log file and as server notice */ | |
137 | log_write(LS_SYSTEM, L_CRIT, 0, "Server terminating: %s", message); | |
138 | flush_connections(0); | |
139 | close_connections(1); | |
140 | running = 0; | |
141 | } | |
142 | ||
143 | /*---------------------------------------------------------------------------- | |
144 | * API: server_panic | |
145 | *--------------------------------------------------------------------------*/ | |
146 | /** Immediately terminate the server with a message. | |
147 | * @param[in] message Message to log, but not send to operators. | |
148 | */ | |
149 | void server_panic(const char *message) | |
150 | { | |
151 | /* inhibit sending server notice--we may be panicking due to low memory */ | |
152 | log_write(LS_SYSTEM, L_CRIT, LOG_NOSNOTICE, "Server panic: %s", message); | |
153 | flush_connections(0); | |
154 | log_close(); | |
155 | close_connections(1); | |
156 | exit(1); | |
157 | } | |
158 | ||
159 | /*---------------------------------------------------------------------------- | |
160 | * API: server_restart | |
161 | *--------------------------------------------------------------------------*/ | |
162 | /** Restart the server with a message. | |
163 | * @param[in] message Message to log and send to operators. | |
164 | */ | |
165 | void server_restart(const char *message) | |
166 | { | |
167 | static int restarting = 0; | |
168 | ||
169 | /* inhibit sending any server notices; we may be in a loop */ | |
170 | log_write(LS_SYSTEM, L_WARNING, LOG_NOSNOTICE, "Restarting Server: %s", | |
171 | message); | |
172 | if (restarting++) /* increment restarting to prevent looping */ | |
173 | return; | |
174 | ||
175 | sendto_opmask_butone(0, SNO_OLDSNO, "Restarting server: %s", message); | |
176 | Debug((DEBUG_NOTICE, "Restarting server...")); | |
177 | flush_connections(0); | |
178 | ||
179 | log_close(); | |
180 | ||
181 | close_connections(!(thisServer.bootopt & (BOOT_TTY | BOOT_DEBUG | BOOT_CHKCONF))); | |
182 | ||
183 | reap_children(); | |
184 | ||
185 | execv(SPATH, thisServer.argv); | |
186 | ||
187 | /* Have to reopen since it has been closed above */ | |
188 | log_reopen(); | |
189 | ||
190 | log_write(LS_SYSTEM, L_CRIT, 0, "execv(%s,%s) failed: %m", SPATH, | |
191 | *thisServer.argv); | |
192 | ||
193 | Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s", | |
194 | SPATH, (strerror(errno)) ? strerror(errno) : "")); | |
195 | exit(8); | |
196 | } | |
197 | ||
198 | ||
199 | /*---------------------------------------------------------------------------- | |
200 | * outofmemory: Handler for out of memory conditions... | |
201 | *--------------------------------------------------------------------------*/ | |
202 | /** Handle out-of-memory condition. */ | |
203 | static void outofmemory(void) { | |
204 | Debug((DEBUG_FATAL, "Out of memory: restarting server...")); | |
205 | server_restart("Out of Memory"); | |
206 | } | |
207 | ||
208 | ||
209 | /*---------------------------------------------------------------------------- | |
210 | * write_pidfile | |
211 | *--------------------------------------------------------------------------*/ | |
212 | /** Write process ID to PID file. */ | |
213 | static void write_pidfile(void) { | |
214 | char buff[20]; | |
215 | ||
216 | if (thisServer.pid_fd >= 0) { | |
217 | memset(buff, 0, sizeof(buff)); | |
218 | sprintf(buff, "%5d\n", (int)getpid()); | |
219 | if (write(thisServer.pid_fd, buff, strlen(buff)) == -1) | |
220 | Debug((DEBUG_NOTICE, "Error writing to pid file %s: %m", | |
221 | feature_str(FEAT_PPATH))); | |
222 | return; | |
223 | } | |
224 | Debug((DEBUG_NOTICE, "Error opening pid file %s: %m", | |
225 | feature_str(FEAT_PPATH))); | |
226 | } | |
227 | ||
228 | /** Try to create the PID file. | |
229 | * @return Zero on success; non-zero on any error. | |
230 | */ | |
231 | static int check_pid(void) | |
232 | { | |
233 | struct flock lock; | |
234 | ||
235 | lock.l_type = F_WRLCK; | |
236 | lock.l_start = 0; | |
237 | lock.l_whence = SEEK_SET; | |
238 | lock.l_len = 0; | |
239 | ||
240 | if ((thisServer.pid_fd = open(feature_str(FEAT_PPATH), O_CREAT | O_RDWR, | |
241 | 0600)) >= 0) | |
242 | return fcntl(thisServer.pid_fd, F_SETLK, &lock) == -1; | |
243 | ||
244 | return 1; | |
245 | } | |
246 | ||
247 | ||
248 | /** Look for any connections that we should try to initiate. | |
249 | * Reschedules itself to run again at the appropriate time. | |
250 | * @param[in] ev Timer event (ignored). | |
251 | */ | |
252 | static void try_connections(struct Event* ev) { | |
253 | struct ConfItem* aconf; | |
254 | struct ConfItem** pconf; | |
255 | time_t next; | |
256 | struct Jupe* ajupe; | |
257 | int hold; | |
258 | int done; | |
259 | ||
260 | assert(ET_EXPIRE == ev_type(ev)); | |
261 | assert(0 != ev_timer(ev)); | |
262 | ||
263 | Debug((DEBUG_NOTICE, "Connection check at : %s", myctime(CurrentTime))); | |
264 | next = CurrentTime + feature_int(FEAT_CONNECTFREQUENCY); | |
265 | done = 0; | |
266 | ||
267 | for (aconf = GlobalConfList; aconf; aconf = aconf->next) { | |
268 | /* Only consider server items with non-zero port and non-zero | |
269 | * connect times that are not actively juped. | |
270 | */ | |
271 | if (!(aconf->status & CONF_SERVER) | |
272 | || aconf->address.port == 0 | |
273 | || !(aconf->flags & CONF_AUTOCONNECT) | |
274 | || ((ajupe = jupe_find(aconf->name)) && JupeIsActive(ajupe))) | |
275 | continue; | |
276 | ||
277 | /* Do we need to postpone this connection further? */ | |
278 | hold = aconf->hold > CurrentTime; | |
279 | ||
280 | /* Update next possible connection check time. */ | |
281 | if (hold && next > aconf->hold) | |
282 | next = aconf->hold; | |
283 | ||
284 | /* Do not try to connect if its use is still on hold until future, | |
285 | * we have already initiated a connection this try_connections(), | |
286 | * too many links in its connection class, it is already linked, | |
287 | * or if connect rules forbid a link now. | |
288 | */ | |
289 | if (hold || done | |
290 | || (ConfLinks(aconf) > ConfMaxLinks(aconf)) | |
291 | || FindServer(aconf->name) | |
292 | || conf_eval_crule(aconf->name, CRULE_MASK)) | |
293 | continue; | |
294 | ||
295 | /* Ensure it is at the end of the list for future checks. */ | |
296 | if (aconf->next) { | |
297 | /* Find aconf's location in the list and splice it out. */ | |
298 | for (pconf = &GlobalConfList; *pconf; pconf = &(*pconf)->next) | |
299 | if (*pconf == aconf) | |
300 | *pconf = aconf->next; | |
301 | /* Reinsert it at the end of the list (where pconf is now). */ | |
302 | *pconf = aconf; | |
303 | aconf->next = 0; | |
304 | } | |
305 | ||
306 | /* Activate the connection itself. */ | |
307 | if (connect_server(aconf, 0)) | |
308 | sendto_opmask_butone(0, SNO_OLDSNO, "Connection to %s activated.", | |
309 | aconf->name); | |
310 | ||
311 | /* And stop looking for further candidates. */ | |
312 | done = 1; | |
313 | } | |
314 | ||
315 | Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next))); | |
316 | timer_add(&connect_timer, try_connections, 0, TT_ABSOLUTE, next); | |
317 | } | |
318 | ||
319 | ||
320 | /** Check for clients that have not sent a ping response recently. | |
321 | * Reschedules itself to run again at the appropriate time. | |
322 | * @param[in] ev Timer event (ignored). | |
323 | */ | |
324 | static void check_pings(struct Event* ev) { | |
325 | int expire = 0; | |
326 | int next_check = CurrentTime; | |
327 | int max_ping = 0; | |
328 | int i; | |
329 | ||
330 | assert(ET_EXPIRE == ev_type(ev)); | |
331 | assert(0 != ev_timer(ev)); | |
332 | ||
333 | next_check += feature_int(FEAT_PINGFREQUENCY); | |
334 | ||
335 | /* Scan through the client table */ | |
336 | for (i=0; i <= HighestFd; i++) { | |
337 | struct Client *cptr = LocalClientArray[i]; | |
338 | ||
339 | if (!cptr) | |
340 | continue; | |
341 | ||
342 | assert(&me != cptr); /* I should never be in the local client array! */ | |
343 | ||
344 | ||
345 | /* Remove dead clients. */ | |
346 | if (IsDead(cptr)) { | |
347 | exit_client(cptr, cptr, &me, cli_info(cptr)); | |
348 | continue; | |
349 | } | |
350 | ||
351 | max_ping = IsRegistered(cptr) ? client_get_ping(cptr) : | |
352 | feature_int(FEAT_CONNECTTIMEOUT); | |
353 | ||
354 | Debug((DEBUG_DEBUG, "check_pings(%s)=status:%s limit: %d current: %d", | |
355 | cli_name(cptr), | |
356 | IsPingSent(cptr) ? "[Ping Sent]" : "[]", | |
357 | max_ping, (int)(CurrentTime - cli_lasttime(cptr)))); | |
358 | ||
359 | /* Ok, the thing that will happen most frequently, is that someone will | |
360 | * have sent something recently. Cover this first for speed. | |
361 | * -- | |
362 | * If it's an unregistered client and hasn't managed to register within | |
363 | * max_ping then it's obviously having problems (broken client) or it's | |
364 | * just up to no good, so we won't skip it, even if its been sending | |
365 | * data to us. | |
366 | * -- hikari | |
367 | */ | |
368 | if ((CurrentTime-cli_lasttime(cptr) < max_ping) && IsRegistered(cptr)) { | |
369 | expire = cli_lasttime(cptr) + max_ping; | |
370 | if (expire < next_check) | |
371 | next_check = expire; | |
372 | continue; | |
373 | } | |
374 | ||
375 | /* Unregistered clients pingout after max_ping seconds, they don't | |
376 | * get given a second chance - if they were then people could not quite | |
377 | * finish registration and hold resources without being subject to k/g | |
378 | * lines | |
379 | */ | |
380 | if (!IsRegistered(cptr)) { | |
381 | assert(!IsServer(cptr)); | |
382 | /* If client authorization time has expired, ask auth whether they | |
383 | * should be checked again later. */ | |
384 | if ((CurrentTime-cli_firsttime(cptr) >= max_ping) | |
385 | && auth_ping_timeout(cptr)) | |
386 | continue; | |
387 | /* OK, they still have enough time left, so we'll just skip to the | |
388 | * next client. Set the next check to be when their time is up, if | |
389 | * that's before the currently scheduled next check -- hikari */ | |
390 | expire = cli_firsttime(cptr) + max_ping; | |
391 | if (expire < next_check) | |
392 | next_check = expire; | |
393 | continue; | |
394 | } | |
395 | ||
396 | /* Quit the client after max_ping*2 - they should have answered by now */ | |
397 | if (CurrentTime-cli_lasttime(cptr) >= (max_ping*2) ) | |
398 | { | |
399 | /* If it was a server, then tell ops about it. */ | |
400 | if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) | |
401 | sendto_opmask_butone(0, SNO_OLDSNO, | |
402 | "No response from %s, closing link", | |
403 | cli_name(cptr)); | |
404 | exit_client_msg(cptr, cptr, &me, "Ping timeout"); | |
405 | continue; | |
406 | } | |
407 | ||
408 | if (!IsPingSent(cptr)) | |
409 | { | |
410 | /* If we haven't PINGed the connection and we haven't heard from it in a | |
411 | * while, PING it to make sure it is still alive. | |
412 | */ | |
413 | SetPingSent(cptr); | |
414 | ||
415 | /* If we're late in noticing don't hold it against them :) */ | |
416 | cli_lasttime(cptr) = CurrentTime - max_ping; | |
417 | ||
418 | if (IsUser(cptr)) | |
419 | sendrawto_one(cptr, MSG_PING " :%s", cli_name(&me)); | |
420 | else | |
421 | { | |
422 | char *asll_ts = militime_float(NULL); | |
423 | sendcmdto_one(&me, CMD_PING, cptr, "!%s %s %s", asll_ts, | |
424 | cli_name(cptr), asll_ts); | |
425 | } | |
426 | } | |
427 | ||
428 | expire = cli_lasttime(cptr) + max_ping * 2; | |
429 | if (expire < next_check) | |
430 | next_check=expire; | |
431 | } | |
432 | ||
433 | assert(next_check >= CurrentTime); | |
434 | ||
435 | Debug((DEBUG_DEBUG, "[%i] check_pings() again in %is", | |
436 | CurrentTime, next_check-CurrentTime)); | |
437 | ||
438 | timer_add(&ping_timer, check_pings, 0, TT_ABSOLUTE, next_check); | |
439 | } | |
440 | ||
441 | ||
442 | /** Parse command line arguments. | |
443 | * Global variables are updated to reflect the arguments. | |
444 | * As a side effect, makes sure the process's effective user id is the | |
445 | * same as the real user id. | |
446 | * @param[in] argc Number of arguments on command line. | |
447 | * @param[in,out] argv Command-lne arguments. | |
448 | */ | |
449 | static void parse_command_line(int argc, char** argv) { | |
450 | const char *options = "d:f:h:nktvx:c:"; | |
451 | int opt; | |
452 | ||
453 | if (thisServer.euid != thisServer.uid) | |
454 | setuid(thisServer.uid); | |
455 | ||
456 | /* Do we really need to sanity check the non-NULLness of optarg? That's | |
457 | * getopt()'s job... Removing those... -zs | |
458 | */ | |
459 | while ((opt = getopt(argc, argv, options)) != EOF) | |
460 | switch (opt) { | |
461 | case 'k': thisServer.bootopt |= BOOT_CHKCONF | BOOT_TTY; break; | |
462 | case 'c': dbg_client = optarg; break; | |
463 | case 'n': | |
464 | case 't': thisServer.bootopt |= BOOT_TTY; break; | |
465 | case 'd': dpath = optarg; break; | |
466 | case 'f': configfile = optarg; break; | |
467 | case 'h': ircd_strncpy(cli_name(&me), optarg, HOSTLEN); break; | |
468 | case 'v': | |
469 | printf("ircd %s\n", version); | |
470 | printf("Event engines: "); | |
471 | #ifdef USE_KQUEUE | |
472 | printf("kqueue() "); | |
473 | #endif | |
474 | #ifdef USE_DEVPOLL | |
475 | printf("/dev/poll "); | |
476 | #endif | |
477 | #ifdef USE_EPOLL | |
478 | printf("epoll_*() "); | |
479 | #endif | |
480 | #ifdef USE_POLL | |
481 | printf("poll()"); | |
482 | #else | |
483 | printf("select()"); | |
484 | #endif | |
485 | printf("\nCompiled for a maximum of %d connections.\n", MAXCONNECTIONS); | |
486 | ||
487 | ||
488 | exit(0); | |
489 | break; | |
490 | ||
491 | case 'x': | |
492 | debuglevel = atoi(optarg); | |
493 | if (debuglevel < 0) | |
494 | debuglevel = 0; | |
495 | debugmode = optarg; | |
496 | thisServer.bootopt |= BOOT_DEBUG; | |
497 | break; | |
498 | ||
499 | default: | |
500 | printf("Usage: ircd [-f config] [-h servername] [-x loglevel] [-ntv] [-k [-c clispec]]\n" | |
501 | "\n -f config\t specify explicit configuration file" | |
502 | "\n -x loglevel\t set debug logging verbosity" | |
503 | "\n -n or -t\t don't detach" | |
504 | "\n -v\t\t display version" | |
505 | "\n -k\t\t exit after checking config" | |
506 | "\n -c clispec\t search for client/kill blocks matching client" | |
507 | "\n\t\t clispec is comma-separated list of user@host," | |
508 | "\n\t\t user@ip, $Rrealname, and port number" | |
509 | "\n\nServer not started.\n"); | |
510 | exit(1); | |
511 | } | |
512 | } | |
513 | ||
514 | ||
515 | /** Become a daemon. | |
516 | * @param[in] no_fork If non-zero, do not fork into the background. | |
517 | */ | |
518 | static void daemon_init(int no_fork) { | |
519 | if (no_fork) | |
520 | return; | |
521 | ||
522 | if (fork()) | |
523 | exit(0); | |
524 | ||
525 | #ifdef TIOCNOTTY | |
526 | { | |
527 | int fd; | |
528 | if ((fd = open("/dev/tty", O_RDWR)) > -1) { | |
529 | ioctl(fd, TIOCNOTTY, 0); | |
530 | close(fd); | |
531 | } | |
532 | } | |
533 | #endif | |
534 | ||
535 | setsid(); | |
536 | } | |
537 | ||
538 | /** Check that we have access to a particular file. | |
539 | * If we do not have access to the file, complain on stderr. | |
540 | * @param[in] path File name to check for access. | |
541 | * @param[in] which Configuration character associated with file. | |
542 | * @param[in] mode Bitwise combination of R_OK, W_OK, X_OK and/or F_OK. | |
543 | * @return Non-zero if we have the necessary access, zero if not. | |
544 | */ | |
545 | static char check_file_access(const char *path, char which, int mode) { | |
546 | if (!access(path, mode)) | |
547 | return 1; | |
548 | ||
549 | fprintf(stderr, | |
550 | "Check on %cPATH (%s) failed: %s\n" | |
551 | "Please create this file and/or rerun `configure' " | |
552 | "using --with-%cpath and recompile to correct this.\n", | |
553 | which, path, strerror(errno), which); | |
554 | ||
555 | return 0; | |
556 | } | |
557 | ||
558 | ||
559 | /*---------------------------------------------------------------------------- | |
560 | * set_core_limit | |
561 | *--------------------------------------------------------------------------*/ | |
562 | #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE) | |
563 | /** Set the core size soft limit to the same as the hard limit. */ | |
564 | static void set_core_limit(void) { | |
565 | struct rlimit corelim; | |
566 | ||
567 | if (getrlimit(RLIMIT_CORE, &corelim)) { | |
568 | fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno)); | |
569 | corelim.rlim_max = RLIM_INFINITY; /* Try to recover */ | |
570 | } | |
571 | ||
572 | corelim.rlim_cur = corelim.rlim_max; | |
573 | if (setrlimit(RLIMIT_CORE, &corelim)) | |
574 | fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno)); | |
575 | } | |
576 | #endif | |
577 | ||
578 | ||
579 | ||
580 | /** Complain to stderr if any user or group ID belongs to the superuser. | |
581 | * @return Non-zero if all IDs are okay, zero if some are 0. | |
582 | */ | |
583 | static int set_userid_if_needed(void) { | |
584 | if (getuid() == 0 || geteuid() == 0 || | |
585 | getgid() == 0 || getegid() == 0) { | |
586 | fprintf(stderr, "ERROR: This server will not run as superuser.\n"); | |
587 | return 0; | |
588 | } | |
589 | ||
590 | return 1; | |
591 | } | |
592 | ||
593 | ||
594 | /*---------------------------------------------------------------------------- | |
595 | * main - entrypoint | |
596 | * | |
597 | * TODO: This should set the basic environment up and start the main loop. | |
598 | * we're doing waaaaaaaaay too much server initialization here. I hate | |
599 | * long and ugly control paths... -smd | |
600 | *--------------------------------------------------------------------------*/ | |
601 | /** Run the daemon. | |
602 | * @param[in] argc Number of arguments in \a argv. | |
603 | * @param[in] argv Arguments to program execution. | |
604 | */ | |
605 | int main(int argc, char **argv) { | |
606 | CurrentTime = time(NULL); | |
607 | ||
608 | thisServer.argc = argc; | |
609 | thisServer.argv = argv; | |
610 | thisServer.uid = getuid(); | |
611 | thisServer.euid = geteuid(); | |
612 | ||
613 | #ifdef MDEBUG | |
614 | mem_dbg_initialise(); | |
615 | #endif | |
616 | ||
617 | #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE) | |
618 | set_core_limit(); | |
619 | #endif | |
620 | ||
621 | umask(077); /* better safe than sorry --SRB */ | |
622 | memset(&me, 0, sizeof(me)); | |
623 | memset(&me_con, 0, sizeof(me_con)); | |
624 | cli_connect(&me) = &me_con; | |
625 | cli_fd(&me) = -1; | |
626 | ||
627 | parse_command_line(argc, argv); | |
628 | ||
629 | if (chdir(dpath)) { | |
630 | fprintf(stderr, "Fail: Cannot chdir(%s): %s, check DPATH\n", dpath, strerror(errno)); | |
631 | return 2; | |
632 | } | |
633 | ||
634 | if (!set_userid_if_needed()) | |
635 | return 3; | |
636 | ||
637 | /* Check paths for accessibility */ | |
638 | if (!check_file_access(SPATH, 'S', X_OK) || | |
639 | !check_file_access(configfile, 'C', R_OK)) | |
640 | return 4; | |
641 | ||
642 | if (!init_connection_limits()) | |
643 | return 9; | |
644 | ||
645 | close_connections(!(thisServer.bootopt & (BOOT_DEBUG | BOOT_TTY | BOOT_CHKCONF))); | |
646 | ||
647 | /* daemon_init() must be before event_init() because kqueue() FDs | |
648 | * are, perversely, not inherited across fork(). | |
649 | */ | |
650 | daemon_init(thisServer.bootopt & BOOT_TTY); | |
651 | ||
652 | #ifdef DEBUGMODE | |
653 | /* Must reserve fd 2... */ | |
654 | if (debuglevel >= 0 && !(thisServer.bootopt & BOOT_TTY)) { | |
655 | int fd; | |
656 | if ((fd = open("/dev/null", O_WRONLY)) < 0) { | |
657 | fprintf(stderr, "Unable to open /dev/null (to reserve fd 2): %s\n", | |
658 | strerror(errno)); | |
659 | return 8; | |
660 | } | |
661 | if (fd != 2 && dup2(fd, 2) < 0) { | |
662 | fprintf(stderr, "Unable to reserve fd 2; dup2 said: %s\n", | |
663 | strerror(errno)); | |
664 | return 8; | |
665 | } | |
666 | } | |
667 | #endif | |
668 | ||
669 | event_init(MAXCONNECTIONS); | |
670 | ||
671 | setup_signals(); | |
672 | feature_init(); /* initialize features... */ | |
673 | log_init(*argv); | |
674 | set_nomem_handler(outofmemory); | |
675 | ||
676 | initload(); | |
677 | init_list(); | |
678 | init_hash(); | |
679 | init_class(); | |
680 | initwhowas(); | |
681 | initmsgtree(); | |
682 | initstats(); | |
683 | ||
684 | /* we need this for now, when we're modular this | |
685 | should be removed -- hikari */ | |
686 | ircd_crypt_init(); | |
687 | ||
688 | motd_init(); | |
689 | ||
690 | if (!init_conf()) { | |
691 | log_write(LS_SYSTEM, L_CRIT, 0, "Failed to read configuration file %s", | |
692 | configfile); | |
693 | return 7; | |
694 | } | |
695 | ||
696 | if (thisServer.bootopt & BOOT_CHKCONF) { | |
697 | if (dbg_client) | |
698 | conf_debug_iline(dbg_client); | |
699 | fprintf(stderr, "Configuration file %s checked okay.\n", configfile); | |
700 | return 0; | |
701 | } | |
702 | ||
703 | debug_init(thisServer.bootopt & BOOT_TTY); | |
704 | if (check_pid()) { | |
705 | Debug((DEBUG_FATAL, "Failed to acquire PID file lock after fork")); | |
706 | exit(2); | |
707 | } | |
708 | ||
709 | init_server_identity(); | |
710 | ||
711 | uping_init(); | |
712 | ||
713 | stats_init(); | |
714 | ||
715 | IPcheck_init(); | |
716 | timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1); | |
717 | timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1); | |
718 | timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60); | |
719 | ||
720 | CurrentTime = time(NULL); | |
721 | ||
722 | SetMe(&me); | |
723 | cli_magic(&me) = CLIENT_MAGIC; | |
724 | cli_from(&me) = &me; | |
725 | make_server(&me); | |
726 | ||
727 | cli_serv(&me)->timestamp = TStime(); /* Abuse own link timestamp as start TS */ | |
728 | cli_serv(&me)->prot = atoi(MAJOR_PROTOCOL); | |
729 | cli_serv(&me)->up = &me; | |
730 | cli_serv(&me)->down = NULL; | |
731 | cli_handler(&me) = SERVER_HANDLER; | |
732 | ||
733 | SetYXXCapacity(&me, MAXCLIENTS); | |
734 | ||
735 | cli_lasttime(&me) = cli_since(&me) = cli_firsttime(&me) = CurrentTime; | |
736 | ||
737 | hAddClient(&me); | |
738 | ||
739 | write_pidfile(); | |
740 | init_counters(); | |
741 | ||
742 | Debug((DEBUG_NOTICE, "Server ready...")); | |
743 | log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready"); | |
744 | ||
745 | event_loop(); | |
746 | ||
747 | return 0; | |
748 | } | |
749 | ||
750 |