]>
Commit | Line | Data |
---|---|---|
189935b1 | 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 2005/09/12 22:52:01 decampos 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 | execv(SPATH, thisServer.argv); | |
184 | ||
185 | /* Have to reopen since it has been closed above */ | |
186 | log_reopen(); | |
187 | ||
188 | log_write(LS_SYSTEM, L_CRIT, 0, "execv(%s,%s) failed: %m", SPATH, | |
189 | *thisServer.argv); | |
190 | ||
191 | Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s", | |
192 | SPATH, (strerror(errno)) ? strerror(errno) : "")); | |
193 | exit(8); | |
194 | } | |
195 | ||
196 | ||
197 | /*---------------------------------------------------------------------------- | |
198 | * outofmemory: Handler for out of memory conditions... | |
199 | *--------------------------------------------------------------------------*/ | |
200 | /** Handle out-of-memory condition. */ | |
201 | static void outofmemory(void) { | |
202 | Debug((DEBUG_FATAL, "Out of memory: restarting server...")); | |
203 | server_restart("Out of Memory"); | |
204 | } | |
205 | ||
206 | ||
207 | /*---------------------------------------------------------------------------- | |
208 | * write_pidfile | |
209 | *--------------------------------------------------------------------------*/ | |
210 | /** Write process ID to PID file. */ | |
211 | static void write_pidfile(void) { | |
212 | char buff[20]; | |
213 | ||
214 | if (thisServer.pid_fd >= 0) { | |
215 | memset(buff, 0, sizeof(buff)); | |
216 | sprintf(buff, "%5d\n", (int)getpid()); | |
217 | if (write(thisServer.pid_fd, buff, strlen(buff)) == -1) | |
218 | Debug((DEBUG_NOTICE, "Error writing to pid file %s: %m", | |
219 | feature_str(FEAT_PPATH))); | |
220 | return; | |
221 | } | |
222 | Debug((DEBUG_NOTICE, "Error opening pid file %s: %m", | |
223 | feature_str(FEAT_PPATH))); | |
224 | } | |
225 | ||
226 | /** Try to create the PID file. | |
227 | * @return Zero on success; non-zero on any error. | |
228 | */ | |
229 | static int check_pid(void) | |
230 | { | |
231 | struct flock lock; | |
232 | ||
233 | lock.l_type = F_WRLCK; | |
234 | lock.l_start = 0; | |
235 | lock.l_whence = SEEK_SET; | |
236 | lock.l_len = 0; | |
237 | ||
238 | if ((thisServer.pid_fd = open(feature_str(FEAT_PPATH), O_CREAT | O_RDWR, | |
239 | 0600)) >= 0) | |
240 | return fcntl(thisServer.pid_fd, F_SETLK, &lock) == -1; | |
241 | ||
242 | return 1; | |
243 | } | |
244 | ||
245 | ||
246 | /** Look for any connections that we should try to initiate. | |
247 | * Reschedules itself to run again at the appropriate time. | |
248 | * @param[in] ev Timer event (ignored). | |
249 | */ | |
250 | static void try_connections(struct Event* ev) { | |
251 | struct ConfItem* aconf; | |
252 | struct ConfItem** pconf; | |
253 | time_t next = 0; | |
254 | struct ConnectionClass* cltmp; | |
255 | struct Jupe* ajupe; | |
256 | int hold; | |
257 | ||
258 | assert(ET_EXPIRE == ev_type(ev)); | |
259 | assert(0 != ev_timer(ev)); | |
260 | ||
261 | Debug((DEBUG_NOTICE, "Connection check at : %s", myctime(CurrentTime))); | |
262 | for (aconf = GlobalConfList; aconf; aconf = aconf->next) { | |
263 | /* Only consider server items with non-zero port and non-zero | |
264 | * connect times that are not actively juped. | |
265 | */ | |
266 | if (!(aconf->status & CONF_SERVER) | |
267 | || aconf->address.port == 0 | |
268 | || !(aconf->flags & CONF_AUTOCONNECT) | |
269 | || ((ajupe = jupe_find(aconf->name)) && JupeIsActive(ajupe))) | |
270 | continue; | |
271 | ||
272 | /* Do we need to postpone this connection further? */ | |
273 | hold = aconf->hold > CurrentTime; | |
274 | ||
275 | /* Update next possible connection check time. */ | |
276 | if (hold && (next > aconf->hold || next == 0)) | |
277 | next = aconf->hold; | |
278 | ||
279 | cltmp = aconf->conn_class; | |
280 | ||
281 | /* Do not try to connect if its use is still on hold until future, | |
282 | * too many links in its connection class, it is already linked, | |
283 | * or if connect rules forbid a link now. | |
284 | */ | |
285 | if (hold | |
286 | || (Links(cltmp) > MaxLinks(cltmp)) | |
287 | || FindServer(aconf->name) | |
288 | || conf_eval_crule(aconf->name, CRULE_MASK)) | |
289 | continue; | |
290 | ||
291 | /* Ensure it is at the end of the list for future checks. */ | |
292 | if (aconf->next) { | |
293 | /* Find aconf's location in the list and splice it out. */ | |
294 | for (pconf = &GlobalConfList; *pconf; pconf = &(*pconf)->next) | |
295 | if (*pconf == aconf) | |
296 | *pconf = aconf->next; | |
297 | /* Reinsert it at the end of the list (where pconf is now). */ | |
298 | *pconf = aconf; | |
299 | aconf->next = 0; | |
300 | } | |
301 | ||
302 | /* Activate the connection itself. */ | |
303 | if (connect_server(aconf, 0)) | |
304 | sendto_opmask_butone(0, SNO_OLDSNO, "Connection to %s activated.", | |
305 | aconf->name); | |
306 | ||
307 | /* And stop looking for further candidates. */ | |
308 | break; | |
309 | } | |
310 | ||
311 | if (next == 0) | |
312 | next = CurrentTime + feature_int(FEAT_CONNECTFREQUENCY); | |
313 | ||
314 | Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next))); | |
315 | ||
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 ((CurrentTime-cli_firsttime(cptr) >= max_ping)) { | |
383 | /* Display message if they have sent a NICK and a USER but no | |
384 | * nospoof PONG. | |
385 | */ | |
386 | if (*(cli_name(cptr)) && cli_user(cptr) && *(cli_user(cptr))->username) { | |
387 | send_reply(cptr, SND_EXPLICIT | ERR_BADPING, | |
388 | ":Your client may not be compatible with this server."); | |
389 | send_reply(cptr, SND_EXPLICIT | ERR_BADPING, | |
390 | ":Compatible clients are available at %s", | |
391 | feature_str(FEAT_URL_CLIENTS)); | |
392 | } | |
393 | exit_client_msg(cptr,cptr,&me, "Registration Timeout"); | |
394 | continue; | |
395 | } else { | |
396 | /* OK, they still have enough time left, so we'll just skip to the | |
397 | * next client. Set the next check to be when their time is up, if | |
398 | * that's before the currently scheduled next check -- hikari */ | |
399 | expire = cli_firsttime(cptr) + max_ping; | |
400 | if (expire < next_check) | |
401 | next_check = expire; | |
402 | continue; | |
403 | } | |
404 | } | |
405 | ||
406 | /* Quit the client after max_ping*2 - they should have answered by now */ | |
407 | if (CurrentTime-cli_lasttime(cptr) >= (max_ping*2) ) | |
408 | { | |
409 | /* If it was a server, then tell ops about it. */ | |
410 | if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) | |
411 | sendto_opmask_butone(0, SNO_OLDSNO, | |
412 | "No response from %s, closing link", | |
413 | cli_name(cptr)); | |
414 | exit_client_msg(cptr, cptr, &me, "Ping timeout"); | |
415 | continue; | |
416 | } | |
417 | ||
418 | if (!IsPingSent(cptr)) | |
419 | { | |
420 | /* If we haven't PINGed the connection and we haven't heard from it in a | |
421 | * while, PING it to make sure it is still alive. | |
422 | */ | |
423 | SetPingSent(cptr); | |
424 | ||
425 | /* If we're late in noticing don't hold it against them :) */ | |
426 | cli_lasttime(cptr) = CurrentTime - max_ping; | |
427 | ||
428 | if (IsUser(cptr)) | |
429 | sendrawto_one(cptr, MSG_PING " :%s", cli_name(&me)); | |
430 | else | |
431 | { | |
432 | char *asll_ts = militime_float(NULL); | |
433 | sendcmdto_one(&me, CMD_PING, cptr, "!%s %s %s", asll_ts, | |
434 | cli_name(cptr), asll_ts); | |
435 | } | |
436 | } | |
437 | ||
438 | expire = cli_lasttime(cptr) + max_ping * 2; | |
439 | if (expire < next_check) | |
440 | next_check=expire; | |
441 | } | |
442 | ||
443 | assert(next_check >= CurrentTime); | |
444 | ||
445 | Debug((DEBUG_DEBUG, "[%i] check_pings() again in %is", | |
446 | CurrentTime, next_check-CurrentTime)); | |
447 | ||
448 | timer_add(&ping_timer, check_pings, 0, TT_ABSOLUTE, next_check); | |
449 | } | |
450 | ||
451 | ||
452 | /** Parse command line arguments. | |
453 | * Global variables are updated to reflect the arguments. | |
454 | * As a side effect, makes sure the process's effective user id is the | |
455 | * same as the real user id. | |
456 | * @param[in] argc Number of arguments on command line. | |
457 | * @param[in,out] argv Command-lne arguments. | |
458 | */ | |
459 | static void parse_command_line(int argc, char** argv) { | |
460 | const char *options = "d:f:h:nktvx:c:"; | |
461 | int opt; | |
462 | ||
463 | if (thisServer.euid != thisServer.uid) | |
464 | setuid(thisServer.uid); | |
465 | ||
466 | /* Do we really need to sanity check the non-NULLness of optarg? That's | |
467 | * getopt()'s job... Removing those... -zs | |
468 | */ | |
469 | while ((opt = getopt(argc, argv, options)) != EOF) | |
470 | switch (opt) { | |
471 | case 'k': thisServer.bootopt |= BOOT_CHKCONF | BOOT_TTY; break; | |
472 | case 'c': dbg_client = optarg; break; | |
473 | case 'n': | |
474 | case 't': thisServer.bootopt |= BOOT_TTY; break; | |
475 | case 'd': dpath = optarg; break; | |
476 | case 'f': configfile = optarg; break; | |
477 | case 'h': ircd_strncpy(cli_name(&me), optarg, HOSTLEN); break; | |
478 | case 'v': | |
479 | printf("ircd %s\n", version); | |
480 | printf("Event engines: "); | |
481 | #ifdef USE_KQUEUE | |
482 | printf("kqueue() "); | |
483 | #endif | |
484 | #ifdef USE_DEVPOLL | |
485 | printf("/dev/poll "); | |
486 | #endif | |
487 | #ifdef USE_EPOLL | |
488 | printf("epoll_*() "); | |
489 | #endif | |
490 | #ifdef USE_POLL | |
491 | printf("poll()"); | |
492 | #else | |
493 | printf("select()"); | |
494 | #endif | |
495 | printf("\nCompiled for a maximum of %d connections.\n", MAXCONNECTIONS); | |
496 | ||
497 | ||
498 | exit(0); | |
499 | break; | |
500 | ||
501 | case 'x': | |
502 | debuglevel = atoi(optarg); | |
503 | if (debuglevel < 0) | |
504 | debuglevel = 0; | |
505 | debugmode = optarg; | |
506 | thisServer.bootopt |= BOOT_DEBUG; | |
507 | break; | |
508 | ||
509 | default: | |
510 | printf("Usage: ircd [-f config] [-h servername] [-x loglevel] [-ntv] [-k [-c clispec]]\n" | |
511 | "\n -f config\t specify explicit configuration file" | |
512 | "\n -x loglevel\t set debug logging verbosity" | |
513 | "\n -n or -t\t don't detach" | |
514 | "\n -v\t\t display version" | |
515 | "\n -k\t\t exit after checking config" | |
516 | "\n -c clispec\t search for client/kill blocks matching client" | |
517 | "\n\t\t clispec is comma-separated list of user@host," | |
518 | "\n\t\t user@ip, $Rrealname, and port number" | |
519 | "\n\nServer not started.\n"); | |
520 | exit(1); | |
521 | } | |
522 | } | |
523 | ||
524 | ||
525 | /** Become a daemon. | |
526 | * @param[in] no_fork If non-zero, do not fork into the background. | |
527 | */ | |
528 | static void daemon_init(int no_fork) { | |
529 | if (no_fork) | |
530 | return; | |
531 | ||
532 | if (fork()) | |
533 | exit(0); | |
534 | ||
535 | #ifdef TIOCNOTTY | |
536 | { | |
537 | int fd; | |
538 | if ((fd = open("/dev/tty", O_RDWR)) > -1) { | |
539 | ioctl(fd, TIOCNOTTY, 0); | |
540 | close(fd); | |
541 | } | |
542 | } | |
543 | #endif | |
544 | ||
545 | setsid(); | |
546 | } | |
547 | ||
548 | /** Check that we have access to a particular file. | |
549 | * If we do not have access to the file, complain on stderr. | |
550 | * @param[in] path File name to check for access. | |
551 | * @param[in] which Configuration character associated with file. | |
552 | * @param[in] mode Bitwise combination of R_OK, W_OK, X_OK and/or F_OK. | |
553 | * @return Non-zero if we have the necessary access, zero if not. | |
554 | */ | |
555 | static char check_file_access(const char *path, char which, int mode) { | |
556 | if (!access(path, mode)) | |
557 | return 1; | |
558 | ||
559 | fprintf(stderr, | |
560 | "Check on %cPATH (%s) failed: %s\n" | |
561 | "Please create this file and/or rerun `configure' " | |
562 | "using --with-%cpath and recompile to correct this.\n", | |
563 | which, path, strerror(errno), which); | |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
568 | ||
569 | /*---------------------------------------------------------------------------- | |
570 | * set_core_limit | |
571 | *--------------------------------------------------------------------------*/ | |
572 | #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE) | |
573 | /** Set the core size soft limit to the same as the hard limit. */ | |
574 | static void set_core_limit(void) { | |
575 | struct rlimit corelim; | |
576 | ||
577 | if (getrlimit(RLIMIT_CORE, &corelim)) { | |
578 | fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno)); | |
579 | corelim.rlim_max = RLIM_INFINITY; /* Try to recover */ | |
580 | } | |
581 | ||
582 | corelim.rlim_cur = corelim.rlim_max; | |
583 | if (setrlimit(RLIMIT_CORE, &corelim)) | |
584 | fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno)); | |
585 | } | |
586 | #endif | |
587 | ||
588 | ||
589 | ||
590 | /** Complain to stderr if any user or group ID belongs to the superuser. | |
591 | * @return Non-zero if all IDs are okay, zero if some are 0. | |
592 | */ | |
593 | static int set_userid_if_needed(void) { | |
594 | if (getuid() == 0 || geteuid() == 0 || | |
595 | getgid() == 0 || getegid() == 0) { | |
596 | fprintf(stderr, "ERROR: This server will not run as superuser.\n"); | |
597 | return 0; | |
598 | } | |
599 | ||
600 | return 1; | |
601 | } | |
602 | ||
603 | ||
604 | /*---------------------------------------------------------------------------- | |
605 | * main - entrypoint | |
606 | * | |
607 | * TODO: This should set the basic environment up and start the main loop. | |
608 | * we're doing waaaaaaaaay too much server initialization here. I hate | |
609 | * long and ugly control paths... -smd | |
610 | *--------------------------------------------------------------------------*/ | |
611 | /** Run the daemon. | |
612 | * @param[in] argc Number of arguments in \a argv. | |
613 | * @param[in] argv Arguments to program execution. | |
614 | */ | |
615 | int main(int argc, char **argv) { | |
616 | CurrentTime = time(NULL); | |
617 | ||
618 | thisServer.argc = argc; | |
619 | thisServer.argv = argv; | |
620 | thisServer.uid = getuid(); | |
621 | thisServer.euid = geteuid(); | |
622 | ||
623 | #ifdef MDEBUG | |
624 | mem_dbg_initialise(); | |
625 | #endif | |
626 | ||
627 | #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE) | |
628 | set_core_limit(); | |
629 | #endif | |
630 | ||
631 | umask(077); /* better safe than sorry --SRB */ | |
632 | memset(&me, 0, sizeof(me)); | |
633 | memset(&me_con, 0, sizeof(me_con)); | |
634 | cli_connect(&me) = &me_con; | |
635 | cli_fd(&me) = -1; | |
636 | ||
637 | parse_command_line(argc, argv); | |
638 | ||
639 | if (chdir(dpath)) { | |
640 | fprintf(stderr, "Fail: Cannot chdir(%s): %s, check DPATH\n", dpath, strerror(errno)); | |
641 | return 2; | |
642 | } | |
643 | ||
644 | if (!set_userid_if_needed()) | |
645 | return 3; | |
646 | ||
647 | /* Check paths for accessibility */ | |
648 | if (!check_file_access(SPATH, 'S', X_OK) || | |
649 | !check_file_access(configfile, 'C', R_OK)) | |
650 | return 4; | |
651 | ||
652 | if (!init_connection_limits()) | |
653 | return 9; | |
654 | ||
655 | close_connections(!(thisServer.bootopt & (BOOT_DEBUG | BOOT_TTY | BOOT_CHKCONF))); | |
656 | ||
657 | /* daemon_init() must be before event_init() because kqueue() FDs | |
658 | * are, perversely, not inherited across fork(). | |
659 | */ | |
660 | daemon_init(thisServer.bootopt & BOOT_TTY); | |
661 | ||
662 | #ifdef DEBUGMODE | |
663 | /* Must reserve fd 2... */ | |
664 | if (debuglevel >= 0 && !(thisServer.bootopt & BOOT_TTY)) { | |
665 | int fd; | |
666 | if ((fd = open("/dev/null", O_WRONLY)) < 0) { | |
667 | fprintf(stderr, "Unable to open /dev/null (to reserve fd 2): %s\n", | |
668 | strerror(errno)); | |
669 | return 8; | |
670 | } | |
671 | if (fd != 2 && dup2(fd, 2) < 0) { | |
672 | fprintf(stderr, "Unable to reserve fd 2; dup2 said: %s\n", | |
673 | strerror(errno)); | |
674 | return 8; | |
675 | } | |
676 | } | |
677 | #endif | |
678 | ||
679 | event_init(MAXCONNECTIONS); | |
680 | ||
681 | setup_signals(); | |
682 | feature_init(); /* initialize features... */ | |
683 | log_init(*argv); | |
684 | set_nomem_handler(outofmemory); | |
685 | ||
686 | initload(); | |
687 | init_list(); | |
688 | init_hash(); | |
689 | init_class(); | |
690 | initwhowas(); | |
691 | initmsgtree(); | |
692 | initstats(); | |
693 | ||
694 | /* we need this for now, when we're modular this | |
695 | should be removed -- hikari */ | |
696 | ircd_crypt_init(); | |
697 | ||
698 | motd_init(); | |
699 | ||
700 | if (!init_conf()) { | |
701 | log_write(LS_SYSTEM, L_CRIT, 0, "Failed to read configuration file %s", | |
702 | configfile); | |
703 | return 7; | |
704 | } | |
705 | ||
706 | if (thisServer.bootopt & BOOT_CHKCONF) { | |
707 | if (dbg_client) | |
708 | conf_debug_iline(dbg_client); | |
709 | fprintf(stderr, "Configuration file %s checked okay.\n", configfile); | |
710 | return 0; | |
711 | } | |
712 | ||
713 | debug_init(thisServer.bootopt & BOOT_TTY); | |
714 | if (check_pid()) { | |
715 | Debug((DEBUG_FATAL, "Failed to acquire PID file lock after fork")); | |
716 | exit(2); | |
717 | } | |
718 | ||
719 | init_server_identity(); | |
720 | ||
721 | uping_init(); | |
722 | ||
723 | stats_init(); | |
724 | ||
725 | IPcheck_init(); | |
726 | timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1); | |
727 | timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1); | |
728 | timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60); | |
729 | ||
730 | CurrentTime = time(NULL); | |
731 | ||
732 | SetMe(&me); | |
733 | cli_magic(&me) = CLIENT_MAGIC; | |
734 | cli_from(&me) = &me; | |
735 | make_server(&me); | |
736 | ||
737 | cli_serv(&me)->timestamp = TStime(); /* Abuse own link timestamp as start TS */ | |
738 | cli_serv(&me)->prot = atoi(MAJOR_PROTOCOL); | |
739 | cli_serv(&me)->up = &me; | |
740 | cli_serv(&me)->down = NULL; | |
741 | cli_handler(&me) = SERVER_HANDLER; | |
742 | ||
743 | SetYXXCapacity(&me, MAXCLIENTS); | |
744 | ||
745 | cli_lasttime(&me) = cli_since(&me) = cli_firsttime(&me) = CurrentTime; | |
746 | ||
747 | hAddClient(&me); | |
748 | ||
749 | write_pidfile(); | |
750 | init_counters(); | |
751 | ||
752 | Debug((DEBUG_NOTICE, "Server ready...")); | |
753 | log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready"); | |
754 | ||
755 | event_loop(); | |
756 | ||
757 | return 0; | |
758 | } | |
759 | ||
760 |