diff options
Diffstat (limited to 'src/ircd.c')
-rw-r--r-- | src/ircd.c | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/src/ircd.c b/src/ircd.c new file mode 100644 index 0000000..ee02bcd --- /dev/null +++ b/src/ircd.c @@ -0,0 +1,658 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * ircd.c: Starts up and runs the ircd. + * + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * $Id$ + */ + +#include "stdinc.h" +#include "s_user.h" +#include "list.h" +#include "ircd.h" +#include "channel.h" +#include "channel_mode.h" +#include "client.h" +#include "event.h" +#include "fdlist.h" +#include "hash.h" +#include "irc_string.h" +#include "ircd_signal.h" +#include "s_gline.h" +#include "motd.h" +#include "hostmask.h" +#include "numeric.h" +#include "packet.h" +#include "parse.h" +#include "irc_res.h" +#include "restart.h" +#include "rng_mt.h" +#include "s_auth.h" +#include "s_bsd.h" +#include "conf.h" +#include "log.h" +#include "s_misc.h" +#include "s_serv.h" /* try_connections */ +#include "send.h" +#include "whowas.h" +#include "modules.h" +#include "memory.h" +#include "hook.h" +#include "ircd_getopt.h" +#include "balloc.h" +#include "motd.h" +#include "supported.h" +#include "watch.h" + + +/* /quote set variables */ +struct SetOptions GlobalSetOptions; + +/* configuration set from ircd.conf */ +struct config_file_entry ConfigFileEntry; +/* server info set from ircd.conf */ +struct server_info ServerInfo; +/* admin info set from ircd.conf */ +struct admin_info AdminInfo = { NULL, NULL, NULL }; +struct Counter Count = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +struct ServerState_t server_state = { 0 }; +struct logging_entry ConfigLoggingEntry = { .use_logging = 1 }; +struct ServerStatistics ServerStats; +struct timeval SystemTime; +struct Client me; /* That's me */ +struct LocalUser meLocalUser; /* That's also part of me */ + +const char *logFileName = LPATH; +const char *pidFileName = PPATH; + +char **myargv; + +int dorehash = 0; +int doremotd = 0; + +/* Set to zero because it should be initialized later using + * initialize_server_capabs + */ +int default_server_capabs = 0; +unsigned int splitmode; +unsigned int splitchecking; +unsigned int split_users; +unsigned int split_servers; + +/* Do klines the same way hybrid-6 did them, i.e. at the + * top of the next io_loop instead of in the same loop as + * the klines are being applied. + * + * This should fix strange CPU starvation as very indirectly reported. + * (Why do you people not email bug reports? WHY? WHY?) + * + * - Dianora + */ + +int rehashed_klines = 0; + + +/* + * print_startup - print startup information + */ +static void +print_startup(int pid) +{ + printf("ircd: version %s\n", ircd_version); + printf("ircd: pid %d\n", pid); + printf("ircd: running in %s mode from %s\n", !server_state.foreground ? "background" + : "foreground", ConfigFileEntry.dpath); +} + +static void +make_daemon(void) +{ + int pid; + + if ((pid = fork()) < 0) + { + perror("fork"); + exit(EXIT_FAILURE); + } + else if (pid > 0) + { + print_startup(pid); + exit(EXIT_SUCCESS); + } + + setsid(); +} + +static int printVersion = 0; + +static struct lgetopt myopts[] = { + {"dlinefile", &ConfigFileEntry.dlinefile, + STRING, "File to use for dline.conf"}, + {"configfile", &ConfigFileEntry.configfile, + STRING, "File to use for ircd.conf"}, + {"klinefile", &ConfigFileEntry.klinefile, + STRING, "File to use for kline.conf"}, + {"xlinefile", &ConfigFileEntry.xlinefile, + STRING, "File to use for xline.conf"}, + {"logfile", &logFileName, + STRING, "File to use for ircd.log"}, + {"pidfile", &pidFileName, + STRING, "File to use for process ID"}, + {"foreground", &server_state.foreground, + YESNO, "Run in foreground (don't detach)"}, + {"version", &printVersion, + YESNO, "Print version and exit"}, + {"help", NULL, USAGE, "Print this text"}, + {NULL, NULL, STRING, NULL}, +}; + +void +set_time(void) +{ + static char to_send[200]; + struct timeval newtime; + newtime.tv_sec = 0; + newtime.tv_usec = 0; + + if (gettimeofday(&newtime, NULL) == -1) + { + ilog(LOG_TYPE_IRCD, "Clock Failure (%s), TS can be corrupted", + strerror(errno)); + sendto_realops_flags(UMODE_ALL, L_ALL, + "Clock Failure (%s), TS can be corrupted", + strerror(errno)); + restart("Clock Failure"); + } + + if (newtime.tv_sec < CurrentTime) + { + snprintf(to_send, sizeof(to_send), + "System clock is running backwards - (%lu < %lu)", + (unsigned long)newtime.tv_sec, (unsigned long)CurrentTime); + report_error(L_ALL, to_send, me.name, 0); + set_back_events(CurrentTime - newtime.tv_sec); + } + + SystemTime.tv_sec = newtime.tv_sec; + SystemTime.tv_usec = newtime.tv_usec; +} + +static void +io_loop(void) +{ + while (1 == 1) + { + /* + * Maybe we want a flags word? + * ie. if (REHASHED_KLINES(global_flags)) + * SET_REHASHED_KLINES(global_flags) + * CLEAR_REHASHED_KLINES(global_flags) + * + * - Dianora + */ + if (rehashed_klines) + { + check_conf_klines(); + rehashed_klines = 0; + } + + if (listing_client_list.head) + { + dlink_node *ptr = NULL, *ptr_next = NULL; + DLINK_FOREACH_SAFE(ptr, ptr_next, listing_client_list.head) + { + struct Client *client_p = ptr->data; + assert(client_p->localClient->list_task); + safe_list_channels(client_p, client_p->localClient->list_task, 0); + } + } + + /* Run pending events, then get the number of seconds to the next + * event + */ + while (eventNextTime() <= CurrentTime) + eventRun(); + + comm_select(); + exit_aborted_clients(); + free_exited_clients(); + send_queued_all(); + + /* Check to see whether we have to rehash the configuration .. */ + if (dorehash) + { + rehash(1); + dorehash = 0; + } + if (doremotd) + { + read_message_file(&ConfigFileEntry.motd); + sendto_realops_flags(UMODE_ALL, L_ALL, + "Got signal SIGUSR1, reloading ircd motd file"); + doremotd = 0; + } + } +} + +/* initalialize_global_set_options() + * + * inputs - none + * output - none + * side effects - This sets all global set options needed + */ +static void +initialize_global_set_options(void) +{ + memset(&GlobalSetOptions, 0, sizeof(GlobalSetOptions)); + + GlobalSetOptions.autoconn = 1; + GlobalSetOptions.spam_time = MIN_JOIN_LEAVE_TIME; + GlobalSetOptions.spam_num = MAX_JOIN_LEAVE_COUNT; + + if (ConfigFileEntry.default_floodcount) + GlobalSetOptions.floodcount = ConfigFileEntry.default_floodcount; + else + GlobalSetOptions.floodcount = 10; + + /* XXX I have no idea what to try here - Dianora */ + GlobalSetOptions.joinfloodcount = 16; + GlobalSetOptions.joinfloodtime = 8; + + split_servers = ConfigChannel.default_split_server_count; + split_users = ConfigChannel.default_split_user_count; + + if (split_users && split_servers && (ConfigChannel.no_create_on_split || + ConfigChannel.no_join_on_split)) + { + splitmode = 1; + splitchecking = 1; + } + + GlobalSetOptions.ident_timeout = IDENT_TIMEOUT; + /* End of global set options */ +} + +/* initialize_message_files() + * + * inputs - none + * output - none + * side effects - Set up all message files needed, motd etc. + */ +static void +initialize_message_files(void) +{ + init_message_file(USER_MOTD, MPATH, &ConfigFileEntry.motd); + init_message_file(USER_LINKS, LIPATH, &ConfigFileEntry.linksfile); + + read_message_file(&ConfigFileEntry.motd); + read_message_file(&ConfigFileEntry.linksfile); + + init_isupport(); +} + +/* initialize_server_capabs() + * + * inputs - none + * output - none + */ +static void +initialize_server_capabs(void) +{ + add_capability("QS", CAP_QS, 1); + add_capability("EOB", CAP_EOB, 1); + add_capability("TS6", CAP_TS6, 0); + add_capability("CLUSTER", CAP_CLUSTER, 1); + add_capability("SVS", CAP_SVS, 1); +#ifdef HALFOPS + add_capability("HOPS", CAP_HOPS, 1); +#endif +} + +/* write_pidfile() + * + * inputs - filename+path of pid file + * output - NONE + * side effects - write the pid of the ircd to filename + */ +static void +write_pidfile(const char *filename) +{ + FILE *fb; + + if ((fb = fopen(filename, "w"))) + { + char buff[32]; + unsigned int pid = (unsigned int)getpid(); + + snprintf(buff, sizeof(buff), "%u\n", pid); + + if ((fputs(buff, fb) == -1)) + ilog(LOG_TYPE_IRCD, "Error writing %u to pid file %s (%s)", + pid, filename, strerror(errno)); + + fclose(fb); + } + else + { + ilog(LOG_TYPE_IRCD, "Error opening pid file %s", filename); + } +} + +/* check_pidfile() + * + * inputs - filename+path of pid file + * output - none + * side effects - reads pid from pidfile and checks if ircd is in process + * list. if it is, gracefully exits + * -kre + */ +static void +check_pidfile(const char *filename) +{ + FILE *fb; + char buff[32]; + pid_t pidfromfile; + + /* Don't do logging here, since we don't have log() initialised */ + if ((fb = fopen(filename, "r"))) + { + if (fgets(buff, 20, fb) == NULL) + { + /* log(L_ERROR, "Error reading from pid file %s (%s)", filename, + * strerror(errno)); + */ + } + else + { + pidfromfile = atoi(buff); + + if (!kill(pidfromfile, 0)) + { + /* log(L_ERROR, "Server is already running"); */ + printf("ircd: daemon is already running\n"); + exit(-1); + } + } + + fclose(fb); + } + else if (errno != ENOENT) + { + /* log(L_ERROR, "Error opening pid file %s", filename); */ + } +} + +/* setup_corefile() + * + * inputs - nothing + * output - nothing + * side effects - setups corefile to system limits. + * -kre + */ +static void +setup_corefile(void) +{ +#ifdef HAVE_SYS_RESOURCE_H + struct rlimit rlim; /* resource limits */ + + /* Set corefilesize to maximum */ + if (!getrlimit(RLIMIT_CORE, &rlim)) + { + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_CORE, &rlim); + } +#endif +} + +/* init_ssl() + * + * inputs - nothing + * output - nothing + * side effects - setups SSL context. + */ +static void +init_ssl(void) +{ +#ifdef HAVE_LIBCRYPTO + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + if ((ServerInfo.server_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) + { + const char *s; + + fprintf(stderr, "ERROR: Could not initialize the SSL Server context -- %s\n", + s = ERR_lib_error_string(ERR_get_error())); + ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize the SSL Server context -- %s\n", s); + } + + SSL_CTX_set_options(ServerInfo.server_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1); + SSL_CTX_set_options(ServerInfo.server_ctx, SSL_OP_TLS_ROLLBACK_BUG|SSL_OP_ALL); + SSL_CTX_set_verify(ServerInfo.server_ctx, SSL_VERIFY_NONE, NULL); + + if ((ServerInfo.client_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) + { + const char *s; + + fprintf(stderr, "ERROR: Could not initialize the SSL Client context -- %s\n", + s = ERR_lib_error_string(ERR_get_error())); + ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize the SSL Client context -- %s\n", s); + } + + SSL_CTX_set_options(ServerInfo.client_ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1); + SSL_CTX_set_options(ServerInfo.client_ctx, SSL_OP_TLS_ROLLBACK_BUG|SSL_OP_ALL); + SSL_CTX_set_verify(ServerInfo.client_ctx, SSL_VERIFY_NONE, NULL); +#endif /* HAVE_LIBCRYPTO */ +} + +/* init_callbacks() + * + * inputs - nothing + * output - nothing + * side effects - setups standard hook points + */ +static void +init_callbacks(void) +{ + iorecv_cb = register_callback("iorecv", iorecv_default); + iosend_cb = register_callback("iosend", iosend_default); +} + +int +main(int argc, char *argv[]) +{ + /* Check to see if the user is running + * us as root, which is a nono + */ + if (geteuid() == 0) + { + fprintf(stderr, "Don't run ircd as root!!!\n"); + return -1; + } + + /* Setup corefile size immediately after boot -kre */ + setup_corefile(); + + /* save server boot time right away, so getrusage works correctly */ + set_time(); + + /* It ain't random, but it ought to be a little harder to guess */ + init_genrand(SystemTime.tv_sec ^ (SystemTime.tv_usec | (getpid() << 20))); + + me.localClient = &meLocalUser; + dlinkAdd(&me, &me.node, &global_client_list); /* Pointer to beginning + of Client list */ + /* Initialise the channel capability usage counts... */ + init_chcap_usage_counts(); + + ConfigFileEntry.dpath = DPATH; + ConfigFileEntry.configfile = CPATH; /* Server configuration file */ + ConfigFileEntry.klinefile = KPATH; /* Server kline file */ + ConfigFileEntry.xlinefile = XPATH; /* Server xline file */ + ConfigFileEntry.dlinefile = DLPATH; /* dline file */ + ConfigFileEntry.cresvfile = CRESVPATH; /* channel resv file */ + ConfigFileEntry.nresvfile = NRESVPATH; /* nick resv file */ + myargv = argv; + umask(077); /* better safe than sorry --SRB */ + + parseargs(&argc, &argv, myopts); + + if (printVersion) + { + printf("ircd: version %s\n", ircd_version); + exit(EXIT_SUCCESS); + } + + if (chdir(ConfigFileEntry.dpath)) + { + perror("chdir"); + exit(EXIT_FAILURE); + } + + init_ssl(); + + if (!server_state.foreground) + { + make_daemon(); + close_standard_fds(); /* this needs to be before init_netio()! */ + } + else + print_startup(getpid()); + + setup_signals(); + + /* Init the event subsystem */ + eventInit(); + /* We need this to initialise the fd array before anything else */ + fdlist_init(); + log_add_file(LOG_TYPE_IRCD, 0, logFileName); + check_can_use_v6(); + init_comm(); /* This needs to be setup early ! -- adrian */ + /* Check if there is pidfile and daemon already running */ + check_pidfile(pidFileName); + + initBlockHeap(); + init_dlink_nodes(); + init_callbacks(); + initialize_message_files(); + dbuf_init(); + init_hash(); + init_ip_hash_table(); /* client host ip hash table */ + init_host_hash(); /* Host-hashtable. */ + init_client(); + init_class(); + whowas_init(); + watch_init(); + init_auth(); /* Initialise the auth code */ + init_resolver(); /* Needs to be setup before the io loop */ + modules_init(); + read_conf_files(1); /* cold start init conf files */ + init_uid(); + initialize_server_capabs(); /* Set up default_server_capabs */ + initialize_global_set_options(); + init_channels(); + + if (EmptyString(ServerInfo.sid)) + { + ilog(LOG_TYPE_IRCD, "ERROR: No server id specified in serverinfo block."); + exit(EXIT_FAILURE); + } + + strlcpy(me.id, ServerInfo.sid, sizeof(me.id)); + + if (EmptyString(ServerInfo.name)) + { + ilog(LOG_TYPE_IRCD, "ERROR: No server name specified in serverinfo block."); + exit(EXIT_FAILURE); + } + + strlcpy(me.name, ServerInfo.name, sizeof(me.name)); + + /* serverinfo{} description must exist. If not, error out.*/ + if (EmptyString(ServerInfo.description)) + { + ilog(LOG_TYPE_IRCD, "ERROR: No server description specified in serverinfo block."); + exit(EXIT_FAILURE); + } + + strlcpy(me.info, ServerInfo.description, sizeof(me.info)); + + me.from = &me; + me.servptr = &me; + me.localClient->lasttime = CurrentTime; + me.localClient->since = CurrentTime; + me.localClient->firsttime = CurrentTime; + + SetMe(&me); + make_server(&me); + + hash_add_id(&me); + hash_add_client(&me); + + /* add ourselves to global_serv_list */ + dlinkAdd(&me, make_dlink_node(), &global_serv_list); + + if (chdir(MODPATH)) + { + ilog(LOG_TYPE_IRCD, "Could not load core modules. Terminating!"); + exit(EXIT_FAILURE); + } + + load_all_modules(1); + load_conf_modules(); + load_core_modules(1); + + /* Go back to DPATH after checking to see if we can chdir to MODPATH */ + if (chdir(ConfigFileEntry.dpath)) + { + perror("chdir"); + exit(EXIT_FAILURE); + } + + /* + * assemble_umode_buffer() has to be called after + * reading conf/loading modules. + */ + assemble_umode_buffer(); + + write_pidfile(pidFileName); + + ilog(LOG_TYPE_IRCD, "Server Ready"); + + eventAddIsh("cleanup_glines", cleanup_glines, NULL, CLEANUP_GLINES_TIME); + eventAddIsh("cleanup_tklines", cleanup_tklines, NULL, CLEANUP_TKLINES_TIME); + + /* We want try_connections to be called as soon as possible now! -- adrian */ + /* No, 'cause after a restart it would cause all sorts of nick collides */ + eventAddIsh("try_connections", try_connections, NULL, STARTUP_CONNECTIONS_TIME); + + /* Setup the timeout check. I'll shift it later :) -- adrian */ + eventAddIsh("comm_checktimeouts", comm_checktimeouts, NULL, 1); + + if (ConfigServerHide.links_delay > 0) + eventAddIsh("write_links_file", write_links_file, NULL, ConfigServerHide.links_delay); + else + ConfigServerHide.links_disabled = 1; + + if (splitmode) + eventAddIsh("check_splitmode", check_splitmode, NULL, 60); + + io_loop(); + return 0; +} |