diff --git a/assets/sai.js b/assets/sai.js
index 45259b5..49d5411 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -1717,6 +1717,22 @@ function post_login_form()
/* stuff that has to be delayed until all the page assets are loaded */
window.addEventListener("load", function() {
+ const lnameInput = document.getElementById("lname");
+ const lpassInput = document.getElementById("lpass");
+
+ function stopClickPropagation(event) {
+ // This is the key. It prevents the click event from
+ // reaching any parent elements.
+ event.stopPropagation();
+ }
+
+ if (lnameInput) {
+ lnameInput.addEventListener("click", stopClickPropagation);
+ }
+
+ if (lpassInput) {
+ lpassInput.addEventListener("click", stopClickPropagation);
+ }
if (document.getElementById("noscript"))
document.getElementById("noscript").display = "none";
diff --git a/src/common/include/private.h b/src/common/include/private.h
index e363331..ae50033 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -400,7 +400,10 @@ typedef struct sai_plat {
struct lws *wsi; /* server side only */
lws_dll2_owner_t env_head;
+ uint64_t uid;
lws_dll2_owner_t loads;
+ int online; /* 1 = connected, 0 = offline */
+ uint64_t last_seen; /* unix time */
int instances;
int ongoing;
@@ -457,6 +460,7 @@ extern const lws_struct_map_t
lsm_schema_json_map_event[1],
lsm_resource[4]
;
+extern const lws_struct_map_t lsm_plat[8];
extern const lws_ss_info_t ssi_said_logproxy;
extern struct lws_ss_handle *ssh[3];
diff --git a/src/common/struct-metadata.c b/src/common/struct-metadata.c
index ff74673..3fb39fc 100644
--- a/src/common/struct-metadata.c
+++ b/src/common/struct-metadata.c
@@ -40,11 +40,14 @@ const lws_struct_map_t lsm_load_report_members[] = {
NULL, lsm_platform_load, "platforms"),
};
-static const lws_struct_map_t lsm_plat[] = {
+const lws_struct_map_t lsm_plat[] = { /* !!! keep extern length in common/include/private.h in sync */
+ LSM_UNSIGNED (sai_event_t, uid, "uid"),
LSM_STRING_PTR (sai_plat_t, name, "name"),
LSM_UNSIGNED (sai_plat_t, ongoing, "ongoing"),
LSM_UNSIGNED (sai_plat_t, instances, "instances"),
LSM_STRING_PTR (sai_plat_t, platform, "platform"),
+ LSM_SIGNED (sai_plat_t, online, "online"),
+ LSM_UNSIGNED (sai_plat_t, last_seen, "last_seen"),
LSM_CARRAY (sai_plat_t, peer_ip, "peer_ip"),
};
@@ -62,6 +65,11 @@ const lws_struct_map_t lsm_schema_map_plat[] = {
"com-warmcat-sai-ba"),
};
+const lws_struct_map_t lsm_schema_sq3_map_plat[] = {
+ LSM_SCHEMA_DLL2 (sai_plat_t, sai_plat_list, NULL, lsm_plat,
+ "builders"),
+};
+
const lws_struct_map_t lsm_event[] = {
LSM_UNSIGNED (sai_event_t, uid, "uid"),
LSM_CARRAY (sai_event_t, repo_name, "repo_name"),
diff --git a/src/server/s-comms.c b/src/server/s-comms.c
index b4e2905..e069cad 100644
--- a/src/server/s-comms.c
+++ b/src/server/s-comms.c
@@ -499,6 +499,16 @@ callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
return -1;
}
+ if (lws_struct_sq3_create_table(vhd->server.pdb,
+ lsm_schema_sq3_map_plat)) {
+ lwsl_err("%s: unable to create builders table\n", __func__);
+ return -1;
+ }
+
+ sai_sqlite3_statement(vhd->server.pdb,
+ "CREATE UNIQUE INDEX IF NOT EXISTS name_idx ON builders (name)",
+ "create builder name index");
+
lwsl_notice("%s: creating server stream\n", __func__);
if (lws_ss_create(vhd->context, 0, &ssi_server, vhd,
@@ -746,28 +756,28 @@ callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
lwsac_free(&pss->query_ac);
lwsl_user("%s: CLOSED builder conn\n", __func__);
- /* remove pss from vhd->builders */
+ /* remove pss from vhd->builders (active connection list) */
lws_dll2_remove(&pss->same);
/*
- * Destroy any the builder-tracking objects that
- * were using this departing connection
+ * Find any builder-tracking objects that were using this departing
+ * connection. Mark them as offline in the database.
+ * Also remove from the in-memory list of active builders.
*/
-
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
vhd->server.builder_owner.head) {
sai_plat_t *cb = lws_container_of(p, sai_plat_t,
sai_plat_list);
+ char q[256];
if (cb->wsi == wsi) {
- /* remove builder object itself from server list */
- cb->wsi = NULL;
+ lwsl_warn("%s: Builder '%s' disconnected. Removing from live list.\n",
+ __func__, cb->name);
+ lws_snprintf(q, sizeof(q), "UPDATE builders SET online=0 WHERE name='%s'", cb->name);
+ sai_sqlite3_statement(vhd->server.pdb, q, "set builder offline");
+
+ /* remove from active in-memory list */
lws_dll2_remove(&cb->sai_plat_list);
- /*
- * free the deserialized builder object,
- * everything he pointed to was overallocated
- * when his deep copy was made
- */
free(cb);
}
diff --git a/src/server/s-private.h b/src/server/s-private.h
index 1ff2a36..1098f26 100644
--- a/src/server/s-private.h
+++ b/src/server/s-private.h
@@ -205,6 +205,9 @@ struct vhd {
sais_t server;
};
+extern const lws_struct_map_t lsm_plat[];
+extern const lws_struct_map_t lsm_schema_sq3_map_plat[];
+
extern struct lws_context *
sai_lws_context_from_json(const char *config_dir,
struct lws_context_creation_info *info,
diff --git a/src/server/s-websrv.c b/src/server/s-websrv.c
index 325149a..2d351bb 100644
--- a/src/server/s-websrv.c
+++ b/src/server/s-websrv.c
@@ -153,7 +153,8 @@ sais_websrv_broadcast(struct lws_ss_handle *hsrv, const char *str, size_t len)
int
sais_list_builders(struct vhd *vhd)
{
- lws_dll2_t *walk = lws_dll2_get_head(&vhd->server.builder_owner);
+ lws_dll2_owner_t dbo;
+ struct lwsac *ac = NULL;
char *p = vhd->json_builders, *end = p + sizeof(vhd->json_builders),
subsequent = 0;
lws_struct_serialize_t *js;
@@ -161,13 +162,21 @@ sais_list_builders(struct vhd *vhd)
size_t w;
int n;
- lwsl_err("%s: entry\n", __func__);
+ /*
+ * Query the database for ALL builders, online and offline,
+ * sorted by name.
+ */
+ if (lws_struct_sq3_deserialize(vhd->server.pdb, NULL, "name ",
+ lsm_schema_sq3_map_plat, &dbo, &ac, 0, 100)) {
+ lwsl_err("%s: Failed to query builders from DB\n", __func__);
+ return 1;
+ }
p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
"{\"schema\":\"sai-builders\","
"\"platforms\":[");
- while (end - p > 512 && walk) {
+ lws_start_foreach_dll(struct lws_dll2 *, walk, dbo.head) {
b = lws_container_of(walk, sai_plat_t, sai_plat_list);
@@ -176,8 +185,8 @@ sais_list_builders(struct vhd *vhd)
LWS_ARRAY_SIZE(lsm_schema_map_plat_simple),
0, b);
if (!js) {
- lwsl_err("%s: failed at json serialize create\n", __func__);
- return 1;
+ lwsl_err("%s: json serialize create failed\n", __func__);
+ goto bail;
}
if (subsequent)
*p++ = ',';
@@ -189,27 +198,27 @@ sais_list_builders(struct vhd *vhd)
lws_struct_json_serialize_destroy(&js);
if (n == LSJS_RESULT_ERROR) {
- lwsl_err("%s: json serialize error\n", __func__);
- return 1;
+ lwsl_err("%s: json serialize failed\n", __func__);
+ goto bail;
}
+ } lws_end_foreach_dll(walk);
- walk = walk->next;
- if (!walk) {
-
- /* end of the list of builders */
-
- p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "]}");
+ /* end of the list of builders */
- lwsl_ss_notice(vhd->h_ss_websrv, "sai-server's WEB daemon server broadcasting to all WEB daemons: %s\n", vhd->json_builders);
+ p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "]}");
- sais_websrv_broadcast(vhd->h_ss_websrv,
- vhd->json_builders,
- lws_ptr_diff_size_t(p, vhd->json_builders));
+ /*
+ * This is the SERVER's WEB daemon server, broadcasting to all connected
+ * clients (the WEB daemons)... the list of BUILDERS
+ */
+ sais_websrv_broadcast(vhd->h_ss_websrv, vhd->json_builders,
+ lws_ptr_diff_size_t(p, vhd->json_builders));
- return 0;
- }
- }
+ lwsac_free(&ac);
+ return 0;
+bail:
+ lwsac_free(&ac);
return 1;
}
diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c
index 7ff3ed0..9de422e 100644
--- a/src/server/s-ws-builder.c
+++ b/src/server/s-ws-builder.c
@@ -29,6 +29,21 @@
#include "s-private.h"
+typedef struct {
+ int count;
+} count_ctx_t;
+
+#if 0
+static int
+online_builder_count_cb(void *priv, int cols, char **cv, char **cn)
+{
+ count_ctx_t *ctx = (count_ctx_t *)priv;
+ ctx->count++;
+ lwsl_err("%s: FOUND an online builder in DB: %s\n", __func__, cv[0]);
+ return 0;
+}
+#endif
+
enum sai_overview_state {
SOS_EVENT,
SOS_TASKS,
@@ -329,107 +344,85 @@ handle:
switch (pss->a.top_schema_index) {
case SAIM_WSSCH_BUILDER_PLATS:
-
/*
* builder is sending us an array of platforms it provides us
*/
pss->u.o = (sai_plat_owner_t *)pss->a.dest;
- lwsl_notice("%s: SERVER: seen incoming platform list: count %d\n", __func__,
- pss->u.o->plat_owner.count);
-
lws_start_foreach_dll(struct lws_dll2 *, pb,
pss->u.o->plat_owner.head) {
build = lws_container_of(pb, sai_plat_t, sai_plat_list);
+ sai_plat_t *live_cb;
lwsl_notice("%s: seeing plat %s\n", __func__, build->name);
/*
- * ... so is this one a new guy?
+ * Step 1: Upsert this platform into the persistent database.
*/
-
- cb = sais_builder_from_uuid(vhd, build->name);
- if (!cb) {
- char *cp;
-
- /*
- * We need to make a persistent, deep, copy of
- * the (from JSON) builder object representing
- * this client.
- *
- * "platform" is eg "linux-ubuntu-bionic-arm64"
- * and "name" is "hostname.<platform>".
- */
-
- if (!build->name || !build->platform) {
- lwsl_err("%s: missing build '%s'/hostname '%s'\n",
- __func__,
- build->name ? build->name : "null",
- build->platform ? build->platform : "null");
- return -1;
- }
-
- cb = malloc(sizeof(*cb) +
- strlen(build->name) + 1 +
- strlen(build->platform) + 1);
-
- memset(cb, 0, sizeof(*cb));
- cp = (char *)&cb[1];
-
- memcpy(cp, build->name, strlen(build->name) + 1);
- cb->name = cp;
- cp += strlen(build->name) + 1;
-
- memcpy(cp, build->platform, strlen(build->platform) + 1);
- cb->platform = cp;
- cp += strlen(build->platform) + 1;
-
- cb->ongoing = build->ongoing;
- cb->instances = build->instances;
-
- cb->wsi = pss->wsi;
- pss->announced = 0;
-
- /* Then attach the copy to the server in the vhd
- */
- lws_dll2_add_tail(&cb->sai_plat_list,
- &vhd->server.builder_owner);
+ build->online = 1;
+ build->last_seen = (uint64_t)lws_now_secs();
+ lws_strncpy(build->peer_ip, pss->peer_ip, sizeof(build->peer_ip));
+ if (lws_struct_sq3_upsert(vhd->server.pdb, "builders", lsm_plat,
+ LWS_ARRAY_SIZE(lsm_plat), build, "name")) {
+ lwsl_err("%s: Failed to upsert builder %s\n",
+ __func__, build->name);
}
/*
- * It's a reconnect, update connection-specific things
+ * Step 2: Update the long-lived, malloc'd in-memory list.
*/
-
- cb->wsi = pss->wsi;
-
- if (pss->peer_ip[0])
- lws_strncpy(cb->peer_ip, pss->peer_ip, sizeof(cb->peer_ip));
-
- /*
- * Even if he's not new, we should use his updated info about
- * builder load
- */
-
- cb->ongoing = build->ongoing;
- cb->instances = build->instances;
-
- lwsl_notice("%s: builder %s reports load %d/%d\n",
- __func__, cb->name, cb->ongoing,
- cb->instances);
-
+ live_cb = sais_builder_from_uuid(vhd, build->name);
+ if (live_cb) {
+ /* Already exists (reconnect), just update dynamic info */
+ live_cb->wsi = pss->wsi;
+ live_cb->ongoing = 0; /* Reset ongoing task count on connect */
+ lws_strncpy(live_cb->peer_ip, pss->peer_ip, sizeof(live_cb->peer_ip));
+ } else {
+ /* New builder, create a deep-copied, malloc'd object */
+ size_t nlen = strlen(build->name) + 1;
+ size_t plen = strlen(build->platform) + 1;
+ live_cb = malloc(sizeof(*live_cb) + nlen + plen);
+ if (live_cb) {
+ char *p_str = (char *)(live_cb + 1);
+ memset(live_cb, 0, sizeof(*live_cb));
+ live_cb->name = p_str;
+ memcpy(p_str, build->name, nlen);
+ live_cb->platform = p_str + nlen;
+ memcpy(p_str + nlen, build->platform, plen);
+ live_cb->instances = build->instances;
+ live_cb->wsi = pss->wsi;
+ lws_strncpy(live_cb->peer_ip, pss->peer_ip, sizeof(live_cb->peer_ip));
+ lws_dll2_add_tail(&live_cb->sai_plat_list, &vhd->server.builder_owner);
+ }
+ }
} lws_end_foreach_dll(pb);
+ /* The lwsac from the parsed message is now completely disposable */
lwsac_free(&pss->a.ac);
-
/*
- * look if we should offer the builder a task, given the
- * platforms he's offering
- */
-
- if (sais_allocate_task(vhd, pss, cb, cb->platform) < 0)
- goto bail;
+ * Now, iterate through the in-memory list of online builders and
+ * try to allocate a task for each platform that belongs to the
+ * builder that just connected.
+ */
+ lws_start_foreach_dll(struct lws_dll2 *, p, vhd->server.builder_owner.head) {
+ cb = lws_container_of(p, sai_plat_t, sai_plat_list);
+ if (cb->wsi == pss->wsi) {
+ /* This platform belongs to the connection that sent the message */
+ if (sais_allocate_task(vhd, pss, cb, cb->platform) < 0)
+ goto bail;
+ }
+ } lws_end_foreach_dll(p);
+
+ lws_start_foreach_dll(struct lws_dll2 *, p, vhd->server.builder_owner.head) {
+ cb = lws_container_of(p, sai_plat_t, sai_plat_list);
+ if (cb->wsi == pss->wsi) {
+ /* This platform belongs to the connection that sent the message */
+ if (sais_allocate_task(vhd, pss, cb, cb->platform) < 0)
+ goto bail;
+ }
+ } lws_end_foreach_dll(p);
/*
* If we did allocate a task in pss->a.ac, responsibility of
@@ -495,6 +488,7 @@ bail:
rej = (sai_rejection_t *)pss->a.dest;
+ rej->host_platform[sizeof(rej->host_platform) - 1] = '\0';
cb = sais_builder_from_uuid(vhd, rej->host_platform);
if (!cb) {
lwsl_info("%s: unknown builder %s rejecting\n",