diff --git a/src/builder/b-private.h b/src/builder/b-private.h
index f2fee9e..784e676 100644
--- a/src/builder/b-private.h
+++ b/src/builder/b-private.h
@@ -74,67 +74,7 @@ enum {
NSSTATE_FAILED,
};
-struct saib_logproxy {
- char sockpath[128];
- struct sai_nspawn *ns;
- int log_channel_idx;
-};
-
-struct saib_resproxy {
- char sockpath[128];
- struct sai_nspawn *ns;
-};
-
-struct sai_nspawn;
-
-struct sai_nspawn {
- lws_dll2_owner_t chunk_cache;
-
- char inp[512];
- char path[384];
- char pending_mirror_log[128];
-
- /* convenient place to store it */
- struct saib_logproxy slp_control;
- struct saib_logproxy slp[2];
-
- lws_dll2_t list; /* sai_plat owner lists sai_nspawns */
- struct sai_builder *builder;
- struct lws_fsmount fsm;
- struct lws_spawn_piped *lsp;
- sai_task_t *task;
- lws_dll2_owner_t artifact_owner; /* struct artifact_path */
-
- struct lws_threadpool *tp;
- struct lws_threadpool_task *tp_task;
- lws_sorted_usec_list_t sul_cleaner;
- lws_sorted_usec_list_t sul_task_cancel;
-
- sai_plat_t *sp; /* the sai_plat */
- struct sai_plat_server *spm; /* the sai plat / server with the ss / wsi */
-
- size_t chunk_cache_size;
-
- const char *server_name; /* sai-server name who triggered this, eg, 'warmcat' */
- const char *project_name; /* name of the git project, eg, 'libwebsockets' */
- const char *ref; /* remote refname, eg 'server' */
- const char *hash; /* remote hash */
- const char *git_repo_url;
-
- int retcode;
- int instance_idx;
- int mirror_wait_budget;
-
- uint8_t spins;
- uint8_t state; /* NSSTATE_ */
- uint8_t stdcount;
- uint8_t term_budget;
-
- uint8_t finished_when_logs_drained:1;
- uint8_t state_changed:1;
- uint8_t user_cancel:1;
-};
typedef struct sai_mirror_instance {
pthread_mutex_t mut;
diff --git a/src/common/include/private.h b/src/common/include/private.h
index 4a024e7..f020053 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -54,31 +54,8 @@ enum {
SAISPRF_SIGNALLED = 0x4000,
};
-/*
- * Builder is indicating he can't take the task and server should free it up
- * and try another builder.
- */
-
-typedef struct sai_rejection {
- struct lws_dll2 list;
- char host_platform[65];
- char task_uuid[65];
- int ongoing;
- int limit;
-} sai_rejection_t;
-
-/*
- * Master is broadcasting that builders should stop work on the given task,
- * because, eg, the task was reset
- */
-
-typedef struct sai_cancel {
- struct lws_dll2 list;
- char task_uuid[65];
-} sai_cancel_t;
-
-struct sai_event;
+struct sai_nspawn;
typedef struct {
lws_dll2_t list;
@@ -115,6 +92,94 @@ typedef struct {
char told_ongoing;
} sai_task_t;
+typedef struct sai_plat sai_plat_t;
+
+struct saib_logproxy {
+ char sockpath[128];
+ struct sai_nspawn *ns;
+ int log_channel_idx;
+};
+
+struct saib_resproxy {
+ char sockpath[128];
+ struct sai_nspawn *ns;
+};
+
+struct sai_nspawn {
+ lws_dll2_owner_t chunk_cache;
+
+ char inp[512];
+ char path[384];
+ char pending_mirror_log[128];
+
+ /* convenient place to store it */
+ struct saib_logproxy slp_control;
+ struct saib_logproxy slp[2];
+
+ lws_dll2_t list; /* sai_plat owner lists sai_nspawns */
+ struct sai_builder *builder;
+ struct lws_fsmount fsm;
+ struct lws_spawn_piped *lsp;
+ sai_task_t *task;
+
+ lws_dll2_owner_t artifact_owner; /* struct artifact_path */
+
+ struct lws_threadpool *tp;
+ struct lws_threadpool_task *tp_task;
+ lws_sorted_usec_list_t sul_cleaner;
+ lws_sorted_usec_list_t sul_task_cancel;
+
+ sai_plat_t *sp; /* the sai_plat */
+ struct sai_plat_server *spm; /* the sai plat / server with the ss / wsi */
+
+ size_t chunk_cache_size;
+
+ const char *server_name; /* sai-server name who triggered this, eg, 'warmcat' */
+ const char *project_name; /* name of the git project, eg, 'libwebsockets' */
+ const char *ref; /* remote refname, eg 'server' */
+ const char *hash; /* remote hash */
+ const char *git_repo_url;
+
+ int retcode;
+ int instance_idx;
+ int mirror_wait_budget;
+
+ uint8_t spins;
+ uint8_t state; /* NSSTATE_ */
+ uint8_t stdcount;
+ uint8_t term_budget;
+
+ uint8_t finished_when_logs_drained:1;
+ uint8_t state_changed:1;
+ uint8_t user_cancel:1;
+};
+
+/*
+ * Builder is indicating he can't take the task and server should free it up
+ * and try another builder.
+ */
+
+typedef struct sai_rejection {
+ struct lws_dll2 list;
+ char host_platform[65];
+ char task_uuid[65];
+ int ongoing;
+ int limit;
+} sai_rejection_t;
+
+/*
+ * Master is broadcasting that builders should stop work on the given task,
+ * because, eg, the task was reset
+ */
+
+typedef struct sai_cancel {
+ struct lws_dll2 list;
+ char task_uuid[65];
+} sai_cancel_t;
+
+
+struct sai_event;
+
typedef struct sai_event {
struct lws_dll2 list;
lws_dll2_owner_t task_owner;
diff --git a/src/power/p-comms.c b/src/power/p-comms.c
index 8b64718..d551000 100644
--- a/src/power/p-comms.c
+++ b/src/power/p-comms.c
@@ -70,17 +70,9 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
* is powered on.
*/
if (!strcmp(sp->power_on_type, "wol")) {
- uint8_t mac[LWS_ETHER_ADDR_LEN];
-
- if (lws_parse_mac(sp->power_on_mac, mac)) {
- lwsl_user("Failed to parse mac '%s'\n", sp->power_on_mac);
- } else
-
- if (!lws_wol(lws_ss_cx_from_user(pss),
- NULL, mac)) {
- lwsl_user("Failed to WOL '%s'\n", sp->power_on_mac);
- } else
- lwsl_user("Sent WOL to '%s'\n", sp->power_on_mac);
+ lwsl_notice("%s: triggering WOL\n", __func__);
+ write(lws_spawn_get_fd_stdxxx(lsp_wol, 0),
+ sp->power_on_mac, strlen(sp->power_on_mac));
}
if (!strcmp(sp->power_on_type, "tasmota")) {
diff --git a/src/power/p-private.h b/src/power/p-private.h
index dae86da..fc3eb62 100644
--- a/src/power/p-private.h
+++ b/src/power/p-private.h
@@ -91,6 +91,8 @@ struct sai_power {
struct lws_context *context;
struct lws_vhost *vhost;
+ struct sai_nspawn wol_nspawn;
+
lws_sorted_usec_list_t sul_idle;
const char *power_off;
@@ -116,6 +118,7 @@ struct jpargs {
extern struct sai_power power;
extern const lws_ss_info_t ssi_saip_server_link_t, ssi_saip_smartplug_t;
extern const struct lws_protocols protocol_com_warmcat_sai, protocol_ws_power;
+extern struct lws_spawn_piped *lsp_wol;
int
saip_config_global(struct sai_power *power, const char *d);
extern int saip_config(struct sai_power *power, const char *d);
diff --git a/src/power/p-sai.c b/src/power/p-sai.c
index 8bf3775..1949a67 100644
--- a/src/power/p-sai.c
+++ b/src/power/p-sai.c
@@ -50,6 +50,9 @@
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#if defined(__linux__)
#include <unistd.h>
@@ -74,6 +77,7 @@ int getpid(void) { return 0; }
static const char *config_dir = "/etc/sai/power";
static int interrupted;
static lws_state_notify_link_t nl;
+struct lws_spawn_piped *lsp_wol;
struct sai_power power;
@@ -320,6 +324,52 @@ int main(int argc, const char **argv)
lws_context_info_defaults(&info, NULL);
+ if ((p = lws_cmdline_option(argc, argv, "-s"))) {
+ struct lws_context *cx;
+ ssize_t n = 0;
+
+ printf("%s: WOL subprocess generation...\n", __func__);
+
+ cx = lws_create_context(&info);
+ if (!cx) {
+ lwsl_err("%s: failed to create wol cx\n", __func__);
+ return 1;
+ }
+
+ /*
+ * A new process gets started with this option before we drop
+ * privs. This allows us to do WOL with root privs later.
+ *
+ * We just wait until we get an ascii mac on stdin from the main
+ * process indicating the WOL needed.
+ */
+
+ while (n >= 0) {
+ char min[20];
+ uint8_t mac[LWS_ETHER_ADDR_LEN];
+
+ n = read(0, min, sizeof(min) - 1);
+ lwsl_notice("%s: wol process read returned %d\n", __func__, (int)n);
+
+ if (n <= 0)
+ continue;
+
+ min[n] = '\0';
+
+ { int fd = open("/tmp/q", O_CREAT | O_TRUNC | O_RDWR, 0644); write(fd, min, (size_t)(n + 1)); close(fd); }
+
+ if (lws_parse_mac(min, mac)) {
+ lwsl_user("Failed to parse mac '%s'\n", min);
+ } else
+ if (lws_wol(cx, NULL, mac)) {
+ lwsl_user("Failed to WOL '%s'\n", min);
+ } else
+ lwsl_user("Sent WOL to '%s'\n", min);
+ }
+
+ return 0;
+ }
+
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
@@ -336,6 +386,10 @@ int main(int argc, const char **argv)
lws_set_log_level(logs, NULL);
+ lwsl_user("Sai Power - "
+ "Copyright (C) 2019-2025 Andy Green <andy@warmcat.com>\n");
+ lwsl_user(" sai-power [-c <config-file>]\n");
+
#if defined(WIN32)
{
PWSTR wdi = NULL;
@@ -364,14 +418,11 @@ int main(int argc, const char **argv)
* Let's parse the global bits out of the config
*/
- lwsl_user("Sai Power - "
- "Copyright (C) 2019-2025 Andy Green <andy@warmcat.com>\n");
- lwsl_user(" sai-power [-c <config-file>]\n");
-
info.pprotocols = pprotocols;
info.uid = 883;
info.pt_serv_buf_size = 32 * 1024;
info.rlimit_nofile = 20000;
+ info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
signal(SIGINT, sigint_handler);
@@ -392,7 +443,6 @@ int main(int argc, const char **argv)
return 1;
}
-
lwsl_notice("%s: config dir %s\n", __func__, config_dir);
if (saip_config_global(&power, config_dir)) {
lwsl_err("%s: global config failed\n", __func__);
@@ -406,6 +456,29 @@ int main(int argc, const char **argv)
goto bail;
}
+ {
+ struct lws_spawn_piped_info info;
+ char rpath[PATH_MAX];
+ const char * const ea[] = { rpath, "-s", NULL };
+
+ realpath(argv[0], rpath);
+
+ memset(&info, 0, sizeof(info));
+ memset(&power.wol_nspawn, 0, sizeof(power.wol_nspawn));
+
+ info.vh = power.vhost;
+ info.exec_array = ea;
+ info.max_log_lines = 100;
+ info.opaque = (void *)&power.wol_nspawn;
+
+ lsp_wol = lws_spawn_piped(&info);
+ if (!lsp_wol)
+ lwsl_err("%s: wol spawn failed\n", __func__);
+ }
+
+ lws_finalize_startup(power.context);
+
+
while (!lws_service(power.context, 0) && !interrupted)
;