Skip to content

Commit

Permalink
variables.c: Allow variables to be reloaded.
Browse files Browse the repository at this point in the history
Allow global and per-user variables to be reloaded
during runtime.

Also add a reload handler that simplifies and standardizes
reloading of core components, rather than using individual
ad-hoc CLI commands for each one.
  • Loading branch information
InterLinked1 committed Jan 30, 2024
1 parent 9ac1df3 commit 0a9aa4c
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 44 deletions.
7 changes: 4 additions & 3 deletions bbs/group.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "include/stringlist.h"
#include "include/cli.h"
#include "include/group.h"
#include "include/reload.h"

/* Currently, groups must be defined statically in groups.conf.
* If this becomes a hindrance, this could be dynamized. */
Expand Down Expand Up @@ -140,20 +141,19 @@ static int load_groups(void)
}

/*! \todo Should use a generic system-wide reload mechanism */
static int cli_groupreload(struct bbs_cli_args *a)
static int reload_groups(int fd)
{
RWLIST_WRLOCK(&groups);
RWLIST_REMOVE_ALL(&groups, entry, group_free);
load_groups();
RWLIST_UNLOCK(&groups);
bbs_dprintf(a->fdout, "Groups reloaded\n");
bbs_dprintf(fd, "Reloaded groups\n");
return 0;
}

static struct bbs_cli_entry cli_commands_groups[] = {
BBS_CLI_COMMAND(cli_groups, "groups", 1, "List user groups", NULL),
BBS_CLI_COMMAND(cli_group, "group", 2, "List members of a user group", "group <name>"),
BBS_CLI_COMMAND(cli_groupreload, "groupreload", 1, "Reload user groups", NULL),
};

/*! \todo Currently groups cannot be reloaded, but it might make sense here (and in several other places) to allow this, using some builtin functionality */
Expand All @@ -175,6 +175,7 @@ int bbs_groups_cleanup(void)
int bbs_groups_init(void)
{
load_config();
bbs_register_reload_handler("groups", "Reload BBS user groups", reload_groups);
bbs_cli_register_multiple(cli_commands_groups);
return 0;
}
12 changes: 8 additions & 4 deletions bbs/menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "include/utils.h"
#include "include/startup.h"
#include "include/cli.h"
#include "include/reload.h"

static int case_sensitive = 0;

Expand Down Expand Up @@ -944,10 +945,13 @@ static int check_menus(void)
return 0;
}

static int cli_menureload(struct bbs_cli_args *a)
static int reload_menus(int fd)
{
bbs_cli_set_stdout_logging(a->fdout, 1); /* We want to be able to see the logging */
return bbs_load_menus(1);
int res = bbs_load_menus(1);
if (!res) {
bbs_dprintf(fd, "Reloaded menus\n");
}
return res;
}

static int cli_menus(struct bbs_cli_args *a)
Expand All @@ -961,7 +965,6 @@ static int cli_menu(struct bbs_cli_args *a)
}

static struct bbs_cli_entry cli_commands_menu[] = {
BBS_CLI_COMMAND(cli_menureload, "menureload", 1, "Reload menus", NULL),
BBS_CLI_COMMAND(cli_menus, "menus", 1, "List all menus", NULL),
BBS_CLI_COMMAND(cli_menu, "menu", 2, "Dump a menu", "menu <name>"),
};
Expand All @@ -976,6 +979,7 @@ int bbs_load_menus(int reload)
bbs_run_when_started(check_menus, STARTUP_PRIORITY_DEFAULT);

if (!reload) {
bbs_register_reload_handler("menus", "Reload BBS menus", reload_menus);
res |= bbs_cli_register_multiple(cli_commands_menu);
}
return res;
Expand Down
127 changes: 123 additions & 4 deletions bbs/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "include/linkedlists.h"
#include "include/stringlist.h"
#include "include/module.h"
#include "include/reload.h"
#include "include/config.h"
#include "include/utils.h" /* use bbs_dir_traverse */
#include "include/node.h"
Expand Down Expand Up @@ -932,7 +933,7 @@ static int on_file_autoload(const char *dir_name, const char *filename, void *ob

if (mod) {
if (!stringlist_contains(&modules_preload, filename)) { /* If it was preloaded, then it's legitimate */
bbs_error("Module %s is already loaded\n", filename);
bbs_debug(1, "Module %s is already loaded\n", filename);
}
return 0; /* Always return 0 or otherwise we'd abort the entire autoloading process */
}
Expand Down Expand Up @@ -1328,11 +1329,126 @@ static int cli_unloadwait(struct bbs_cli_args *a)
return 0;
}

struct reload_handler {
const char *name;
const char *description;
int (*reloader)(int fd);
RWLIST_ENTRY(reload_handler) entry;
char data[];
};

static RWLIST_HEAD_STATIC(reload_handlers, reload_handler);

int bbs_register_reload_handler(const char *name, const char *description, int (*reloader)(int fd))
{
struct reload_handler *r;
size_t namelen;

if (strchr(name, '_')) {
bbs_warning("Reload handler '%s' contains forbidden character\n", name);
return -1;
}

namelen = strlen(name);

RWLIST_WRLOCK(&reload_handlers);
RWLIST_TRAVERSE(&reload_handlers, r, entry) {
if (!strcmp(name, r->name)) {
RWLIST_UNLOCK(&reload_handlers);
bbs_error("Reload handler '%s' already registered\n", name);
return -1;
}
}

r = calloc(1, sizeof(*r) + namelen + strlen(description) + 2);
if (ALLOC_FAILURE(r)) {
RWLIST_UNLOCK(&reload_handlers);
return -1;
}

r->reloader = reloader;
strcpy(r->data, name); /* Safe */
strcpy(r->data + namelen + 1, description);
r->name = r->data;
r->description = r->data + namelen + 1;
RWLIST_INSERT_TAIL(&reload_handlers, r, entry);
RWLIST_UNLOCK(&reload_handlers);
return 0;
}

static int cli_reloadhandlers(struct bbs_cli_args *a)
{
struct reload_handler *r;

bbs_dprintf(a->fdout, "%-20s %s\n", "Name", "Description");
RWLIST_RDLOCK(&reload_handlers);
RWLIST_TRAVERSE(&reload_handlers, r, entry) {
bbs_dprintf(a->fdout, "%-20s %s\n", r->name, r->description);
}
RWLIST_UNLOCK(&reload_handlers);
return 0;
}

static int reload_core(const char *name, int fd)
{
int res = 0;
int reloaded = 0;
struct reload_handler *r;

if (bbs_is_shutting_down()) {
/* Can't reload if shutting down, particularly as
* some stuff in the core will start unregistering
* and cleaning up, past a certain point of shutdown. */
return -1;
}

/* There should never be more than one reload happening at a time,
* so just write lock, even though we're not changing the list. */
RWLIST_WRLOCK(&reload_handlers);
RWLIST_TRAVERSE(&reload_handlers, r, entry) {
if (!name || !strcmp(name, r->name)) {
int rres;
/* These are all in the core, so no need to ref/unref a module.
* Just execute the callback. */
rres = r->reloader(fd);
if (!res) {
reloaded++;
}
res |= rres;
}
}
RWLIST_UNLOCK(&reload_handlers);
if (!res && reloaded) {
/* We reloaded at least one thing, and everything reloaded successfully */
return 0;
}
if (res) {
/* Handler(s) failed to reload */
bbs_dprintf(fd, "Full or partial reload failure\n");
return res;
}
/* Either something failed to reload, or we didn't actually reload anything (typo in target) */
bbs_dprintf(fd, "No such component to reload: '%s'\n", name);
return 1;
}

static int cli_reload(struct bbs_cli_args *a)
{
const char *s = a->argv[1];
bbs_cli_set_stdout_logging(a->fdout, 1); /* We want to be able to see the logging */
return threadsafe_reload(s, 0);

/* Request to reload a specific module */
if (a->argc >= 2) {
const char *s = a->argv[1];
/* If it's a module name, it contains an underscore */
if (strchr(s, '_')) {
return threadsafe_reload(s, 0);
} else {
return reload_core(s, a->fdout);
}
}

/* Reload all core components */
return reload_core(NULL, a->fdout);
}

static int cli_reloadwait(struct bbs_cli_args *a)
Expand Down Expand Up @@ -1382,7 +1498,8 @@ static struct bbs_cli_entry cli_commands_modules[] = {
BBS_CLI_COMMAND(cli_loadwait, "loadwait", 2, "Keep retrying load of dynamic module until it successfully loads", "loadwait <module>"),
BBS_CLI_COMMAND(cli_unload, "unload", 2, "Unload dynamic module", "unload <module>"),
BBS_CLI_COMMAND(cli_unloadwait, "unloadwait", 2, "Keep retrying load of dynamic module until it successfully unloads", "unloadwait <module>"),
BBS_CLI_COMMAND(cli_reload, "reload", 2, "Hotswap (unload and load) dynamic module", "reload <module>"),
BBS_CLI_COMMAND(cli_reload, "reload", 1, "Hotswap (unload and load) dynamic module or run a specific (or all) core reload handler(s)", "reload [<module>]"),
BBS_CLI_COMMAND(cli_reloadhandlers, "reloadhandlers", 1, "List all core reload handlers", NULL),
BBS_CLI_COMMAND(cli_reloadwait, "reloadwait", 2, "Keep retrying hotswap reload of dynamic module until successful", "reloadwait <module>"),
BBS_CLI_COMMAND(cli_reloadqueue, "reloadqueue", 2, "Unload and load dynamic module, queuing if necessary", "reloadqueue <module>"),
};
Expand Down Expand Up @@ -1521,5 +1638,7 @@ int unload_modules(void)
RWLIST_UNLOCK(&modules);

bbs_cli_unregister_multiple(cli_commands_modules);

RWLIST_WRLOCK_REMOVE_ALL(&reload_handlers, entry, free);
return 0;
}
10 changes: 6 additions & 4 deletions bbs/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "include/utils.h"
#include "include/event.h"
#include "include/cli.h"
#include "include/reload.h"

#ifdef HAVE_OPENSSL
static char root_certs[84] = "/etc/ssl/certs/ca-certificates.crt";
Expand Down Expand Up @@ -1025,12 +1026,12 @@ static int thread_launched = 0;
static int locks_initialized = 0;

/*! \brief Limited support for reloading configuration (e.g. new certificates) */
static int cli_tlsreload(struct bbs_cli_args *a)
static int tlsreload(int fd)
{
struct ssl_fd *sfd;

if (!locks_initialized) {
bbs_dprintf(a->fdout, "TLS may only be reloaded if it initialized during startup. Restart the BBS to load new configuration.\n");
bbs_dprintf(fd, "TLS may only be reloaded if it initialized during startup. Restart the BBS to load new configuration.\n");
return -1;
}

Expand All @@ -1057,7 +1058,7 @@ static int cli_tlsreload(struct bbs_cli_args *a)
break;
}
if (sfd) { /* At least server session exists */
bbs_dprintf(a->fdout, "TLS may not be reloaded while any server sessions are in use. Kick any TLS sessions and try again.\n");
bbs_dprintf(fd, "TLS may not be reloaded while any server sessions are in use. Kick any TLS sessions and try again.\n");
RWLIST_UNLOCK(&sslfds);
pthread_rwlock_unlock(&ssl_cert_lock);
return -1;
Expand All @@ -1077,12 +1078,12 @@ static int cli_tlsreload(struct bbs_cli_args *a)
ssl_is_available = 1;
pthread_rwlock_unlock(&ssl_cert_lock);

bbs_dprintf(fd, "Reloaded TLS configuration\n");
return 0;
}

static struct bbs_cli_entry cli_commands_tls[] = {
BBS_CLI_COMMAND(cli_tls, "tls", 1, "List all TLS sessions", NULL),
BBS_CLI_COMMAND(cli_tlsreload, "tlsreload", 1, "Reload existing TLS configuration", NULL),
};

static int setup_ssl_io(void)
Expand All @@ -1100,6 +1101,7 @@ static int setup_ssl_io(void)

int ssl_server_init(void)
{
bbs_register_reload_handler("tls", "Reload TLS certificates and configuration", tlsreload);
bbs_cli_register_multiple(cli_commands_tls);
#ifdef HAVE_OPENSSL
setup_ssl_io(); /* Even if we can't be a TLS server, we can still be a TLS client. */
Expand Down
Loading

0 comments on commit 0a9aa4c

Please sign in to comment.