| Author | Andy Green <andy@warmcat.com> 2026-09-21 08:32 UTC | | Committer | Andy Green <andy@warmcat.com> 2026-09-21 08:32 UTC | | Tree | 3e1b33d76d4c40193586f136262d4ca145cd8bc3 Raw Patch | | | server: refuse the comms protocol on more than one vhost | server: refuse the comms protocol on more than one vhost
All the builder, power and task state that sai-web sees over the websrv
control link belongs to the vhd of the vhost the protocol initialized
on, and sais_list_builders() marks a builder online only if it is
connected on that same vhd. So sai-server only supports one vhost
carrying com-warmcat-sai.
With the link on a path-based unix socket, a second vhost with the
protocol (eg, a unix-socket vhost for a front-end proxy that was given
the full pvo set) no longer fails at bind like it did on the abstract
name: lws unlinks the first vhd's socket and binds its own, so sai-web
ends up connected to a vhd with no builders on it. The UI then shows
every builder offline and never sees task progress until a refresh
reads the db, while the builders are busy on the other vhd.
Make the second protocol init a loud failure naming both vhosts
instead, and say in the README that hooks go to the same vhost as the
builders.
|
diff --git a/README.md b/README.md
index c0da922..65b2479 100644
--- a/README.md
+++ b/README.md
@@ -156,6 +156,15 @@ the same on both sides:
"sockpath": "/var/run/sai-websrv",
```
+Only one sai-server vhost may carry the com-warmcat-sai protocol: the
+builders, the hook intake and this link all belong to that one vhost's
+protocol instance. A second vhost with the protocol (eg, a unix-socket vhost
+for a front-end proxy) would bind its own copy of the link on the same path
+and sai-web would see a vhd with no builders on it; sai-server now refuses to
+initialize the protocol on any vhost after the first. Hook notifications go
+to the same vhost the builders use, eg, `http://127.0.0.1:4444/update-hook`
+from a hook on the same host.
+
sai-server binds it during protocol init, before dropping privileges, and lws
gives the socket sai-server's conf `uid`:`gid` with mode 0660 (the same way it
treats any path-based listen socket). So only that user and members of that
diff --git a/src/server/s-comms.c b/src/server/s-comms.c
index 2b1fc36..b97dc2f 100644
--- a/src/server/s-comms.c
+++ b/src/server/s-comms.c
@@ -40,6 +40,16 @@
extern const lws_struct_map_t lsm_schema_sq3_map_event[];
extern const lws_ss_info_t ssi_server;
+/*
+ * The vhost whose protocol instance serves the websrv control link. All the
+ * builder, power and task state sai-web sees over the link belongs to that
+ * one vhd, so sai-server supports exactly one vhost carrying the
+ * com-warmcat-sai protocol: a second one would bind its own link on the same
+ * path, unlinking the first, and sai-web would then be talking to a vhd that
+ * has no builders on it.
+ */
+static struct lws_vhost *sais_link_vhost;
+
typedef enum {
SHMUT_NONE = -1,
SHMUT_HOOK,
@@ -190,6 +200,17 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
vhd->context = lws_get_context(wsi);
vhd->vhost = lws_get_vhost(wsi);
+ if (sais_link_vhost) {
+ lwsl_err("%s: com-warmcat-sai is already active on"
+ " vhost %s; only one sai-server vhost may"
+ " carry it (builders, hooks and the sai-web"
+ " control link all belong to that vhd)."
+ " Remove the protocol from vhost %s\n",
+ __func__, lws_get_vhost_name(sais_link_vhost),
+ lws_get_vhost_name(vhd->vhost));
+ return -1;
+ }
+
if (lws_pvo_get_str(in, "notification-key",
&vhd->notification_key)) {
lwsl_warn("%s: notification_key pvo required\n", __func__);
@@ -394,6 +415,8 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
return -1;
}
+ sais_link_vhost = vhd->vhost;
+
lws_sul_schedule(vhd->context, 0, &vhd->sul_central,
sais_central_cb, 500 * LWS_US_PER_MS);
@@ -403,6 +426,8 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
+ if (vhd && vhd->vhost == sais_link_vhost)
+ sais_link_vhost = NULL;
sais_server_destroy(vhd, &vhd->server);
goto passthru;
|