diff --git a/CMakeLists.txt b/CMakeLists.txt
index 338079f..544fff3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -120,6 +120,21 @@ if (LWS_OPENSSL_INCLUDE_DIRS)
set(CMAKE_C_FLAGS "-I\"${LWS_OPENSSL_INCLUDE_DIRS}\" ${CMAKE_C_FLAGS}")
endif()
+find_package(OpenSSL)
+if (OPENSSL_FOUND)
+ include_directories("${OPENSSL_INCLUDE_DIR}")
+endif()
+
+if (APPLE AND EXISTS "/opt/homebrew/opt/openssl/include")
+ include_directories("/opt/homebrew/opt/openssl/include")
+ link_directories("/opt/homebrew/opt/openssl/lib")
+endif()
+
+if (APPLE AND EXISTS "/opt/homebrew/opt/openssl@3/include")
+ include_directories("/opt/homebrew/opt/openssl@3/include")
+ link_directories("/opt/homebrew/opt/openssl@3/lib")
+endif()
+
set(CMAKE_C_FLAGS "-DBUILD_INFO=\\\"${LWS_BUILD_HASH}\\\" ${CMAKE_C_FLAGS}")
diff --git a/assets/sai.js b/assets/sai.js
index 65c30e0..df4e768 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -1923,6 +1923,20 @@ function ws_open_sai()
if (platformsArray) {
last_builder_list = platformsArray;
+
+ /* Ensure PCONs exist in topology even if omitted by the server payload */
+ last_builder_list.forEach(b => {
+ if (b.pcon && !pcon_topology[b.pcon]) {
+ pcon_topology[b.pcon] = {
+ name: b.pcon,
+ on: 1, /* Default to on so it shows green if unknown */
+ type: "auto-discovered",
+ depends_on: "",
+ children: []
+ };
+ }
+ });
+
const container = document.getElementById("sai_builders");
if (container) renderPconHierarchy(container);
}
@@ -2148,7 +2162,7 @@ function ws_open_sai()
const url_task_uuid = urlParams.get('task');
if (url_task_uuid === jso.t.uuid &&
- document.getElementById("sai_sticky"))
+ document.getElementById("sai_sticky")) {
document.getElementById("sai_sticky").innerHTML =
"<div class=\"taskinfo\" id=\"taskinfo-" +
san(jso.t.uuid) + "\">" +
@@ -2156,25 +2170,26 @@ function ws_open_sai()
"</div>";
- s = "<table><td colspan=\"3\"><pre><table class=\"scrollogs\"><tr>" +
- "<td class=\"atop\">" +
- "<div id=\"dlogsn\" class=\"dlogsn\">" + lines + "</div></td>" +
- "<td class=\"atop\">" +
- "<div id=\"dlogst\" class=\"dlogst\">" + times + "</div></td>" +
- "<td class=\"atop\"><div id=\"dlogs\" class=\"dlogs\">" +
- "<span id=\"logs\" class=\"nowrap\">" + logs +
- "</span>"+
- "</div></td></tr></table></pre>";
+ s = "<table><td colspan=\"3\"><pre><table class=\"scrollogs\"><tr>" +
+ "<td class=\"atop\">" +
+ "<div id=\"dlogsn\" class=\"dlogsn\">" + lines + "</div></td>" +
+ "<td class=\"atop\">" +
+ "<div id=\"dlogst\" class=\"dlogst\">" + times + "</div></td>" +
+ "<td class=\"atop\"><div id=\"dlogs\" class=\"dlogs\">" +
+ "<span id=\"logs\" class=\"nowrap\">" + logs +
+ "</span>"+
+ "</div></td></tr></table></pre>";
- if (document.getElementById("sai_overview")) {
- document.getElementById("sai_overview").innerHTML = s;
- logs_pending = times_pending = lines_pending = "";
+ if (document.getElementById("sai_overview")) {
+ document.getElementById("sai_overview").innerHTML = s;
+ logs_pending = times_pending = lines_pending = "";
- if (document.getElementById("esr-" + jso.e.uuid))
- document.getElementById("esr-" + jso.e.uuid).innerHTML =
- sai_event_summary_render(jso, now_ut, 1);
+ if (document.getElementById("esr-" + jso.e.uuid))
+ document.getElementById("esr-" + jso.e.uuid).innerHTML =
+ sai_event_summary_render(jso, now_ut, 1);
- }
+ }
+ }
update_summary_and_progress(jso.e.uuid);
if (document.getElementById("rebuild-" + san(jso.t.uuid))) {
@@ -2206,9 +2221,12 @@ function ws_open_sai()
document.getElementById("dlogsn").innerHTML = "";
document.getElementById("dlogst").innerHTML = "";
- document.getElementById("logs").innerHTML = "";
+ document.getElementById("dlogs").innerHTML = "<span id=\"logs\" class=\"nowrap\"></span>";
lines = times = logs = "";
lines_pending = times_pending = logs_pending = "";
+ segment_stack = [];
+ seg_counter = 0;
+ window.held_start_line = null;
logAnsiState = {};
tfirst = 0;
lli = 1;
@@ -2379,61 +2397,92 @@ function ws_open_sai()
default: s_logs = "<span class=\"tty1\">" + s + "</span>"; break;
}
+ var eval_line = text_line;
+ if (window.pending_log_line && idx === 0) {
+ eval_line = window.pending_log_line + text_line;
+ }
+
+ if (has_nl === '') {
+ window.pending_log_line = eval_line;
+ } else if (idx === 0) {
+ window.pending_log_line = "";
+ }
+
var skip_push = false;
var skip_render = false;
- var match_fail = jso.channel === 1 ? text_line.match(/Test\s+#(\d+):\s+.*(Failed|\*\*\*)/i) : null;
- var is_fail = match_fail || (jso.channel === 1 && /test failed/i.test(text_line));
+ var match_fail = (jso.channel === 1 || jso.channel === 2) ? eval_line.match(/Test\s+#(\d+):\s+.*(Failed|\*\*\*|Timeout)/i) : null;
+ var is_fail = match_fail || ((jso.channel === 1 || jso.channel === 2) && /test failed/i.test(eval_line));
- if (jso.channel === 1) {
+ if (jso.channel === 1 || jso.channel === 2) {
if (window.held_start_line) {
if (is_fail) {
+ var is_same_test = false;
+ if (match_fail) {
+ var start_match = window.held_start_line.text.match(/Start\s+(\d+):/i);
+ if (start_match && start_match[1] === match_fail[1]) {
+ is_same_test = true;
+ }
+ }
+ // If the failing test isn't the one that just started, it must be running in parallel.
+ // Flush the unrelated valid 'Start' line out into the parent CTest boundary first.
+ if (!is_same_test) {
+ logs += window.held_start_line.s_logs; logs_pending += window.held_start_line.s_logs;
+ if (window.held_start_line.li) {
+ lines += window.held_start_line.en; lines_pending += window.held_start_line.en;
+ times += window.held_start_line.tn; times_pending += window.held_start_line.tn;
+ }
+ window.held_start_line = null;
+ }
+
while (segment_stack.length > 1) pop_segment();
- push_segment(text_line, true);
+ push_segment(eval_line, true);
skip_push = true;
}
- logs += window.held_start_line.s_logs; logs_pending += window.held_start_line.s_logs;
- if (window.held_start_line.li) {
- lines += window.held_start_line.en; lines_pending += window.held_start_line.en;
- times += window.held_start_line.tn; times_pending += window.held_start_line.tn;
+ if (window.held_start_line) {
+ logs += window.held_start_line.s_logs; logs_pending += window.held_start_line.s_logs;
+ if (window.held_start_line.li) {
+ lines += window.held_start_line.en; lines_pending += window.held_start_line.en;
+ times += window.held_start_line.tn; times_pending += window.held_start_line.tn;
+ }
+ window.held_start_line = null;
}
- window.held_start_line = null;
}
- if (/^\s*Start\s+\d+:/i.test(text_line)) {
- window.held_start_line = { text: text_line, s_logs: s_logs, en: en, tn: tn, li: li };
+ if (/^\s*Start\s+\d+:/i.test(eval_line)) {
+ window.held_start_line = { text: eval_line, s_logs: s_logs, en: en, tn: tn, li: li };
skip_render = true;
}
}
if (jso.channel === 3) {
- if (/^>saib>\s+Starting task step/.test(text_line)) {
+ if (/^>saib>\s+Starting task step/.test(eval_line)) {
while (segment_stack.length > 0) pop_segment();
- push_segment(text_line, true);
- } else if (/^>saib>\s+Step \d+:/.test(text_line) && segment_stack.length > 0) {
+ push_segment(eval_line, true);
+ } else if (/^>saib>\s+Step \d+:/.test(eval_line) && segment_stack.length > 0) {
var pseg = segment_stack[0];
var phdr = document.getElementById("hdr-seg-" + pseg.id);
- if (phdr) phdr.querySelector('.seg-title').innerText = text_line;
+ if (phdr) phdr.querySelector('.seg-title').innerText = eval_line;
} else {
while (segment_stack.length > 1) pop_segment();
}
}
- if (jso.channel === 1) {
+ if (jso.channel === 1 || jso.channel === 2) {
if (skip_push) {
// Fold logic successfully handled during lookahead execution
- } else if (match_fail || /test failed/i.test(text_line)) {
+ } else if (match_fail || /test failed/i.test(eval_line)) {
while (segment_stack.length > 1) pop_segment();
- push_segment(text_line, true);
- } else if (/^\d+% tests passed/i.test(text_line) || /Total Test time/i.test(text_line) || /The following tests FAILED:/i.test(text_line) || /Errors while running CTest/i.test(text_line)) {
+ push_segment(eval_line, true);
+ } else if (/^\d+% tests passed/i.test(eval_line) || /Total Test time/i.test(eval_line) || /The following tests FAILED:/i.test(eval_line) || /Errors while running CTest/i.test(eval_line)) {
while (segment_stack.length > 1) pop_segment();
- } else if (/^\d+\/\d+\s+Test\s+#\d+:/i.test(text_line)) {
+ } else if (/^\d+\/\d+\s+Test\s+#\d+:/i.test(eval_line)) {
while (segment_stack.length > 1) pop_segment();
}
}
- var text_lower = text_line.toLowerCase();
- if (text_lower.includes("error:") || text_lower.includes("fatal:") || /error\s\d+:/.test(text_lower)) {
+ var text_lower = eval_line.toLowerCase();
+ if (is_fail || text_lower.includes("error:") || text_lower.includes("fatal:") || /error\s\d+:/.test(text_lower)) {
for (var si = 0; si < segment_stack.length; si++) {
var sobj = segment_stack[si];
sobj.error_count++;
diff --git a/src/builder/b-nspawn.c b/src/builder/b-nspawn.c
index 7a7f42a..b11ab5c 100644
--- a/src/builder/b-nspawn.c
+++ b/src/builder/b-nspawn.c
@@ -523,11 +523,15 @@ saib_spawn_script(struct sai_nspawn *ns)
ns->inp);
#endif
- lws_strncpy(one_step, ns->task->script, sizeof(one_step));
+ n = lws_snprintf(one_step, sizeof(one_step), "%s\n", ns->task->script);
+ if (n < 1)
+ return -1;
- if (saib_log_chunk_create(op->ns, one_step, strlen(one_step), 3))
+ if (saib_log_chunk_create(ns, one_step, strlen(one_step), 3))
return -1;
+ one_step[n - 1] = '\0'; /* trim off the CR; n is always at least 1 */
+
#if defined(WIN32)
if (_sopen_s(&fd, ns->script_path, _O_CREAT | _O_TRUNC | _O_WRONLY,
_SH_DENYNO, _S_IWRITE))
diff --git a/src/builder/b-sai.c b/src/builder/b-sai.c
index 501ba17..ac7f12d 100644
--- a/src/builder/b-sai.c
+++ b/src/builder/b-sai.c
@@ -384,6 +384,13 @@ app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
return 1;
}
+ if (builder.power_controller_name) {
+ lws_start_foreach_dll(struct lws_dll2 *, d, builder.sai_plat_owner.head) {
+ struct sai_plat *sp = lws_container_of(d, struct sai_plat, sai_plat_list);
+ sp->pcon = builder.power_controller_name;
+ } lws_end_foreach_dll(d);
+ }
+
/*
* For each platform...
*/
@@ -547,6 +554,7 @@ saib_app_run(int argc, const char **argv)
return 1;
}
+
/*
* We need to sample the true uid / gid we should use inside
* the mountpoint for sai:nobody or sai:sai, by looking at
@@ -642,14 +650,12 @@ saib_app_run(int argc, const char **argv)
/* ... and our vhost... */
- builder.context = lws_create_context(&info);
- if (!builder.context) {
+ builder.vhost = lws_create_vhost(builder.context, &info);
+ if (!builder.vhost) {
lwsl_err("lws init failed\n");
return 1;
}
- /* ... and our vhost... */
-
while (!lws_service(builder.context, 0) && !interrupted)
;
diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c
index 1738aa6..070fddb 100644
--- a/src/server/s-ws-builder.c
+++ b/src/server/s-ws-builder.c
@@ -775,11 +775,15 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b
char q[1024];
lws_snprintf(q, sizeof(q),
- "INSERT INTO builders (name, platform, last_seen, peer_ip, sai_hash, lws_hash, windows) "
- "VALUES ('%s', '%s', %llu, '%s', '%s', '%s', %d) "
- "ON CONFLICT(name) DO UPDATE SET last_seen=excluded.last_seen, "
+ "INSERT INTO builders (name, platform, pcon, last_seen, peer_ip, sai_hash, lws_hash, windows) "
+ "VALUES ('%s', '%s', %s%s%s, %llu, '%s', '%s', '%s', %d) "
+ "ON CONFLICT(name) DO UPDATE SET pcon=COALESCE(NULLIF(excluded.pcon, ''), pcon), last_seen=excluded.last_seen, "
"peer_ip=excluded.peer_ip, sai_hash=excluded.sai_hash, lws_hash=excluded.lws_hash",
- build->name, build->platform, (unsigned long long)lws_now_secs(),
+ build->name, build->platform,
+ build->pcon ? "'" : "NULL",
+ build->pcon ? build->pcon : "",
+ build->pcon ? "'" : "",
+ (unsigned long long)lws_now_secs(),
pss->peer_ip, build->sai_hash, build->lws_hash, build->windows);
if (sai_sqlite3_statement(vhd->server.pdb, q, "upsert builder"))
@@ -801,7 +805,7 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b
lws_strncpy(host, build->name, sizeof(host));
lws_snprintf(q, sizeof(q),
- "UPDATE builders SET pcon = (SELECT pcon_name FROM pcon_builders WHERE builder_name = '%s') "
+ "UPDATE builders SET pcon = COALESCE((SELECT pcon_name FROM pcon_builders WHERE builder_name = '%s'), pcon) "
"WHERE name = '%s' OR name LIKE '%s.%%'",
host, build->name, build->name);
lwsl_notice("%s: Syncing pcon for host '%s' (plat '%s'): %s\n", __func__, host, build->name, q);
diff --git a/src/server/s-ws-web.c b/src/server/s-ws-web.c
index 6fe1033..0584aa5 100644
--- a/src/server/s-ws-web.c
+++ b/src/server/s-ws-web.c
@@ -179,29 +179,70 @@ sais_list_pcons(struct vhd *vhd)
lws_wsmsg_info_t info;
size_t w;
lws_struct_json_serialize_result_t r;
+ char pcon_query[128];
+ sqlite3_stmt *pcon_stmt;
sai_power_managed_builders_t pmb;
sai_power_controller_t *pc;
memset(&pmb, 0, sizeof(pmb));
- /* Query PCONs from DB using schema map */
- if (lws_struct_sq3_deserialize(vhd->server.pdb, NULL, "name ",
- lsm_schema_sq3_map_power_controller,
- &pmb.power_controllers, &ac, 0, 100)) {
- /* It's okay if empty */
+ /*
+ * We use raw sqlite queries for PCON deserialization to bypass
+ * complex inner/outer lws_struct_sq3 memory management edge cases that truncates rows.
+ */
+ lws_snprintf(pcon_query, sizeof(pcon_query),
+ "SELECT name, type, depends_on, state FROM power_controllers ORDER BY name LIMIT 100");
+
+ if (sqlite3_prepare_v2(vhd->server.pdb, pcon_query, -1, &pcon_stmt, NULL) == SQLITE_OK) {
+ while (sqlite3_step(pcon_stmt) == SQLITE_ROW) {
+ const char *name = (const char *)sqlite3_column_text(pcon_stmt, 0);
+ if (name) {
+ pc = lwsac_use_zero(&ac, sizeof(*pc), 2048);
+ if (pc) {
+ lws_strncpy(pc->name, name, sizeof(pc->name));
+
+ const char *type = (const char *)sqlite3_column_text(pcon_stmt, 1);
+ if (type) lws_strncpy(pc->type, type, sizeof(pc->type));
+
+ const char *depends_on = (const char *)sqlite3_column_text(pcon_stmt, 2);
+ if (depends_on) lws_strncpy(pc->depends_on, depends_on, sizeof(pc->depends_on));
+
+ pc->on = (char)sqlite3_column_int(pcon_stmt, 3);
+
+ lws_dll2_add_tail(&pc->list, &pmb.power_controllers);
+ }
+ }
+ }
+ sqlite3_finalize(pcon_stmt);
}
/* Iterate PCONs and populate controlled builders */
lws_start_foreach_dll(struct lws_dll2 *, d, pmb.power_controllers.head) {
pc = lws_container_of(d, sai_power_controller_t, list);
- char filter[128];
-
- /* Map 'builder_name' column to 'name' field in struct */
- lws_snprintf(filter, sizeof(filter), "and pcon_name = '%s'", pc->name);
- lws_struct_sq3_deserialize(vhd->server.pdb, filter, "builder_name ",
- lsm_schema_sq3_map_controlled_builder,
- &pc->controlled_builders_owner, &ac, 0, 100);
+ char query[256];
+ sqlite3_stmt *stmt;
+
+ /* Manually query builders mapped to this pcon to avoid LWS struct nested 0-row ac free bug */
+ lws_snprintf(query, sizeof(query),
+ "SELECT builder_name FROM pcon_builders WHERE pcon_name = '%s' "
+ "ORDER BY builder_name LIMIT 100", pc->name);
+
+ if (sqlite3_prepare_v2(vhd->server.pdb, query, -1, &stmt, NULL) == SQLITE_OK) {
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ const char *bname = (const char *)sqlite3_column_text(stmt, 0);
+ if (bname) {
+ sai_controlled_builder_t *c =
+ lwsac_use_zero(&ac, sizeof(*c), 2048);
+ if (c) {
+ lws_strncpy(c->name, bname, sizeof(c->name));
+ lws_dll2_add_tail(&c->list,
+ &pc->controlled_builders_owner);
+ }
+ }
+ }
+ sqlite3_finalize(stmt);
+ }
} lws_end_foreach_dll(d);
diff --git a/src/web/w-comms.c b/src/web/w-comms.c
index 3bdf56c..d6fc7a2 100644
--- a/src/web/w-comms.c
+++ b/src/web/w-comms.c
@@ -539,6 +539,11 @@ http_resp:
lws_dll2_remove(&pss->subs_list);
lws_sul_cancel(&pss->sul_logcache);
+ for (n = 0; n < 3; n++) {
+ if (pss->last_bps[n])
+ free(pss->last_bps[n]);
+ }
+
lwsac_free(&pss->logs_ac);
break;
diff --git a/src/web/w-private.h b/src/web/w-private.h
index 54b1b00..bd27cd7 100644
--- a/src/web/w-private.h
+++ b/src/web/w-private.h
@@ -110,6 +110,9 @@ struct pss {
uint64_t artifact_offset;
uint64_t artifact_length;
+ char *last_bps[3];
+ size_t last_bps_len[3];
+
unsigned int spa_failed:1;
unsigned int dry:1;
unsigned int frag:1;
@@ -233,5 +236,9 @@ int
saiw_browser_broadcast_queue_builders(struct vhd *vhd, struct pss *pss);
int
saiw_browser_broadcast_queue_pcons(struct vhd *vhd, struct pss *pss);
+int
+saiw_browser_broadcast_queue_pcon_energy(struct vhd *vhd, struct pss *pss, sai_pcon_energy_report_t *energy);
+
+extern const lws_struct_map_t lsm_schema_pcon_energy[];
diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c
index 2a8b30e..8082b74 100644
--- a/src/web/w-ws-browser.c
+++ b/src/web/w-ws-browser.c
@@ -1126,20 +1126,135 @@ so_finish:
return 0;
}
+struct sai_dyn_buf {
+ char *buf;
+ size_t len;
+ size_t alloc;
+};
+
+static int
+sai_dyn_buf_ensure(struct sai_dyn_buf *d, size_t needed)
+{
+ if (d->len + needed <= d->alloc)
+ return 0;
+ size_t na = d->alloc ? d->alloc * 2 : 4096;
+ while (d->len + needed > na)
+ na *= 2;
+ char *nb = realloc(d->buf, na);
+ if (!nb)
+ return 1;
+ d->buf = nb;
+ d->alloc = na;
+ return 0;
+}
+
+static inline int
+sai_dyn_buf_append(struct sai_dyn_buf *d, const void *p, size_t len)
+{
+ if (sai_dyn_buf_ensure(d, len))
+ return 1;
+ memcpy(d->buf + d->len, p, len);
+ d->len += len;
+ return 0;
+}
+
+static int
+saiw_dedup_and_queue(struct pss *pss, int idx, struct sai_dyn_buf *d)
+{
+ int changed = 1;
+
+ /* check if we changed versus last payload */
+ if (pss->last_bps[idx] && pss->last_bps_len[idx] == d->len - LWS_PRE &&
+ !memcmp(pss->last_bps[idx], d->buf + LWS_PRE, d->len - LWS_PRE)) {
+ changed = 0;
+ } else {
+ free(pss->last_bps[idx]);
+ pss->last_bps[idx] = malloc(d->len - LWS_PRE);
+ if (pss->last_bps[idx]) {
+ memcpy(pss->last_bps[idx], d->buf + LWS_PRE, d->len - LWS_PRE);
+ pss->last_bps_len[idx] = d->len - LWS_PRE;
+ }
+ }
+
+ if (changed)
+ saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, d->buf + LWS_PRE,
+ d->len - LWS_PRE,
+ lws_write_ws_flags(LWS_WRITE_TEXT, 1, 1));
+
+ free(d->buf);
+ d->buf = NULL;
+ return 0;
+}
+
+int
+saiw_browser_broadcast_queue_pcon_energy(struct vhd *vhd, struct pss *pss, sai_pcon_energy_report_t *energy)
+{
+ struct sai_dyn_buf d;
+ char buf[1024];
+ lws_struct_serialize_t *js;
+ lws_struct_json_serialize_result_t r;
+ size_t w;
+
+ if (!vhd || !energy)
+ return 0;
+
+ memset(&d, 0, sizeof(d));
+
+ /* Reserve LWS_PRE header space */
+ memset(buf, 0, LWS_PRE);
+ if (sai_dyn_buf_append(&d, buf, LWS_PRE))
+ return 1;
+
+ js = lws_struct_json_serialize_create(
+ lsm_schema_pcon_energy,
+ LWS_ARRAY_SIZE(lsm_schema_pcon_energy),
+ 0, energy);
+ if (!js) {
+ free(d.buf);
+ return 1;
+ }
+
+ do {
+ r = lws_struct_json_serialize(js, (uint8_t *)buf, sizeof(buf), &w);
+
+ if (w && sai_dyn_buf_append(&d, buf, w)) {
+ lws_struct_json_serialize_destroy(&js);
+ free(d.buf);
+ return 1;
+ }
+
+ if (r == LSJS_RESULT_ERROR) {
+ lws_struct_json_serialize_destroy(&js);
+ free(d.buf);
+ return 1;
+ }
+ } while (r == LSJS_RESULT_CONTINUE);
+
+ lws_struct_json_serialize_destroy(&js);
+
+ return saiw_dedup_and_queue(pss, 2, &d);
+}
+
int
saiw_browser_broadcast_queue_pcons(struct vhd *vhd, struct pss *pss)
{
- char buf[4096 + LWS_PRE], *start = buf + LWS_PRE, *p = start,
- *end = buf + sizeof(buf);
+ struct sai_dyn_buf d;
+ char buf[1024]; /* temp buffer for serialization before append */
lws_struct_serialize_t *js;
sai_power_managed_builders_t pmb;
lws_struct_json_serialize_result_t r;
size_t w;
- char fi = 1;
if (!vhd || !vhd->pcons)
return 0;
+ memset(&d, 0, sizeof(d));
+
+ /* Reserve LWS_PRE header space */
+ memset(buf, 0, LWS_PRE);
+ if (sai_dyn_buf_append(&d, buf, LWS_PRE))
+ return 1;
+
memset(&pmb, 0, sizeof(pmb));
pmb.power_controllers = vhd->pcons_owner;
@@ -1147,51 +1262,61 @@ saiw_browser_broadcast_queue_pcons(struct vhd *vhd, struct pss *pss)
lsm_schema_power_managed_builders,
LWS_ARRAY_SIZE(lsm_schema_power_managed_builders),
0, &pmb);
- if (!js)
+ if (!js) {
+ free(d.buf);
return 1;
+ }
do {
- r = lws_struct_json_serialize(js, (uint8_t *)p,
- lws_ptr_diff_size_t(end, p), &w);
- p += w;
+ r = lws_struct_json_serialize(js, (uint8_t *)buf, sizeof(buf), &w);
- switch (r) {
- case LSJS_RESULT_FINISH:
- case LSJS_RESULT_CONTINUE:
- saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
- lws_ptr_diff_size_t(p, start),
- lws_write_ws_flags(LWS_WRITE_TEXT, fi, r == LSJS_RESULT_FINISH));
- fi = 0;
- p = start;
- break;
- case LSJS_RESULT_ERROR:
+ if (sai_dyn_buf_append(&d, buf, w)) {
lws_struct_json_serialize_destroy(&js);
+ free(d.buf);
+ return 1;
+ }
+
+ if (r == LSJS_RESULT_ERROR) {
+ lws_struct_json_serialize_destroy(&js);
+ free(d.buf);
return 1;
}
} while (r == LSJS_RESULT_CONTINUE);
lws_struct_json_serialize_destroy(&js);
- return 0;
+ return saiw_dedup_and_queue(pss, 1, &d);
}
int
saiw_browser_broadcast_queue_builders(struct vhd *vhd, struct pss *pss)
{
saiw_browser_broadcast_queue_pcons(vhd, pss);
- char buf[4096 + LWS_PRE], *start = buf + LWS_PRE, *p = start,
- *end = buf + sizeof(buf);
+ struct sai_dyn_buf d;
+ char buf[1024]; /* temp buffer for serialization before append */
lws_struct_serialize_t *js;
char esc[256];
lws_dll2_t *walk = NULL;
- char fi = 1, subsequent;
+ char subsequent;
size_t w;
+ int n;
- p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
+ memset(&d, 0, sizeof(d));
+
+ /* Reserve LWS_PRE header space */
+ memset(buf, 0, LWS_PRE);
+ if (sai_dyn_buf_append(&d, buf, LWS_PRE))
+ return 1;
+
+ n = lws_snprintf(buf, sizeof(buf),
"{\"schema\":\"com.warmcat.sai.builders\","
" \"alang\":\"%s\","
" \"builders\":[",
lws_sql_purify(esc, pss->alang, sizeof(esc) - 1));
+ if (sai_dyn_buf_append(&d, buf, (size_t)n)) {
+ free(d.buf);
+ return 1;
+ }
if (vhd && vhd->builders)
walk = lws_dll2_get_head(&vhd->builders_owner);
@@ -1211,66 +1336,58 @@ saiw_browser_broadcast_queue_builders(struct vhd *vhd, struct pss *pss)
0, b);
if (!js) {
lwsac_unreference(&vhd->builders);
+ free(d.buf);
return 1;
}
do {
if (subsequent && start_of_this_builder) {
- *p++ = ',';
+ if (sai_dyn_buf_append(&d, ",", 1)) {
+ lws_struct_json_serialize_destroy(&js);
+ lwsac_unreference(&vhd->builders);
+ free(d.buf);
+ return 1;
+ }
start_of_this_builder = 0;
}
- r = lws_struct_json_serialize(js, (uint8_t *)p, lws_ptr_diff_size_t(end, p) - 2, &w);
- p += w;
+ r = lws_struct_json_serialize(js, (uint8_t *)buf, sizeof(buf), &w);
+
+ if (w && sai_dyn_buf_append(&d, buf, w)) {
+ lws_struct_json_serialize_destroy(&js);
+ lwsac_unreference(&vhd->builders);
+ free(d.buf);
+ return 1;
+ }
switch (r) {
- case LSJS_RESULT_CONTINUE:
- saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
- lws_ptr_diff_size_t(p, start),
- lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0));
- fi = 0;
- p = start;
- break;
case LSJS_RESULT_ERROR:
lws_struct_json_serialize_destroy(&js);
lwsac_unreference(&vhd->builders);
+ free(d.buf);
return 1;
+ case LSJS_RESULT_CONTINUE:
case LSJS_RESULT_FINISH:
- lws_struct_json_serialize_destroy(&js);
break;
}
} while (r == LSJS_RESULT_CONTINUE);
+ lws_struct_json_serialize_destroy(&js);
+
subsequent = 1;
walk = walk->next;
-
- if (walk && lws_ptr_diff_size_t(end, p) < 512) {
- /* No room for another builder, fragment now */
- saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
- lws_ptr_diff_size_t(p, start),
- lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0));
- fi = 0;
- p = start;
- }
}
- if (lws_ptr_diff_size_t(end, p) < 16) {
- saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
- lws_ptr_diff_size_t(p, start),
- lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0));
- fi = 0;
- p = start;
+ n = lws_snprintf(buf, sizeof(buf), " \n]}");
+ if (sai_dyn_buf_append(&d, buf, (size_t)n)) {
+ lwsac_unreference(&vhd->builders);
+ free(d.buf);
+ return 1;
}
- p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), " \n]}");
-
- saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start,
- lws_ptr_diff_size_t(p, start),
- lws_write_ws_flags(LWS_WRITE_TEXT, fi, 1));
-
lwsac_unreference(&vhd->builders);
- return 0;
+ return saiw_dedup_and_queue(pss, 0, &d);
}
/*
diff --git a/src/web/w-ws-server.c b/src/web/w-ws-server.c
index 2261a9e..97036b4 100644
--- a/src/web/w-ws-server.c
+++ b/src/web/w-ws-server.c
@@ -127,9 +127,6 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
switch (m->a.top_schema_index) {
case SAIS_WS_WEBSRV_RX_LOADREPORT:
case SAIS_WS_WEBSRV_RX_TASKACTIVITY:
- case SAIS_WS_WEBSRV_RX_SAI_BUILDERS:
- case SAIS_WS_WEBSRV_RX_POWER_MANAGED_BUILDERS:
- case SAIS_WS_WEBSRV_RX_PCON_ENERGY:
saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, p, rem,
lws_write_ws_flags(LWS_WRITE_TEXT,
is_start,
@@ -151,9 +148,6 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
switch (m->a.top_schema_index) {
case SAIS_WS_WEBSRV_RX_TASKCHANGE:
case SAIS_WS_WEBSRV_RX_EVENTCHANGE:
- case SAIS_WS_WEBSRV_RX_SAI_BUILDERS:
- case SAIS_WS_WEBSRV_RX_POWER_MANAGED_BUILDERS:
- case SAIS_WS_WEBSRV_RX_PCON_ENERGY:
case SAIS_WS_WEBSRV_RX_LOADREPORT:
case SAIS_WS_WEBSRV_RX_TASKACTIVITY:
saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, p, consumed,
@@ -232,6 +226,14 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
} lws_end_foreach_dll(p);
break;
+ case SAIS_WS_WEBSRV_RX_PCON_ENERGY:
+ lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) {
+ struct pss *pss = lws_container_of(p, struct pss, same);
+
+ saiw_browser_broadcast_queue_pcon_energy(pss->vhd, pss, (sai_pcon_energy_report_t *)m->a.dest);
+ } lws_end_foreach_dll(p);
+ break;
+
case SAIS_WS_WEBSRV_RX_OVERVIEW:
lwsl_notice("%s: force overview\n", __func__);
lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) {