Author: Andy Green Date: Mon Sep 21 09:32:57 2026 +0100 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;