Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / READMEs / README-sai-power.md
Author[]Andy Green <andy@warmcat.com> 2026-09-09 08:35 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-09-09 08:35 UTC
Tree4e6f46c509f788387aed5710931742f356f210fd   Raw Patch
 
server, web: share the config helper, let lws own the generic switches
server, web: share the config helper, let lws own the generic switches

sai-server and sai-web each carried an identical copy of
sai_lws_context_from_json().  Move it to src/common/c-conf.c so both get
the lws_stub child handling added in the previous commit, since sai-web can
be configured to serve directly and then owns its own TLS termination and
certs, exactly the case lws-cert-dist-client is for.

Rearrange who fills in the context creation info: main() now zeroes it and
passes argc / argv through lws_cmdline_option_handle_builtin() before
anything logs, then the helper only adds to it.  The builtin handler sets
the log level from -d, so the hand-rolled -d parsing in both mains goes,
and it also fills in info->lws_stub, which the helper keys the stub-mode
adjustments off instead of taking argc / argv itself.  Doing it in that
order means the helper's former memset can no longer wipe what the
commandline set.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
diff --git a/src/common/c-conf.c b/src/common/c-conf.c new file mode 100644 index 0000000..5cd11c9 --- /dev/null +++ b/src/common/c-conf.c @@ -0,0 +1,111 @@ +/* + * Sai - ./src/common/c-conf.c + * + * Copyright (C) 2019 - 2026 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Create an lws context and its vhosts from an lwsws-style JSON config dir, + * shared by the sai daemons that are configured that way (sai-server, + * sai-web). + */ + +#include "include/private.h" + +#include <string.h> + +#define SAI_CONFIG_STRING_SIZE (16 * 1024) + +/* + * The caller owns \p info: it has already zeroed it and passed argc / argv + * through lws_cmdline_option_handle_builtin(), so anything the commandline + * set in there (log level, argc / argv, lws_stub, option flags) is still in + * place when we get it. We only add to it. + */ + +struct lws_context * +sai_lws_context_from_json(const char *config_dir, + struct lws_context_creation_info *info, + const struct lws_protocols **pprotocols, + const char *jpol) +{ + int cs_len = SAI_CONFIG_STRING_SIZE - 1; + struct lws_context *context; + char *cs, *config_strings; + + cs = config_strings = malloc(SAI_CONFIG_STRING_SIZE); + if (!config_strings) { + lwsl_err("Unable to allocate config strings heap\n"); + + return NULL; + } + + info->external_baggage_free_on_destroy = config_strings; + info->pt_serv_buf_size = 8192; + info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_EXPLICIT_VHOSTS | + LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | + LWS_SERVER_OPTION_VALIDATE_UTF8; + info->pss_policies_json = jpol; + + if (info->lws_stub) { + /* + * We were re-exec'd with --lws-stub=<name> by an lws plugin + * (eg, lws-cert-dist-client) that needs a privileged helper. + * We exist only to host that plugin's protocol, on the + * stub-dummy vhost lwsws_get_config_vhosts() creates instead + * of parsing our real vhosts. So none of our own protocols + * or the SS policy should come up, and we must keep our + * privileges rather than dropping to the configured uid / gid, + * since the stub's whole purpose is to do the privileged work. + */ + lwsl_notice("%s: lws stub child '%s'\n", __func__, + info->lws_stub); + info->options |= LWS_SERVER_OPTION_VH_SKIP_PRIV_DROP; + info->pss_policies_json = NULL; + pprotocols = NULL; + } + + lwsl_notice("Using config dir: \"%s\"\n", config_dir); + + /* + * first go through the config for creating the outer context + */ + if (lwsws_get_config_globals(info, config_dir, &cs, &cs_len)) + goto init_failed; + + context = lws_create_context(info); + if (context == NULL) { + /* config_strings freed as 'external baggage' */ + return NULL; + } + + info->pprotocols = pprotocols; + + if (lwsws_get_config_vhosts(context, info, config_dir, &cs, &cs_len)) { + lwsl_err("%s: sai_lws_context_from_json failed\n", __func__); + lws_context_destroy(context); + + return NULL; + } + + return context; + +init_failed: + free(config_strings); + + return NULL; +} diff --git a/src/common/include/private.h b/src/common/include/private.h index ef1ce91..f2b1f1f 100644 --- a/src/common/include/private.h +++ b/src/common/include/private.h @@ -1010,3 +1010,13 @@ sai_event_db_delete_database(const char *sqlite3_path_lhs, const char *event_uui int sai_sqlite3_statement(struct sqlite3 *pdb, const char *cmd, const char *desc); +/* + * c-conf.c: create the context and vhosts from an lwsws-style config dir. + * info must be zeroed and passed through lws_cmdline_option_handle_builtin() + * by the caller first. + */ +struct lws_context * +sai_lws_context_from_json(const char *config_dir, + struct lws_context_creation_info *info, + const struct lws_protocols **pprotocols, + const char *jpol); diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 66982a4..0c13b61 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -4,7 +4,6 @@ set(CPACK_DEBIAN_SERVER_PACKAGE_NAME ${SUB}) set(SRCS s-sai.c - s-conf.c s-notification.c s-comms.c s-helpers.c @@ -18,6 +17,7 @@ set(SRCS s-resource.c s-watcher.c ../common/c-utils.c + ../common/c-conf.c ../common/c-sqlite3.c ../common/struct-metadata.c ) diff --git a/src/server/s-conf.c b/src/server/s-conf.c deleted file mode 100644 index 66f0df3..0000000 --- a/src/server/s-conf.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Sai server src/server/conf.c - * - * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -#include <libwebsockets.h> -#include <string.h> -#include <signal.h> -#include <time.h> - -#define SAI_CONFIG_STRING_SIZE (16 * 1024) - -extern struct lws_context *context; - -struct lws_context * -sai_lws_context_from_json(const char *config_dir, - struct lws_context_creation_info *info, - const struct lws_protocols **pprotocols, - const char *jpol, int argc, const char **argv) -{ - int cs_len = SAI_CONFIG_STRING_SIZE - 1; - struct lws_context *context; - char *cs, *config_strings; - - cs = config_strings = malloc(SAI_CONFIG_STRING_SIZE); - if (!config_strings) { - lwsl_err("Unable to allocate config strings heap\n"); - - return NULL; - } - - memset(info, 0, sizeof(*info)); - - info->external_baggage_free_on_destroy = config_strings; - info->pt_serv_buf_size = 8192; - info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | - LWS_SERVER_OPTION_VALIDATE_UTF8; - info->pss_policies_json = jpol; - - /* - * Let lws see our commandline: lws_cmdline_option_cx() needs it, and - * it is how lws_stub children (lws plugins that spawn a privileged - * helper by re-exec'ing us with --lws-stub=<name>) are recognized, - * both by the plugins themselves and by lwsws_get_config_vhosts() - */ - lws_cmdline_option_handle_builtin(argc, argv, info); - - if (info->lws_stub) { - /* - * We are a stub child, not a sai-server. We exist only to - * host the plugin protocol that spawned us, on the stub-dummy - * vhost lwsws_get_config_vhosts() creates instead of parsing - * our real vhosts. So none of our own protocols or the SS - * websrv listener should come up, and we must keep our - * privileges rather than dropping to the configured uid / gid, - * since the stub's whole purpose is to do the privileged work. - */ - lwsl_notice("%s: lws stub child '%s'\n", __func__, - info->lws_stub); - info->options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_VH_SKIP_PRIV_DROP; - info->pss_policies_json = NULL; - pprotocols = NULL; - } - - lwsl_notice("Using config dir: \"%s\"\n", config_dir); - - /* - * first go through the config for creating the outer context - */ - if (lwsws_get_config_globals(info, config_dir, &cs, &cs_len)) - goto init_failed; - - context = lws_create_context(info); - if (context == NULL) { - /* config_strings freed as 'external baggage' */ - return NULL; - } - - info->pprotocols = pprotocols; - - if (lwsws_get_config_vhosts(context, info, config_dir, &cs, &cs_len)) { - lwsl_err("%s: sai_lws_context_from_json failed\n", __func__); - lws_context_destroy(context); - - return NULL; - } - - return context; - -init_failed: - free(config_strings); - - return NULL; -} diff --git a/src/server/s-private.h b/src/server/s-private.h index afe20be..1f812b5 100644 --- a/src/server/s-private.h +++ b/src/server/s-private.h @@ -273,11 +273,6 @@ struct vhd { unsigned int viewers_are_present:1; }; -extern struct lws_context * -sai_lws_context_from_json(const char *config_dir, - struct lws_context_creation_info *info, - const struct lws_protocols **pprotocols, - const char *jpol, int argc, const char **argv); extern const struct lws_protocols protocol_ws, protocol_ws_power; int diff --git a/src/server/s-sai.c b/src/server/s-sai.c index 84e328d..0a35bc6 100644 --- a/src/server/s-sai.c +++ b/src/server/s-sai.c @@ -71,23 +71,27 @@ static void sigint_handler(int sig) int main(int argc, const char **argv) { - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; const char *p, *conf = "/etc/sai/server"; struct lws_context_creation_info info; signal(SIGINT, sigint_handler); - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); + /* + * lws owns the generic switches: -d for the log level, and + * --lws-stub=<name> when an lws plugin re-exec'd us as its privileged + * helper. This must come after zeroing info and before anything that + * logs, since it sets the log level as well as filling in info. + */ + memset(&info, 0, sizeof(info)); + lws_cmdline_option_handle_builtin(argc, argv, &info); - lws_set_log_level(logs, NULL); - lwsl_user("Sai Server - Copyright (C) 2019-2025 Andy Green <andy@warmcat.com>\n"); + lwsl_user("Sai Server - Copyright (C) 2019-2026 Andy Green <andy@warmcat.com>\n"); if ((p = lws_cmdline_option(argc, argv, "-c"))) conf = p; context = sai_lws_context_from_json(conf, &info, pprotocols, - default_ss_policy, argc, argv); + default_ss_policy); if (!context) { lwsl_err("lws init failed\n"); return 1; diff --git a/src/web/CMakeLists.txt b/src/web/CMakeLists.txt index f06cbfa..6d2b4bd 100644 --- a/src/web/CMakeLists.txt +++ b/src/web/CMakeLists.txt @@ -4,12 +4,12 @@ set(CPACK_DEBIAN_SERVER_PACKAGE_NAME ${SUB}) set(SRCS w-sai.c - w-conf.c w-comms.c w-artifact.c w-ws-server.c w-ws-browser.c ../common/c-utils.c + ../common/c-conf.c ../common/c-sqlite3.c ../common/struct-metadata.c ) diff --git a/src/web/w-conf.c b/src/web/w-conf.c deleted file mode 100644 index b229ace..0000000 --- a/src/web/w-conf.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Sai server src/server/conf.c - * - * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -#include <libwebsockets.h> -#include <string.h> -#include <signal.h> -#include <time.h> - -#define SAI_CONFIG_STRING_SIZE (16 * 1024) - -struct lws_context * -sai_lws_context_from_json(const char *config_dir, - struct lws_context_creation_info *info, - const struct lws_protocols **pprotocols, - const char *pol) -{ - int cs_len = SAI_CONFIG_STRING_SIZE - 1; - struct lws_context *context; - char *cs, *config_strings; - - cs = config_strings = malloc(SAI_CONFIG_STRING_SIZE); - if (!config_strings) { - lwsl_err("Unable to allocate config strings heap\n"); - - return NULL; - } - - memset(info, 0, sizeof(*info)); - - info->external_baggage_free_on_destroy = config_strings; - info->pt_serv_buf_size = 8192; - info->options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS | - LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | - LWS_SERVER_OPTION_VALIDATE_UTF8; - info->pss_policies_json = pol; - - lwsl_notice("Using config dir: \"%s\"\n", config_dir); - - /* - * first go through the config for creating the outer context - */ - if (lwsws_get_config_globals(info, config_dir, &cs, &cs_len)) - goto init_failed; - - context = lws_create_context(info); - if (context == NULL) { - /* config_strings freed as 'external baggage' */ - return NULL; - } - - info->pprotocols = pprotocols; - - if (lwsws_get_config_vhosts(context, info, config_dir, &cs, &cs_len)) { - lwsl_err("%s: sai_lws_context_from_json failed\n", __func__); - lws_context_destroy(context); - - return NULL; - } - - return context; - -init_failed: - free(config_strings); - - return NULL; -} diff --git a/src/web/w-private.h b/src/web/w-private.h index cb1c24c..fd95d26 100644 --- a/src/web/w-private.h +++ b/src/web/w-private.h @@ -232,11 +232,6 @@ typedef struct saiw_websrv { } saiw_websrv_t; -extern struct lws_context * -sai_lws_context_from_json(const char *config_dir, - struct lws_context_creation_info *info, - const struct lws_protocols **pprotocols, - const char *pol); extern const struct lws_protocols protocol_ws; extern const lws_ss_info_t ssi_saiw_websrv; diff --git a/src/web/w-sai.c b/src/web/w-sai.c index 2a7f740..a85c8d3 100644 --- a/src/web/w-sai.c +++ b/src/web/w-sai.c @@ -66,17 +66,21 @@ static void sigint_handler(int sig) int main(int argc, const char **argv) { - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; const char *p, *conf = "/etc/sai/web"; struct lws_context_creation_info info; signal(SIGINT, sigint_handler); - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); + /* + * lws owns the generic switches: -d for the log level, and + * --lws-stub=<name> when an lws plugin re-exec'd us as its privileged + * helper. This must come after zeroing info and before anything that + * logs, since it sets the log level as well as filling in info. + */ + memset(&info, 0, sizeof(info)); + lws_cmdline_option_handle_builtin(argc, argv, &info); - lws_set_log_level(logs, NULL); - lwsl_user("Sai Web - Copyright (C) 2019-2025 Andy Green <andy@warmcat.com>\n"); + lwsl_user("Sai Web - Copyright (C) 2019-2026 Andy Green <andy@warmcat.com>\n"); if ((p = lws_cmdline_option(argc, argv, "-c"))) conf = p;
Page fetched 0s ago, creation time: 15ms (vhost etag hits: 0%, cache hits: 0%)