Make "./unrealircd rehash" show output on the terminal, same for

"./unrealircd reloadtls" and there is now also a "./unrealircd status"

The output is colorized if the terminal supports it (just like on the
boot screen) and also the exit status is 0 for success and non-0 for
failure. The purpose of all this is that you can easily detect rehash
errors on the command line.

These three commands communicate to UnrealIRCd via the new control
UNIX socket, which is in ~/data/unrealircd.ctl.
This also does a lot of other stuff because we now have an internal
tool called bin/unrealircdctl which is called by ./unrealircd for
some of the commands to communicate to the unrealircd.ctl socket.
Later on more of the existing functionality may be moved to that
tool and we may also provide it on Windows in CLI mode so people
have more of the same functionality as on *NIX.
pull/41/head
Bram Matthys 1 year ago
parent 834736070e
commit 39688517b0
No known key found for this signature in database
GPG Key ID: BF8116B163EAAE98

@ -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@

23
configure vendored

@ -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

@ -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])],

@ -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);

@ -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

@ -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 <pre>CMD_FUNC(cmd_yourcmd)</pre> 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))

@ -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

@ -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;
}

@ -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);
}

@ -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;
}

@ -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);

@ -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

@ -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);

@ -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);

@ -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;
}

@ -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.
*/

@ -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 */
}

@ -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 <ares.h>
CMD_FUNC(procio_status);
CMD_FUNC(procio_rehash);
CMD_FUNC(procio_exit);
CMD_FUNC(procio_help);
/** Create the unrealircd.ctl socket (server-side) */
void add_proc_io_server(void)
{
ConfigItem_listen *listener = safe_alloc(sizeof(ConfigItem_listen));
safe_strdup(listener->file, CONTROLFILE);
listener->socket_type = SOCKET_TYPE_UNIX;
listener->options = LISTENER_CONTROL;
listener->fd = -1;
AddListItem(listener, conf_listen);
if (add_listener(listener) == -1)
exit(-1);
CommandAdd(NULL, "STATUS", procio_status, MAXPARA, CMD_CONTROL);
CommandAdd(NULL, "REHASH", procio_rehash, MAXPARA, CMD_CONTROL);
CommandAdd(NULL, "EXIT", procio_exit, MAXPARA, CMD_CONTROL);
CommandAdd(NULL, "HELP", procio_help, MAXPARA, CMD_CONTROL);
}
/** Start of "control channel" client handshake - this is minimal
* @param client The client
*/
void start_of_control_client_handshake(Client *client)
{
sendto_one(client, NULL, "READY %s %s", me.name, version);
fd_setselect(client->local->fd, FD_SELECT_READ, read_packet, client);
}
CMD_FUNC(procio_status)
{
sendto_one(client, NULL, "REPLY servername %s", me.name);
sendto_one(client, NULL, "REPLY unrealircd_version %s", version);
sendto_one(client, NULL, "REPLY libssl_version %s", SSLeay_version(SSLEAY_VERSION));
sendto_one(client, NULL, "REPLY libsodium_version %s", sodium_version_string());
#ifdef USE_LIBCURL
sendto_one(client, NULL, "REPLY libcurl_version %s", curl_version());
#endif
sendto_one(client, NULL, "REPLY libcares_version %s", ares_version(NULL));
sendto_one(client, NULL, "REPLY libpcre2_version %s", pcre2_version());
sendto_one(client, NULL, "REPLY global_clients %ld", (long)irccounts.clients);
sendto_one(client, NULL, "REPLY local_clients %ld", (long)irccounts.me_clients);
sendto_one(client, NULL, "REPLY operators %ld", (long)irccounts.operators);
sendto_one(client, NULL, "REPLY servers %ld", (long)irccounts.servers);
sendto_one(client, NULL, "REPLY channels %ld", (long)irccounts.channels);
sendto_one(client, NULL, "END 0");
}
CMD_FUNC(procio_rehash)
{
if (loop.rehashing)
{
sendto_one(client, NULL, "REPLY ERROR: A rehash is already in progress");
sendto_one(client, NULL, "END 1");
return;
}
if (parv[1] && !strcmp(parv[1], "-tls"))
{
int ret;
SetMonitorRehash(client);
unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", NULL, "Reloading all TLS related data (./unrealircd reloadtls)");
ret = reinit_tls();
sendto_one(client, NULL, "END %d", ret == 0 ? -1 : 0);
ClearMonitorRehash(client);
} else {
SetMonitorRehash(client);
request_rehash(client);
/* completion will go via procio_post_rehash() */
}
}
CMD_FUNC(procio_exit)