diff --git a/Makefile.in b/Makefile.in index a1f644cf7..df3b20d80 100644 --- a/Makefile.in +++ b/Makefile.in @@ -179,6 +179,7 @@ depend: install: all $(INSTALL) -m 0700 -d $(DESTDIR)@BINDIR@ $(INSTALL) -m 0700 src/ircd $(DESTDIR)@BINDIR@/unrealircd + $(INSTALL) -m 0700 src/unrealircdctl $(DESTDIR)@BINDIR@/unrealircdctl $(INSTALL) -m 0700 extras/unrealircd-upgrade-script $(DESTDIR)@BINDIR@/unrealircd-upgrade-script $(INSTALL) -m 0700 -d $(DESTDIR)@DOCDIR@ $(INSTALL) -m 0600 doc/Authors doc/coding-guidelines doc/tao.of.irc doc/KEYS doc/RELEASE-NOTES.md $(DESTDIR)@DOCDIR@ diff --git a/configure b/configure index 7c98be2b2..52e63d4fe 100755 --- a/configure +++ b/configure @@ -659,6 +659,7 @@ PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG LDFLAGS_PRIVATELIBS +CONTROLFILE PIDFILE DOCDIR PERMDATADIR @@ -752,6 +753,7 @@ with_tmpdir with_datadir with_docdir with_pidfile +with_controlfile with_privatelibdir with_maxconnections with_no_operoverride @@ -1460,6 +1462,7 @@ Optional Packages: --with-datadir=path Specify the directory where permanent data is stored --with-docdir=path Specify the directory where documentation is stored --with-pidfile=path Specify the path of the pid file + --with-controlfile=path Specify the path of the control socket --with-privatelibdir=path Specify the directory where private libraries are stored. Disable when building a package for a distro @@ -6548,6 +6551,25 @@ fi +# Check whether --with-controlfile was given. +if test "${with_controlfile+set}" = set; then : + withval=$with_controlfile; +cat >>confdefs.h <<_ACEOF +#define CONTROLFILE "$withval" +_ACEOF + + CONTROLFILE="$withval" +else + +cat >>confdefs.h <<_ACEOF +#define CONTROLFILE "$HOME/unrealircd/data/unrealircd.ctl" +_ACEOF + + CONTROLFILE="$HOME/unrealircd/data/unrealircd.ctl" +fi + + + # Check whether --with-privatelibdir was given. if test "${with_privatelibdir+set}" = set; then : withval=$with_privatelibdir; @@ -6589,6 +6611,7 @@ fi + # Check whether --with-maxconnections was given. if test "${with_maxconnections+set}" = set; then : withval=$with_maxconnections; ac_fd=$withval diff --git a/configure.ac b/configure.ac index 7cbfc82d2..491ccc9bc 100644 --- a/configure.ac +++ b/configure.ac @@ -484,6 +484,12 @@ AC_ARG_WITH(pidfile, [AS_HELP_STRING([--with-pidfile=path],[Specify the path of [AC_DEFINE_UNQUOTED([PIDFILE], ["$HOME/unrealircd/data/unrealircd.pid"], [Define the path of the pid file]) PIDFILE="$HOME/unrealircd/data/unrealircd.pid"]) +AC_ARG_WITH(controlfile, [AS_HELP_STRING([--with-controlfile=path],[Specify the path of the control socket])], + [AC_DEFINE_UNQUOTED([CONTROLFILE], ["$withval"], [Define the path of the control socket]) + CONTROLFILE="$withval"], + [AC_DEFINE_UNQUOTED([CONTROLFILE], ["$HOME/unrealircd/data/unrealircd.ctl"], [Define the path of the control socket]) + CONTROLFILE="$HOME/unrealircd/data/unrealircd.ctl"]) + dnl Ensure that this “feature” can be disabled as it makes it harder to package unrealircd. dnl Users have always been able to specify “./configure LDFLAGS=-Wl,-rpath,/path/to/blah”—binki AC_ARG_WITH(privatelibdir, [AS_HELP_STRING([--with-privatelibdir=path],[Specify the directory where private libraries are stored. Disable when building a package for a distro])], @@ -514,6 +520,7 @@ dnl well, Because DATADIR conflicts with the Windows SDK header files.. amazing. AC_SUBST(PERMDATADIR) AC_SUBST(DOCDIR) AC_SUBST(PIDFILE) +AC_SUBST(CONTROLFILE) AC_SUBST(LDFLAGS_PRIVATELIBS) AC_ARG_WITH(maxconnections, [AS_HELP_STRING([--with-maxconnections=size], [Specify the max file descriptors to use])], diff --git a/include/h.h b/include/h.h index ec0301a09..9c744cdf1 100644 --- a/include/h.h +++ b/include/h.h @@ -28,6 +28,12 @@ #include "setup.h" #include "fdlist.h" +extern int dorehash, dorestart, doreloadcert; +#ifndef _WIN32 +extern char **myargv; +#else +extern LPCSTR cmdLine; +#endif extern MODVAR char *extraflags; extern MODVAR int tainted; extern MODVAR Member *freemember; @@ -153,6 +159,7 @@ extern MODVAR struct list_head lclient_list; extern MODVAR struct list_head server_list; extern MODVAR struct list_head oper_list; extern MODVAR struct list_head unknown_list; +extern MODVAR struct list_head control_list; extern MODVAR struct list_head global_server_list; extern MODVAR struct list_head dead_list; extern RealCommand *find_command(const char *cmd, int flags); @@ -930,7 +937,7 @@ extern Cmode_t get_extmode_bitbychar(char m); extern long find_user_mode(char mode); extern void start_listeners(void); extern void buildvarstring(const char *inbuf, char *outbuf, size_t len, const char *name[], const char *value[]); -extern void reinit_tls(void); +extern int reinit_tls(void); extern CMD_FUNC(cmd_error); extern CMD_FUNC(cmd_dns); extern CMD_FUNC(cmd_info); @@ -1136,6 +1143,8 @@ extern void flood_limit_exceeded_log(Client *client, const char *floodname); /* logging */ extern int config_test_log(ConfigFile *conf, ConfigEntry *ce); extern int config_run_log(ConfigFile *conf, ConfigEntry *ce); +extern const char *log_level_terminal_color(LogLevel loglevel); +#define TERMINAL_COLOR_RESET "\033[0m" extern LogType log_type_stringtoval(const char *str); extern const char *log_type_valtostring(LogType v); #ifdef DEBUGMODE @@ -1209,3 +1218,8 @@ extern void make_umodestr(void); extern void initwhowas(void); extern void uid_init(void); extern const char *uid_get(void); +/* proc i/o */ +extern void add_proc_io_server(void); +extern void procio_post_rehash(int failure); +/* end of proc i/o */ +extern int minimum_msec_since_last_run(struct timeval *tv_old, long minimum); diff --git a/include/setup.h.in b/include/setup.h.in index 18f69081d..16b097c34 100644 --- a/include/setup.h.in +++ b/include/setup.h.in @@ -12,6 +12,9 @@ /* Define the location of the configuration files */ #undef CONFDIR +/* Define the path of the control socket */ +#undef CONTROLFILE + /* Define the location of permanent data files */ #undef DATADIR diff --git a/include/struct.h b/include/struct.h index 5b55df9bf..43f2b7e11 100644 --- a/include/struct.h +++ b/include/struct.h @@ -323,6 +323,7 @@ typedef enum LogDestination { LOG_DEST_SNOMASK=0, LOG_DEST_OPER=1, LOG_DEST_REMO * @{ */ typedef enum ClientStatus { + CLIENT_STATUS_CONTROL = -8, /**< Client is on the control channel */ CLIENT_STATUS_LOG = -7, /**< Client is a log file */ CLIENT_STATUS_TLS_STARTTLS_HANDSHAKE = -8, /**< Client is doing a STARTTLS handshake */ CLIENT_STATUS_CONNECTING = -6, /**< Client is an outgoing connect */ @@ -345,6 +346,7 @@ typedef enum ClientStatus { /** Client is not fully registered yet. May become a user or a server, we don't know yet. */ #define IsUnknown(x) (((x)->status == CLIENT_STATUS_UNKNOWN) || ((x)->status == CLIENT_STATUS_TLS_STARTTLS_HANDSHAKE)) #define IsServer(x) ((x)->status == CLIENT_STATUS_SERVER) /**< Is a server that has completed the connection handshake */ +#define IsControl(x) ((x)->status == CLIENT_STATUS_CONTROL) /**< Is on the control channel (not on IRC) */ #define IsLog(x) ((x)->status == CLIENT_STATUS_LOG) /**< Is a log file, not a user or server */ #define IsStartTLSHandshake(x) ((x)->status == CLIENT_STATUS_TLS_STARTTLS_HANDSHAKE) /**< Currently doing a STARTTLS handshake */ #define IsTLSAcceptHandshake(x) ((x)->status == CLIENT_STATUS_TLS_ACCEPT_HANDSHAKE) /**< Currently doing a TLS handshake - incoming */ @@ -361,6 +363,8 @@ typedef enum ClientStatus { #define SetServer(x) ((x)->status = CLIENT_STATUS_SERVER) #define SetUser(x) ((x)->status = CLIENT_STATUS_USER) #define SetLog(x) ((x)->status = CLIENT_STATUS_LOG) +#define SetControl(x) ((x)->status = CLIENT_STATUS_CONTROL) +#define SetUser(x) ((x)->status = CLIENT_STATUS_USER) /** @} */ @@ -372,6 +376,7 @@ typedef enum ClientStatus { #define CLIENT_FLAG_DEAD 0x00000002 /**< Client is dead: already quit/exited and removed from all lists -- Remaining part will soon be freed in main loop */ #define CLIENT_FLAG_DEADSOCKET 0x00000004 /**< Local socket is dead but otherwise the client still exists fully -- Will soon exit in main loop */ #define CLIENT_FLAG_KILLED 0x00000008 /**< Prevents "QUIT" from being sent for this */ +#define CLIENT_FLAG_MONITOR_REHASH 0x00000010 /**< Client is monitoring rehash output */ #define CLIENT_FLAG_OUTGOING 0x00000020 /**< Outgoing connection (do not touch cptr->listener->clients) */ #define CLIENT_FLAG_CLOSING 0x00000040 /**< Set when closing to suppress errors */ #define CLIENT_FLAG_LISTEN 0x00000080 /**< Used to mark clients which we listen() on */ @@ -470,6 +475,7 @@ typedef enum ClientStatus { #define IsEAuth(x) ((x)->flags & CLIENT_FLAG_EAUTH) #define IsIdentSuccess(x) ((x)->flags & CLIENT_FLAG_IDENTSUCCESS) #define IsKilled(x) ((x)->flags & CLIENT_FLAG_KILLED) +#define IsMonitorRehash(x) ((x)->flags & CLIENT_FLAG_MONITOR_REHASH) #define IsListening(x) ((x)->flags & CLIENT_FLAG_LISTEN) #define IsLocalhost(x) ((x)->flags & CLIENT_FLAG_LOCALHOST) #define IsMap(x) ((x)->flags & CLIENT_FLAG_MAP) @@ -501,6 +507,7 @@ typedef enum ClientStatus { #define SetEAuth(x) do { (x)->flags |= CLIENT_FLAG_EAUTH; } while(0) #define SetIdentSuccess(x) do { (x)->flags |= CLIENT_FLAG_IDENTSUCCESS; } while(0) #define SetKilled(x) do { (x)->flags |= CLIENT_FLAG_KILLED; } while(0) +#define SetMonitorRehash(x) do { (x)->flags |= CLIENT_FLAG_MONITOR_REHASH; } while(0) #define SetListening(x) do { (x)->flags |= CLIENT_FLAG_LISTEN; } while(0) #define SetLocalhost(x) do { (x)->flags |= CLIENT_FLAG_LOCALHOST; } while(0) #define SetMap(x) do { (x)->flags |= CLIENT_FLAG_MAP; } while(0) @@ -530,6 +537,7 @@ typedef enum ClientStatus { #define ClearEAuth(x) do { (x)->flags &= ~CLIENT_FLAG_EAUTH; } while(0) #define ClearIdentSuccess(x) do { (x)->flags &= ~CLIENT_FLAG_IDENTSUCCESS; } while(0) #define ClearKilled(x) do { (x)->flags &= ~CLIENT_FLAG_KILLED; } while(0) +#define ClearMonitorRehash(x) do { (x)->flags &= ~CLIENT_FLAG_MONITOR_REHASH; } while(0) #define ClearListening(x) do { (x)->flags &= ~CLIENT_FLAG_LISTEN; } while(0) #define ClearLocalhost(x) do { (x)->flags &= ~CLIENT_FLAG_LOCALHOST; } while(0) #define ClearMap(x) do { (x)->flags &= ~CLIENT_FLAG_MAP; } while(0) @@ -784,11 +792,11 @@ struct LoopStruct { unsigned do_bancheck : 1; /* perform *line bancheck? */ unsigned do_bancheck_spamf_user : 1; /* perform 'user' spamfilter bancheck */ unsigned do_bancheck_spamf_away : 1; /* perform 'away' spamfilter bancheck */ - unsigned rehashing : 1; unsigned terminating : 1; unsigned config_load_failed : 1; unsigned rehash_download_busy : 1; /* don't return "all downloads complete", needed for race condition */ unsigned tainted : 1; + int rehashing; Client *rehash_save_client; void (*boot_function)(); }; @@ -856,6 +864,8 @@ struct SWhois { #define CMD_VIRUS 0x0080 /** Command requires IRCOp privileges */ #define CMD_OPER 0x0200 +/** Command is for control channel only (unrealircd.ctl socket) */ +#define CMD_CONTROL 0x0400 /** Command function - used by all command handlers. * This is used in the code like
CMD_FUNC(cmd_yourcmd)as a function definition. @@ -1217,6 +1227,7 @@ extern void unload_all_unused_moddata(void); #define LISTENER_TLS 0x000010 #define LISTENER_BOUND 0x000020 #define LISTENER_DEFER_ACCEPT 0x000040 +#define LISTENER_CONTROL 0x000080 /**< Control channel */ #define IsServersOnlyListener(x) ((x) && ((x)->options & LISTENER_SERVERSONLY)) diff --git a/src/Makefile.in b/src/Makefile.in index 62d43881d..589556100 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -21,10 +21,10 @@ CC = "==== DO NOT RUN MAKE FROM THIS DIRECTORY ====" -OBJS=dns.o auth.o channel.o crule.o dbuf.o \ - fdlist.o hash.o ircd.o ircsprintf.o list.o \ +OBJS=ircd_vars.o dns.o auth.o channel.o crule.o dbuf.o \ + fdlist.o hash.o ircsprintf.o list.o \ match.o modules.o parse.o mempool.o operclass.o \ - conf_preprocessor.o conf.o debug.o dispatch.o \ + conf_preprocessor.o conf.o proc_io_server.o debug.o dispatch.o \ misc.o serv.o aliases.o socket.o \ tls.o user.o scache.o send.o support.o \ version.o whowas.o random.o api-usermode.o api-channelmode.o \ @@ -62,22 +62,22 @@ all: build build: # Force build of 'ircd', before we start building any modules: - $(MAKE) ircd + $(MAKE) ircd unrealircdctl $(MAKE) mods custommodule: +cd modules/third; $(MAKE) MODULEFILE=$(MODULEFILE) 'EXLIBS=$(EXLIBS)' custommodule -ircd: $(OBJS) - $(CC) $(CFLAGS) $(BINCFLAGS) $(CRYPTOLIB) -o ircd $(OBJS) $(LDFLAGS) $(BINLDFLAGS) $(IRCDLIBS) $(CRYPTOLIB) +ircd: $(OBJS) ircd.o + $(CC) $(CFLAGS) $(BINCFLAGS) $(CRYPTOLIB) -o ircd ircd.o $(OBJS) $(LDFLAGS) $(BINLDFLAGS) $(IRCDLIBS) $(CRYPTOLIB) + +unrealircdctl: $(OBJS) unrealircdctl.o proc_io_client.o + $(CC) $(CFLAGS) $(BINCFLAGS) $(CRYPTOLIB) -o unrealircdctl unrealircdctl.o proc_io_client.o $(OBJS) $(LDFLAGS) $(BINLDFLAGS) $(IRCDLIBS) $(CRYPTOLIB) mods: @if [ ! -r include ] ; then \ ln -s ../include include; \ fi - @if [ ! -r modules ] ; then \ - echo "You havent done cvs update -P -d"; \ - fi +cd modules; $(MAKE) all version.c: version.c.SH diff --git a/src/api-command.c b/src/api-command.c index f61e2bf52..065a4eb16 100644 --- a/src/api-command.c +++ b/src/api-command.c @@ -89,7 +89,7 @@ static Command *CommandAddInternal(Module *module, const char *cmd, CmdFunc func Command *command = NULL; RealCommand *c; - if (find_command_simple(cmd)) + if ((c = find_command(cmd, flags)) && (c->flags == flags)) { if (module) module->errorcode = MODERR_EXISTS; @@ -253,15 +253,26 @@ static RealCommand *add_Command_backend(const char *cmd) RealCommand *find_command(const char *cmd, int flags) { RealCommand *p; - for (p = CommandHash[toupper(*cmd)]; p; p = p->next) { - if ((flags & CMD_UNREGISTERED) && !(p->flags & CMD_UNREGISTERED)) - continue; - if ((flags & CMD_SHUN) && !(p->flags & CMD_SHUN)) - continue; - if ((flags & CMD_VIRUS) && !(p->flags & CMD_VIRUS)) - continue; - if ((flags & CMD_ALIAS) && !(p->flags & CMD_ALIAS)) - continue; + for (p = CommandHash[toupper(*cmd)]; p; p = p->next) + { + if (flags & CMD_CONTROL) + { + if (!(p->flags & CMD_CONTROL)) + continue; + } else + { + if ((flags & CMD_UNREGISTERED) && !(p->flags & CMD_UNREGISTERED)) + continue; + if ((flags & CMD_SHUN) && !(p->flags & CMD_SHUN)) + continue; + if ((flags & CMD_VIRUS) && !(p->flags & CMD_VIRUS)) + continue; + if ((flags & CMD_ALIAS) && !(p->flags & CMD_ALIAS)) + continue; + if (p->flags & CMD_CONTROL) + continue; /* important to also filter it this way ;) */ + } + if (!strcasecmp(p->cmd, cmd)) return p; } diff --git a/src/api-event.c b/src/api-event.c index 799edb750..1ad24f669 100644 --- a/src/api-event.c +++ b/src/api-event.c @@ -27,9 +27,6 @@ ID_Copyright("(C) Carsten Munk 2001"); MODVAR Event *events = NULL; -extern EVENT(unrealdns_removeoldrecords); -extern EVENT(unrealdb_expire_secret_cache); - /** Add an event, a function that will run at regular intervals. * @param module Module that this event belongs to * @param name Name of the event @@ -211,18 +208,3 @@ void DoEvents(void) CleanupEvents(); } - -void SetupEvents(void) -{ - /* Start events */ - EventAdd(NULL, "tunefile", save_tunefile, NULL, 300*1000, 0); - EventAdd(NULL, "garbage", garbage_collect, NULL, GARBAGE_COLLECT_EVERY*1000, 0); - EventAdd(NULL, "loop", loop_event, NULL, 1000, 0); - EventAdd(NULL, "unrealdns_removeoldrecords", unrealdns_removeoldrecords, NULL, 15000, 0); - EventAdd(NULL, "check_pings", check_pings, NULL, 1000, 0); - EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); - EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0); - EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0); - EventAdd(NULL, "unrealdb_expire_secret_cache", unrealdb_expire_secret_cache, NULL, 61000, 0); - EventAdd(NULL, "throttling_check_expire", throttling_check_expire, NULL, 1000, 0); -} diff --git a/src/conf.c b/src/conf.c index 72d052eb6..dc0b0fedf 100644 --- a/src/conf.c +++ b/src/conf.c @@ -2412,7 +2412,8 @@ void config_rehash() } for (listen_ptr = conf_listen; listen_ptr; listen_ptr = listen_ptr->next) { - listen_ptr->flag.temporary = 1; + if (!(listen_ptr->options & LISTENER_CONTROL)) + listen_ptr->flag.temporary = 1; } for (tld_ptr = conf_tld; tld_ptr; tld_ptr = (ConfigItem_tld *) next) { @@ -2945,7 +2946,9 @@ ConfigItem_listen *find_listen(const char *ipmask, int port, SocketType socket_t for (e = conf_listen; e; e = e->next) { - if (socket_type == SOCKET_TYPE_UNIX) + if (e->socket_type != socket_type) + continue; + if (e->socket_type == SOCKET_TYPE_UNIX) { if (!strcmp(e->file, ipmask)) return e; @@ -9352,6 +9355,7 @@ void config_run(void) { extcmodes_check_for_changes(); start_listeners(); + add_proc_io_server(); free_all_config_resources(); } @@ -10624,13 +10628,16 @@ void request_rehash(Client *client) int rehash_internal(Client *client) { + int failure; + /* Log it here if it is by a signal */ if (client == NULL) unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", client, "Rehashing server configuration file [./unrealircd rehash]"); - loop.rehashing = 1; /* double checking.. */ + loop.rehashing = 2; /* now doing the actual rehash */ - if (config_test() == 0) + failure = config_test(); + if (failure == 0) config_run(); /* TODO: uh.. are we supposed to do all this for a failed rehash too? maybe some but not all? */ reread_motdsandrules(); @@ -10641,8 +10648,11 @@ int rehash_internal(Client *client) // unload_all_unused_moddata(); -- this will crash umodes_check_for_changes(); charsys_check_for_changes(); + + /* Clear everything now that we are done */ loop.rehashing = 0; remote_rehash_client = NULL; + procio_post_rehash(failure); return 1; } diff --git a/src/ircd.c b/src/ircd.c index 4477bdfab..3fc7462bb 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -25,209 +25,33 @@ char *malloc_options = "h" MALLOC_FLAGS_EXTRA; #endif -#ifndef _WIN32 -extern char unreallogo[]; -#endif -int SVSNOOP = 0; +/* Externals */ extern MODVAR char *buildid; -time_t timeofday = 0; -struct timeval timeofday_tv; -int tainted = 0; -LoopStruct loop; -#ifndef _WIN32 -uid_t irc_uid = 0; -gid_t irc_gid = 0; -#endif - -MODVAR IRCCounts irccounts; -MODVAR Client me; /* That's me */ -MODVAR char *me_hash; extern char backupbuf[8192]; -#ifdef _WIN32 -extern SERVICE_STATUS_HANDLE IRCDStatusHandle; -extern SERVICE_STATUS IRCDStatus; -#endif - -MODVAR unsigned char conf_debuglevel = 0; - -void server_reboot(const char *); -void restart(const char *); -static void open_debugfile(), setup_signals(); +extern EVENT(unrealdns_removeoldrecords); +extern EVENT(unrealdb_expire_secret_cache); extern void init_glines(void); extern void tkl_init(void); extern void process_clients(void); extern void unrealdb_test(void); - -#ifndef _WIN32 -MODVAR char **myargv; -#else -LPCSTR cmdLine; -#endif -char *configfile = NULL; /* Server configuration file */ -int debuglevel = 0; /* Server debug level */ -int bootopt = 0; /* Server boot option flags */ -char *debugmode = ""; /* -"- -"- -"- */ -char *sbrk0; /* initial sbrk(0) */ -static int dorehash = 0, dorestart = 0, doreloadcert = 0; -MODVAR int booted = FALSE; - -void s_die() -{ -#ifdef _WIN32 - Client *client; - if (!IsService) - { - loop.terminating = 1; - unload_all_modules(); - - list_for_each_entry(client, &lclient_list, lclient_node) - (void) send_queued(client); - - exit(-1); - } - else { - SERVICE_STATUS status; - SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); - SC_HANDLE hService = OpenService(hSCManager, "UnrealIRCd", SERVICE_STOP); - ControlService(hService, SERVICE_CONTROL_STOP, &status); - } -#else - loop.terminating = 1; - unload_all_modules(); - unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); - exit(0); -#endif -} - -#ifndef _WIN32 -static void s_rehash() -{ - struct sigaction act; - dorehash = 1; - act.sa_handler = s_rehash; - act.sa_flags = 0; - (void)sigemptyset(&act.sa_mask); - (void)sigaddset(&act.sa_mask, SIGHUP); - (void)sigaction(SIGHUP, &act, NULL); -} - -static void s_reloadcert() -{ - struct sigaction act; - doreloadcert = 1; - act.sa_handler = s_reloadcert; - act.sa_flags = 0; - (void)sigemptyset(&act.sa_mask); - (void)sigaddset(&act.sa_mask, SIGUSR1); - (void)sigaction(SIGUSR1, &act, NULL); -} -#endif // #ifndef _WIN32 - -void restart(const char *mesg) -{ - server_reboot(mesg); -} - -void s_restart() -{ - dorestart = 1; -#if 0 - static int restarting = 0; - - if (restarting == 0) { - /* - * Send (or attempt to) a dying scream to oper if present - */ - - restarting = 1; - server_reboot("SIGINT"); - } -#endif -} - - +extern void ignore_this_signal(); +extern void s_rehash(); +extern void s_reloadcert(); +extern void s_restart(); +extern void s_die(); #ifndef _WIN32 -/** Signal handler for signals which we ignore, - * like SIGPIPE ("Broken pipe") and SIGWINCH (terminal window changed) etc. - */ -void ignore_this_signal() -{ - struct sigaction act; - - act.sa_handler = ignore_this_signal; - act.sa_flags = 0; - (void)sigemptyset(&act.sa_mask); - (void)sigaddset(&act.sa_mask, SIGALRM); - (void)sigaddset(&act.sa_mask, SIGPIPE); - (void)sigaction(SIGALRM, &act, (struct sigaction *)NULL); - (void)sigaction(SIGPIPE, &act, (struct sigaction *)NULL); -#ifdef SIGWINCH - (void)sigaddset(&act.sa_mask, SIGWINCH); - (void)sigaction(SIGWINCH, &act, (struct sigaction *)NULL); -#endif -} -#endif /* #ifndef _WIN32 */ - - -void server_reboot(const char *mesg) -{ - int i; - Client *client; - unreal_log(ULOG_INFO, "main", "UNREALIRCD_RESTARTING", NULL, - "Restarting server: $reason", - log_data_string("reason", mesg)); - - list_for_each_entry(client, &lclient_list, lclient_node) - (void) send_queued(client); - - /* - * ** fd 0 must be 'preserved' if either the -d or -i options have - * ** been passed to us before restarting. - */ -#ifdef HAVE_SYSLOG - (void)closelog(); -#endif -#ifndef _WIN32 - for (i = 3; i < MAXCONNECTIONS; i++) - (void)close(i); - if (!(bootopt & (BOOT_TTY | BOOT_DEBUG))) - (void)close(2); - (void)close(1); - (void)close(0); - (void)execv(MYNAME, myargv); +// nix specific +extern char unreallogo[]; #else - close_connections(); - if (!IsService) - { - CleanUp(); - WinExec(cmdLine, SW_SHOWDEFAULT); - } -#endif - unload_all_modules(); -#ifdef _WIN32 - if (IsService) - { - SERVICE_STATUS status; - PROCESS_INFORMATION pi; - STARTUPINFO si; - char fname[MAX_PATH]; - memset(&status, 0, sizeof(status)); - memset(&si, 0, sizeof(si)); - IRCDStatus.dwCurrentState = SERVICE_STOP_PENDING; - SetServiceStatus(IRCDStatusHandle, &IRCDStatus); - GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH); - CreateProcess(fname, "restartsvc", NULL, NULL, FALSE, - 0, NULL, NULL, &si, &pi); - IRCDStatus.dwCurrentState = SERVICE_STOPPED; - SetServiceStatus(IRCDStatusHandle, &IRCDStatus); - ExitProcess(0); - } - else +// windows specific +extern SERVICE_STATUS_HANDLE IRCDStatusHandle; +extern SERVICE_STATUS IRCDStatus; #endif - exit(-1); -} -MODVAR char *areason; +/* Forward declarations */ +void server_reboot(const char *); +void restart(const char *); +static void open_debugfile(), setup_signals(); EVENT(loop_event) { @@ -677,30 +501,19 @@ void detect_timeshift_and_warn(void) oldtimeofday = timeofday; } -/** Check if at least 'minimum' seconds passed by since last run. - * @param tv_old Pointer to a timeval struct to keep track of things. - * @param minimum The time specified in milliseconds (eg: 1000 for 1 second) - * @returns When 'minimum' msec passed 1 is returned and the time is reset, otherwise 0 is returned. - */ -int minimum_msec_since_last_run(struct timeval *tv_old, long minimum) +void SetupEvents(void) { - long v; - - if (tv_old->tv_sec == 0) - { - /* First call ever */ - tv_old->tv_sec = timeofday_tv.tv_sec; - tv_old->tv_usec = timeofday_tv.tv_usec; - return 0; - } - v = ((timeofday_tv.tv_sec - tv_old->tv_sec) * 1000) + ((timeofday_tv.tv_usec - tv_old->tv_usec)/1000); - if (v >= minimum) - { - tv_old->tv_sec = timeofday_tv.tv_sec; - tv_old->tv_usec = timeofday_tv.tv_usec; - return 1; - } - return 0; + /* Start events */ + EventAdd(NULL, "tunefile", save_tunefile, NULL, 300*1000, 0); + EventAdd(NULL, "garbage", garbage_collect, NULL, GARBAGE_COLLECT_EVERY*1000, 0); + EventAdd(NULL, "loop", loop_event, NULL, 1000, 0); + EventAdd(NULL, "unrealdns_removeoldrecords", unrealdns_removeoldrecords, NULL, 15000, 0); + EventAdd(NULL, "check_pings", check_pings, NULL, 1000, 0); + EventAdd(NULL, "check_deadsockets", check_deadsockets, NULL, 1000, 0); + EventAdd(NULL, "handshake_timeout", handshake_timeout, NULL, 1000, 0); + EventAdd(NULL, "tls_check_expiry", tls_check_expiry, NULL, (86400/2)*1000, 0); + EventAdd(NULL, "unrealdb_expire_secret_cache", unrealdb_expire_secret_cache, NULL, 61000, 0); + EventAdd(NULL, "throttling_check_expire", throttling_check_expire, NULL, 1000, 0); } /** The main function. This will call SocketLoop() once the server is ready. */ @@ -755,7 +568,6 @@ int InitUnrealIRCd(int argc, char *argv[]) SetErrorMode(SEM_FAILCRITICALERRORS); #endif #if !defined(_WIN32) && !defined(_AMIGA) - sbrk0 = (char *)sbrk((size_t)0); uid = getuid(); euid = geteuid(); gid = getgid(); @@ -1032,7 +844,6 @@ int InitUnrealIRCd(int argc, char *argv[]) initstats(); if (!loop.config_test) DeleteTempModules(); - booted = FALSE; #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0 /* Hack to stop people from being able to read the config file */ (void)chmod(CPATH, DEFAULT_PERMISSIONS); @@ -1061,7 +872,6 @@ int InitUnrealIRCd(int argc, char *argv[]) } if (config_test() < 0) exit(-1); - booted = TRUE; load_tunefile(); make_umodestr(); SetListening(&me); diff --git a/src/ircd_vars.c b/src/ircd_vars.c new file mode 100644 index 000000000..6dec95055 --- /dev/null +++ b/src/ircd_vars.c @@ -0,0 +1,31 @@ +/************************************************************************ + * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/ircd_vars.c + * (c) 2021- Bram Matthys and The UnrealIRCd team + * License: GPLv2 + */ +#include "unrealircd.h" + +/** @file + * @brief UnrealIRCd global variables of the IRCd + */ + +int SVSNOOP = 0; +time_t timeofday = 0; +struct timeval timeofday_tv; +int tainted = 0; +LoopStruct loop; +MODVAR IRCCounts irccounts; +MODVAR Client me; /* That's me */ +MODVAR char *me_hash; +char *configfile = NULL; /* Server configuration file */ +int debuglevel = 0; /* Server debug level */ +int bootopt = 0; /* Server boot option flags */ +char *debugmode = ""; /* -"- -"- -"- */ +int dorehash = 0; /**< Rehash server on next socket loop */ +int dorestart = 0; /**< Restart server on next socket loop */ +int doreloadcert = 0; /**< Reload TLS certificate on next socket loop */ +#ifndef _WIN32 +char **myargv; +#else +LPCSTR cmdLine; +#endif diff --git a/src/list.c b/src/list.c index d752fb823..9435e7554 100644 --- a/src/list.c +++ b/src/list.c @@ -44,6 +44,7 @@ MODVAR int numclients = 0; // TODO: Document whether servers are included or excluded in these lists... MODVAR struct list_head unknown_list; /**< Local clients in handshake (may become a user or server later) */ +MODVAR struct list_head control_list; /**< Local "control channel" clients */ MODVAR struct list_head lclient_list; /**< Local clients (users only, right?) */ MODVAR struct list_head client_list; /**< All clients - local and remote (not in handshake) */ MODVAR struct list_head server_list; /**< Locally connected servers */ @@ -71,6 +72,7 @@ void initlists(void) INIT_LIST_HEAD(&server_list); INIT_LIST_HEAD(&oper_list); INIT_LIST_HEAD(&unknown_list); + INIT_LIST_HEAD(&control_list); INIT_LIST_HEAD(&global_server_list); INIT_LIST_HEAD(&dead_list); diff --git a/src/log.c b/src/log.c index bfddcddce..51787a3cf 100644 --- a/src/log.c +++ b/src/log.c @@ -1004,7 +1004,6 @@ static NameValue log_colors_terminal[] = { { ULOG_ERROR, "\033[91m" }, { ULOG_FATAL, "\033[95m" }, }; -#define TERMINAL_COLOR_RESET "\033[0m" const char *log_level_irc_color(LogLevel loglevel) { @@ -1635,6 +1634,25 @@ void do_unreal_log_remote(LogLevel loglevel, const char *subsystem, const char * do_unreal_log_remote_deliver(loglevel, subsystem, event_id, msg, json_serialized); } +/** Send server notices to control channel */ +void do_unreal_log_control(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server) +{ + Client *client; + MultiLine *m; + + if (!loop.booted) + return; + + /* Never send these */ + if (!strcmp(subsystem, "rawtraffic")) + return; + + list_for_each_entry(client, &control_list, lclient_node) + if (IsMonitorRehash(client)) + for (m = msg; m; m = m->next) + sendto_one(client, NULL, "REPLY [%s] %s", log_level_valtostring(loglevel), m->line); +} + void do_unreal_log_free_args(va_list vl) { LogData *d; @@ -1815,6 +1833,10 @@ void do_unreal_log_internal(LogLevel loglevel, const char *subsystem, const char from_server = find_server(str, NULL); if (from_server == NULL) from_server = &me; + + if ((loop.rehashing == 2) || !strcmp(subsystem, "config")) + do_unreal_log_control(loglevel, subsystem, event_id, mmsg, json_serialized, from_server); + do_unreal_log_opers(loglevel, subsystem, event_id, mmsg, json_serialized, from_server); do_unreal_log_channels(loglevel, subsystem, event_id, mmsg, json_serialized, from_server); diff --git a/src/misc.c b/src/misc.c index 27c4f3631..8ebde1667 100644 --- a/src/misc.c +++ b/src/misc.c @@ -623,8 +623,8 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, cons if (client->local->fd >= 0 && !IsConnecting(client)) { - sendto_one(client, NULL, "ERROR :Closing Link: %s (%s)", - get_client_name(client, FALSE), comment); + if (!IsControl(client)) + sendto_one(client, NULL, "ERROR :Closing Link: %s (%s)", get_client_name(client, FALSE), comment); } close_connection(client); } @@ -2369,3 +2369,172 @@ void addlettertodynamicstringsorted(char **str, char letter) safe_free_raw(*str); *str = newbuf; } + +void s_die() +{ +#ifdef _WIN32 + Client *client; + if (!IsService) + { + loop.terminating = 1; + unload_all_modules(); + + list_for_each_entry(client, &lclient_list, lclient_node) + (void) send_queued(client); + + exit(-1); + } + else { + SERVICE_STATUS status; + SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + SC_HANDLE hService = OpenService(hSCManager, "UnrealIRCd", SERVICE_STOP); + ControlService(hService, SERVICE_CONTROL_STOP, &status); + } +#else + loop.terminating = 1; + unload_all_modules(); + unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); + exit(0); +#endif +} + +#ifndef _WIN32 +void s_rehash() +{ + struct sigaction act; + dorehash = 1; + act.sa_handler = s_rehash; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGHUP); + (void)sigaction(SIGHUP, &act, NULL); +} + +void s_reloadcert() +{ + struct sigaction act; + doreloadcert = 1; + act.sa_handler = s_reloadcert; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGUSR1); + (void)sigaction(SIGUSR1, &act, NULL); +} +#endif // #ifndef _WIN32 + +void restart(const char *mesg) +{ + server_reboot(mesg); +} + +void s_restart() +{ + dorestart = 1; +} + +#ifndef _WIN32 +/** Signal handler for signals which we ignore, + * like SIGPIPE ("Broken pipe") and SIGWINCH (terminal window changed) etc. + */ +void ignore_this_signal() +{ + struct sigaction act; + + act.sa_handler = ignore_this_signal; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGALRM); + (void)sigaddset(&act.sa_mask, SIGPIPE); + (void)sigaction(SIGALRM, &act, (struct sigaction *)NULL); + (void)sigaction(SIGPIPE, &act, (struct sigaction *)NULL); +#ifdef SIGWINCH + (void)sigaddset(&act.sa_mask, SIGWINCH); + (void)sigaction(SIGWINCH, &act, (struct sigaction *)NULL); +#endif +} +#endif /* #ifndef _WIN32 */ + + +void server_reboot(const char *mesg) +{ + int i; + Client *client; + unreal_log(ULOG_INFO, "main", "UNREALIRCD_RESTARTING", NULL, + "Restarting server: $reason", + log_data_string("reason", mesg)); + + list_for_each_entry(client, &lclient_list, lclient_node) + (void) send_queued(client); + + /* + * ** fd 0 must be 'preserved' if either the -d or -i options have + * ** been passed to us before restarting. + */ +#ifdef HAVE_SYSLOG + (void)closelog(); +#endif +#ifndef _WIN32 + for (i = 3; i < MAXCONNECTIONS; i++) + (void)close(i); + if (!(bootopt & (BOOT_TTY | BOOT_DEBUG))) + (void)close(2); + (void)close(1); + (void)close(0); + (void)execv(MYNAME, myargv); +#else + close_connections(); + if (!IsService) + { + CleanUp(); + WinExec(cmdLine, SW_SHOWDEFAULT); + } +#endif + unload_all_modules(); +#ifdef _WIN32 + if (IsService) + { + SERVICE_STATUS status; + PROCESS_INFORMATION pi; + STARTUPINFO si; + char fname[MAX_PATH]; + memset(&status, 0, sizeof(status)); + memset(&si, 0, sizeof(si)); + IRCDStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(IRCDStatusHandle, &IRCDStatus); + GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH); + CreateProcess(fname, "restartsvc", NULL, NULL, FALSE, + 0, NULL, NULL, &si, &pi); + IRCDStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(IRCDStatusHandle, &IRCDStatus); + ExitProcess(0); + } + else +#endif + exit(-1); +} + +/** Check if at least 'minimum' seconds passed by since last run. + * @param tv_old Pointer to a timeval struct to keep track of things. + * @param minimum The time specified in milliseconds (eg: 1000 for 1 second) + * @returns When 'minimum' msec passed 1 is returned and the time is reset, otherwise 0 is returned. + */ +int minimum_msec_since_last_run(struct timeval *tv_old, long minimum) +{ + long v; + + if (tv_old->tv_sec == 0) + { + /* First call ever */ + tv_old->tv_sec = timeofday_tv.tv_sec; + tv_old->tv_usec = timeofday_tv.tv_usec; + return 0; + } + v = ((timeofday_tv.tv_sec - tv_old->tv_sec) * 1000) + ((timeofday_tv.tv_usec - tv_old->tv_usec)/1000); + if (v >= minimum) + { + tv_old->tv_sec = timeofday_tv.tv_sec; + tv_old->tv_usec = timeofday_tv.tv_usec; + return 1; + } + return 0; +} diff --git a/src/parse.c b/src/parse.c index 473453acf..2b9f884d7 100644 --- a/src/parse.c +++ b/src/parse.c @@ -104,7 +104,9 @@ void parse_client_queued(Client *client) return; /* we delay processing of data until identd has replied */ if (!IsUser(client) && !IsServer(client) && (iConf.handshake_delay > 0) && - !IsNoHandshakeDelay(client) && (TStime() - client->local->creationtime < iConf.handshake_delay)) + !IsNoHandshakeDelay(client) && + !IsControl(client) && + (TStime() - client->local->creationtime < iConf.handshake_delay)) { return; /* we delay processing of data until set::handshake-delay is reached */ } @@ -368,6 +370,8 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, int mtags_ flags |= CMD_VIRUS; if (IsOper(from)) flags |= CMD_OPER; + if (IsControl(from)) + flags |= CMD_CONTROL; cmptr = find_command(ch, flags); if (!cmptr || !(cmptr->flags & CMD_NOLAG)) { @@ -376,6 +380,12 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, int mtags_ } if (!cmptr) { + if (IsControl(from)) + { + sendto_one(from, NULL, "ERROR UNKNOWN_COMMAND: %s", ch); + sendto_one(from, NULL, "END 1"); + return; + } /* Don't send error messages in response to NOTICEs * in pre-connection state. */ diff --git a/src/proc_io_client.c b/src/proc_io_client.c new file mode 100644 index 000000000..538c02811 --- /dev/null +++ b/src/proc_io_client.c @@ -0,0 +1,181 @@ +/************************************************************************ + * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/proc_io_client.c + * (c) 2022- Bram Matthys and The UnrealIRCd team + * + * See file AUTHORS in IRC package for additional names of + * the programmers. + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** @file + * @brief Inter-process I/O + */ +#include "unrealircd.h" + +int procio_client_connect(const char *file) +{ + int fd; + struct sockaddr_un addr; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, file, sizeof(addr.sun_path)); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + return -1; + + return fd; +} + +int procio_send(int fd, const char *command) +{ + char buf[512]; + int n; + snprintf(buf, sizeof(buf), "%s\r\n", command); + n = strlen(buf); + if (send(fd, buf, n, 0) != n) + return 0; + return 1; +} + +const char *recolor_logs(const char *str) +{ + static char retbuf[2048]; + char buf[2048], *p; + const char *color = NULL; + + strlcpy(buf, str, sizeof(buf)); + p = strchr(buf, ' '); + if ((*str != '[') || !p) + return str; + *p++ = '\0'; + + if (!strcmp(buf, "[debug]")) + color = log_level_terminal_color(ULOG_DEBUG); + else if (!strcmp(buf, "[info]")) + color = log_level_terminal_color(ULOG_INFO); + else if (!strcmp(buf, "[warning]")) + color = log_level_terminal_color(ULOG_WARNING); + else if (!strcmp(buf, "[error]")) + color = log_level_terminal_color(ULOG_ERROR); + else if (!strcmp(buf, "[fatal]")) + color = log_level_terminal_color(ULOG_FATAL); + else + color = log_level_terminal_color(ULOG_INVALID); + + snprintf(retbuf, sizeof(retbuf), "%s%s%s %s", + color, buf, TERMINAL_COLOR_RESET, p); + return retbuf; +} + +const char *recolor_split(const char *str) +{ + static char retbuf[2048]; + char buf[2048], *p; + const char *color = NULL; + + strlcpy(buf, str, sizeof(buf)); + p = strchr(buf, ' '); + if (!p) + return str; + *p++ = '\0'; + + snprintf(retbuf, sizeof(retbuf), "%s%s %s%s%s", + "\033[92m", buf, + "\033[93m", p, + TERMINAL_COLOR_RESET); + return retbuf; +} + +int procio_client(const char *command, int auto_color_logs) +{ + int fd; + char buf[READBUFSIZE]; + int n; + dbuf queue; + + if (auto_color_logs && !terminal_supports_color()) + auto_color_logs = 0; + + fd = procio_client_connect(CONTROLFILE); + if (fd < 0) + { + fprintf(stderr, "Could not connect to '%s': %s\n", + CONTROLFILE, strerror(errno)); + fprintf(stderr, "Maybe the IRC server is not running?\n"); + return -1; + } + + /* Expect the welcome message */ + memset(buf, 0, sizeof(buf)); + n = recv(fd, buf, sizeof(buf), 0); + if ((n < 0) || strncmp(buf, "READY", 4)) + { + fprintf(stderr, "Error while communicating to IRCd via '%s': %s\n" + "Maybe the IRC server is not running?\n", + CONTROLFILE, strerror(errno)); + return -1; + } + + if (!procio_send(fd, command)) + { + fprintf(stderr, "Error while sending command to IRCd via '%s'. Strange!\n", + CONTROLFILE); + return -1; + } + + *buf = '\0'; + dbuf_queue_init(&queue); + while(1) + { + n = recv(fd, buf, sizeof(buf)-1, 0); + if (n <= 0) + break; + buf[n] = '\0'; /* terminate the string */ + dbuf_put(&queue, buf, n); + + /* And try to read all complete lines: */ + do + { + n = dbuf_getmsg(&queue, buf); + if (n > 0) + { + if (!strncmp(buf, "REPLY ", 6)) + { + char *reply = buf+6; + if (auto_color_logs == 0) + printf("%s\n", reply); + else if (auto_color_logs == 1) + printf("%s\n", recolor_logs(reply)); + else + printf("%s\n", recolor_split(reply)); + } else + if (!strncmp(buf, "END ", 4)) + { + int exitcode = atoi(buf+4); + close(fd); + return exitcode; + } + } + } while(n > 0); + } + + return 0; /* zero is good */ +} diff --git a/src/proc_io_server.c b/src/proc_io_server.c new file mode 100644 index 000000000..d19065180 --- /dev/null +++ b/src/proc_io_server.c @@ -0,0 +1,133 @@ +/************************************************************************ + * UnrealIRCd - Unreal Internet Relay Chat Daemon - src/proc_io_server.c + * (c) 2022- Bram Matthys and The UnrealIRCd team + * + * See file AUTHORS in IRC package for additional names of + * the programmers. + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** @file + * @brief Inter-process I/O + */ +#include "unrealircd.h" +#include