Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
Author[]Andy Green <andy@warmcat.com> 2025-08-10 11:18 UTC
Committer[]Andy Green <andy@warmcat.com> 2025-08-14 19:26 UTC
Treed5120a4d9d91e25dfd9802fff4e0ab21e9993853   Raw Patch
 
builder-spinner
builder-spinner

Co-developed-by: Gemini 2.5 Pro
diff --git a/READMEs/README-sai-power.md b/READMEs/README-sai-power.md index d2b0008..604451f 100644 --- a/READMEs/README-sai-power.md +++ b/READMEs/README-sai-power.md @@ -122,15 +122,22 @@ or suspend at any time. To simplify that case, you can manually ask sai-power to start up a builder. ``` -# wget -O- http://10.199.0.10:3333/power-on/hostname +$ wget -O- http://10.199.0.10:3333/power-on/hostname ``` You can also manually turn off the builder from the same api. ``` -# wget -O- http://10.199.0.10:3333/power-off/hostname +$ wget -O- http://10.199.0.10:3333/power-off/hostname ``` +You can get a list of controllable hostnames from sai-power, this +info is coming from the JSON conf + +``` +$ wget -O- http://10.199.0.10:3333/ 2>/dev/null +l2,w11-l2,b32,rpi5,rv2,ubuntu_rpi4,rpi3B_netbsdBE + Asking sai-power to do it has some advantages: - it will work the same no matter the details of that particular builder's diff --git a/assets/sai.css b/assets/sai.css index 5ea141b..c44e92f 100644 --- a/assets/sai.css +++ b/assets/sai.css @@ -696,6 +696,52 @@ div.bdr { opacity: 0.35; } +/* Style for the powering-up state */ +.powering-up { + position: relative; /* Needed for the ::after pseudo-element */ +} + +/* The spinner pseudo-element */ +.powering-up::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 24px; /* Adjust size as needed */ + height: 24px; + margin-top: -12px; /* Half of height */ + margin-left: -12px; /* Half of width */ + border: 3px solid rgba(0, 0, 0, 0.2); + border-top-color: #007bff; /* Spinner color */ + border-radius: 50%; + animation: spin 1s linear infinite; + z-index: 10; /* Ensure it's on top */ +} + +@keyframes spin { to { transform: rotate(360deg); } } + +/* Style for the powering-down state */ +.powering-down { + position: relative; /* Needed for the ::after pseudo-element */ +} + +/* The spinner pseudo-element */ +.powering-down::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 24px; /* Adjust size as needed */ + height: 24px; + margin-top: -12px; /* Half of height */ + margin-left: -12px; /* Half of width */ + border: 3px solid rgba(0, 0, 0, 0.2); + border-top-color: #ff4136; /* Spinner color */ + border-radius: 50%; + animation: spin 1s linear infinite; + z-index: 10; /* Ensure it's on top */ +} + img.branch { display: inline-block; diff --git a/assets/sai.js b/assets/sai.js index 4882e2b..c795775 100644 --- a/assets/sai.js +++ b/assets/sai.js @@ -871,6 +871,11 @@ function createBuilderDiv(plat) { platDiv.className = "ibuil bdr"; if (!plat.online) platDiv.className += " offline"; + // Add a new class if the builder is powering up + if (plat.powering_up) + platDiv.className += " powering-up"; + if (plat.powering_down) + platDiv.className += " powering-down"; platDiv.id = "binfo-" + plat.name; platDiv.title = plat.platform + "@" + plat.name.split('.')[0] + " / " + plat.peer_ip; @@ -1130,6 +1135,8 @@ function ws_open_sai() nestedPlatforms.forEach(plat => { const div = existingDivs.get(plat.name) || createBuilderDiv(plat); div.classList.toggle('offline', !plat.online); + div.classList.toggle('powering-up', !!plat.powering_up); + div.classList.toggle('powering-down', !!plat.powering_down); groupDiv.appendChild(div); }); tdContainer.appendChild(groupDiv); @@ -1138,6 +1145,8 @@ function ws_open_sai() mainPlatforms.forEach(plat => { const div = existingDivs.get(plat.name) || createBuilderDiv(plat); div.classList.toggle('offline', !plat.online); + div.classList.toggle('powering-up', !!plat.powering_up); + div.classList.toggle('powering-down', !!plat.powering_down); tdContainer.appendChild(div); }); } diff --git a/src/common/include/private.h b/src/common/include/private.h index 0720c6b..6d0e62d 100644 --- a/src/common/include/private.h +++ b/src/common/include/private.h @@ -404,6 +404,8 @@ typedef struct sai_plat { lws_dll2_owner_t loads; int online; /* 1 = connected, 0 = offline */ uint64_t last_seen; /* unix time */ + int powering_up; /* 1 = sai-power is booting it */ + int powering_down; int instances; int ongoing; @@ -438,6 +440,14 @@ typedef struct sai_browse_rx_taskinfo { uint8_t logs; } sai_browse_rx_taskinfo_t; +/* sai-power -> sai-server, tells it that a platform is being powered up */ +typedef struct sai_power_state { + lws_dll2_t list; /* for parser */ + char host[256]; + int powering_up; + int powering_down; +} sai_power_state_t; + extern const lws_struct_map_t lsm_schema_json_map_task[], lsm_schema_sq3_map_task[], @@ -458,10 +468,11 @@ extern const lws_struct_map_t lsm_schema_json_map_can[1], lsm_schema_json_map_task[1], lsm_schema_json_map_event[1], - lsm_resource[4] + lsm_resource[4], + lsm_power_state[3] ; extern const lws_struct_map_t lsm_plat[6]; -extern const lws_struct_map_t lsm_plat_for_json[8]; +extern const lws_struct_map_t lsm_plat_for_json[10]; 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 e612330..4899184 100644 --- a/src/common/struct-metadata.c +++ b/src/common/struct-metadata.c @@ -58,6 +58,8 @@ const lws_struct_map_t lsm_plat_for_json[] = { LSM_STRING_PTR(sai_plat_t, platform,"platform"), LSM_SIGNED(sai_plat_t, online, "online"), // MUST be present LSM_UNSIGNED(sai_plat_t, last_seen, "last_seen"), + LSM_SIGNED(sai_plat_t, powering_up, "powering_up"), + LSM_SIGNED(sai_plat_t, powering_down, "powering_down"), LSM_CARRAY(sai_plat_t, peer_ip, "peer_ip"), }; @@ -237,6 +239,13 @@ const lws_struct_map_t lsm_schema_json_map_artifact[] = { "com-warmcat-sai-artifact"), }; +const lws_struct_map_t lsm_power_state[] = { + LSM_CARRAY(sai_power_state_t, host, "host"), + LSM_SIGNED(sai_power_state_t, powering_up, "powering_up"), + LSM_SIGNED(sai_power_state_t, powering_down, "powering_down"), +}; + + const lws_struct_map_t lsm_schema_sq3_map_artifact[] = { LSM_SCHEMA_DLL2 (sai_artifact_t, list, NULL, lsm_artifact, "artifacts"), }; diff --git a/src/power/p-comms.c b/src/power/p-comms.c index 942bfb6..566ee30 100644 --- a/src/power/p-comms.c +++ b/src/power/p-comms.c @@ -29,12 +29,60 @@ #include "../common/struct-metadata.c" +/* Map for the "powering up" message we send to the server */ +static const lws_struct_map_t lsm_schema_power_state[] = { + LSM_SCHEMA(sai_power_state_t, NULL, lsm_power_state, + "com.warmcat.sai.powerstate"), +}; + LWS_SS_USER_TYPEDEF char payload[200]; size_t size; size_t pos; + + lws_dll2_owner_t ps_owner; } saip_server_link_t; +void +saip_notify_server_power_state(const char *plat_name, int up, int down) +{ + saip_server_t *sps; + sai_power_state_t *ps; + + /* Find the first (usually only) configured sai-server connection */ + if (!power.sai_server_owner.head) { + lwsl_warn("%s: No sai-server configured to notify\n", __func__); + return; + } + sps = lws_container_of(power.sai_server_owner.head, saip_server_t, list); + if (!sps->ss) { + lwsl_warn("%s: Not connected to sai-server to notify\n", __func__); + return; + } + + /* Allocate and queue the notification message */ + ps = malloc(sizeof(*ps)); + if (!ps) + return; + + memset(ps, 0, sizeof(*ps)); + lws_strncpy(ps->host, plat_name, sizeof(ps->host)); + ps->powering_up = (char)up; + ps->powering_down = (char)down; + + /* The per-connection user object for the server link is a saip_server_link_t */ + { + saip_server_link_t *pss = (saip_server_link_t *)lws_ss_to_user_object(sps->ss); + lws_dll2_add_tail(&ps->list, &pss->ps_owner); + } + + /* Request a writable callback to send the message */ + if (lws_ss_request_tx(sps->ss)) + lwsl_ss_warn(sps->ss, "Unable to request tx"); + + lwsl_notice("%s: Queued notification for %s\n", __func__, plat_name); +} + static lws_ss_state_return_t saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) { @@ -97,6 +145,8 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) if (sp->needed) { lwsl_notice("%s: Needed builders: %s\n", __func__, sp->name); + saip_notify_server_power_state(sp->name, 1, 0); + /* * Server said this platform or at least one dependency * has pending jobs. sai-power config says this builder @@ -136,6 +186,35 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) return 0; } +static lws_ss_state_return_t +saip_m_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, + int *flags) +{ + saip_server_link_t *pss = (saip_server_link_t *)userobj; + lws_struct_serialize_t *js; + + if (!pss->ps_owner.head) + return LWSSSSRET_TX_DONT_SEND; + + /* Dequeue the first pending notification */ + sai_power_state_t *ps = lws_container_of(pss->ps_owner.head, + sai_power_state_t, list); + + js = lws_struct_json_serialize_create(lsm_schema_power_state, 1, 0, ps); + if (js && lws_struct_json_serialize(js, buf, *len, len) == LSJS_RESULT_FINISH) { + *flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM; + lws_dll2_remove(&ps->list); + free(ps); + } + lws_struct_json_serialize_destroy(&js); + + /* If there are more to send, request another writable callback */ + if (pss->ps_owner.head) + if (lws_ss_request_tx(lws_ss_from_user(pss))) + lwsl_ss_warn(lws_ss_from_user(pss), "tx request failed"); + + return LWSSSSRET_OK; +} static int cleanup_on_ss_destroy(struct lws_dll2 *d, void *user) @@ -218,6 +297,13 @@ saip_m_state(void *userobj, void *sh, lws_ss_constate_t state, return lws_ss_request_tx(sps->ss); case LWSSSCS_DISCONNECTED: + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, pss->ps_owner.head) { + sai_power_state_t *ps = lws_container_of(d, sai_power_state_t, list); + + lws_dll2_remove(&ps->list); + free(ps); + } lws_end_foreach_dll_safe(d, d1); + /* * clean up any ongoing spawns related to this connection */ @@ -245,4 +331,5 @@ saip_m_state(void *userobj, void *sh, lws_ss_constate_t state, LWS_SS_INFO("sai_power", saip_server_link_t) .rx = saip_m_rx, .state = saip_m_state, + .tx = saip_m_tx, /* We need a TX handler to send messages */ }; diff --git a/src/power/p-private.h b/src/power/p-private.h index fe08d82..5b75277 100644 --- a/src/power/p-private.h +++ b/src/power/p-private.h @@ -137,5 +137,6 @@ int saip_config_global(struct sai_power *power, const char *d); extern int saip_config(struct sai_power *power, const char *d); extern void saip_config_destroy(struct sai_power *power); - +extern void +saip_notify_server_power_state(const char *plat_name, int up, int down); diff --git a/src/power/p-sai.c b/src/power/p-sai.c index b9bda4a..d340775 100644 --- a/src/power/p-sai.c +++ b/src/power/p-sai.c @@ -195,6 +195,7 @@ saip_sul_action_power_off(struct lws_sorted_usec_list *sul) } lwsl_warn("%s: powering off host %s\n", __func__, sp->host); + saip_notify_server_power_state(sp->host, 0, 1); if (lws_ss_create(power.context, 0, &ssi_saip_smartplug_t, (void *)sp->power_off_url, NULL, NULL, NULL)) { @@ -281,7 +282,7 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, case LWSSSCS_SERVER_TXN: lws_ss_get_metadata(lws_ss_from_user(g), "path", (const void **)&path, &len); - // lwsl_ss_user(lws_ss_from_user(g), "LWSSSCS_SERVER_TXN path %.*s", (int)len, path); + lwsl_ss_user(lws_ss_from_user(g), "LWSSSCS_SERVER_TXN path '%.*s' (%d)", (int)len, path, (int)len); /* * path is containing a string like "/power-off/b32" @@ -301,6 +302,28 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, g->pos = 0; + if (len == 1 && path[0] == '/') { + /* print controllable platforms */ + + g->size = 0; + + lws_start_foreach_dll(struct lws_dll2 *, px, power.sai_server_owner.head) { + saip_server_t *s = lws_container_of(px, saip_server_t, list); + + lws_start_foreach_dll(struct lws_dll2 *, px1, s->sai_plat_owner.head) { + saip_server_plat_t *sp = lws_container_of(px1, saip_server_plat_t, list); + + if (g->size) + g->payload[g->size++] = ','; + g->size = g->size + (size_t)lws_snprintf(g->payload + g->size, sizeof(g->payload) - g->size - 3, "%s", sp->host); + + } lws_end_foreach_dll(px1); + } lws_end_foreach_dll(px); + + g->payload[g->size] = '\0'; + goto bail; + } + if (len > 6 && !strncmp(path, "/stay/", 6)) { lws_strnncpy(pn, &path[6], len - 6, sizeof(pn)); @@ -324,6 +347,7 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, goto bail; } if (sp->power_on_mac) { + saip_notify_server_power_state(sp->host, 1, 0); if (write(lws_spawn_get_fd_stdxxx(lsp_wol, 0), sp->power_on_mac, strlen(sp->power_on_mac)) != (ssize_t)strlen(sp->power_on_mac)) @@ -347,9 +371,10 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), "power-on ss failed create %s", sp->host); goto bail; - } + } lwsl_warn("%s: powered on host %s\n", __func__, sp->host); + saip_notify_server_power_state(sp->host, 1, 0); g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), "Manually powered on %s", sp->host); diff --git a/src/server/s-comms.c b/src/server/s-comms.c index 85aed2e..dae4680 100644 --- a/src/server/s-comms.c +++ b/src/server/s-comms.c @@ -763,30 +763,6 @@ callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, sais_builder_disconnected(vhd, wsi); - /* - * 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) { - 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(cb); - } - - } lws_end_foreach_dll_safe(p, p1); - sais_resource_wellknown_remove_pss(&pss->vhd->server, pss); if (pss->blob_artifact) { @@ -809,12 +785,51 @@ callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, case LWS_CALLBACK_RECEIVE: - if (!pss->vhd) - pss->vhd = vhd; + /* + * A ws client sent us something... it could be a builder or + * it could be sai-power. We can tell which by the `is_power` + * flag we set in the pss during ESTABLISHED. + */ + + if (pss->is_power) { + struct lejp_ctx ctx; + lws_struct_args_t a; + sai_power_state_t *ps; + const lws_struct_map_t lsm_schema_power_state[] = { + LSM_SCHEMA(sai_power_state_t, NULL, lsm_power_state, + "com.warmcat.sai.powerstate"), + }; + + /* This is a message from sai-power */ + lwsl_notice("RX from sai-power: %.*s\n", (int)len, (const char *)in); + + memset(&a, 0, sizeof(a)); + a.map_st[0] = lsm_schema_power_state; + a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_schema_power_state); + a.ac_block_size = 512; + + lws_struct_json_init_parse(&ctx, NULL, &a); + if (lejp_parse(&ctx, (uint8_t *)in, (int)len) < 0 || !a.dest) { + lwsl_warn("Failed to parse msg from sai-power\n"); + lwsac_free(&a.ac); + break; // Exit case + } + + ps = (sai_power_state_t *)a.dest; + if (ps->powering_up) { + lwsl_notice("sai-power is powering up: %s\n", ps->host); + sais_set_builder_power_state(vhd, ps->host, 1, 0); + } else if (ps->powering_down) { + lwsl_notice("sai-power is powering down: %s\n", ps->host); + sais_set_builder_power_state(vhd, ps->host, 0, 1); + } + + lwsac_free(&a.ac); + break; // Exit case + } - lwsl_info("SWT_BUILDER RX: %d\n", (int)len); /* - * Builder sent us something on websockets + * This is a message from a builder */ pss->wsi = wsi; if (sais_ws_json_rx_builder(vhd, pss, in, len)) diff --git a/src/server/s-private.h b/src/server/s-private.h index 2c72ba7..4981a74 100644 --- a/src/server/s-private.h +++ b/src/server/s-private.h @@ -31,6 +31,7 @@ struct sai_plat; typedef struct sai_platm { lws_dll2_owner_t builder_owner; lws_dll2_owner_t subs_owner; + lws_dll2_owner_t power_state_owner; /* sai_power_state_t */ /* the list of well-known, configured resources */ lws_dll2_owner_t resource_wellknown_owner; /* sai_resource_wellknown_t */ @@ -50,6 +51,11 @@ typedef struct sai_platform { /* build and name over-allocated here */ } sai_platform_t; +typedef struct sai_powering_up_plat { + lws_dll2_t list; + char name[256]; +} sai_powering_up_plat_t; + typedef enum { SAIN_ACTION_INVALID, SAIN_ACTION_REPO_UPDATED @@ -185,6 +191,7 @@ struct vhd { struct lws_dll2_owner builders; struct lws_dll2_owner sai_powers; struct lws_dll2_owner pending_plats; + lws_dll2_owner_t powering_up_list; /* sai_powering_up_plat_t */ struct lwsac *ac_plats; @@ -318,10 +325,14 @@ sais_platforms_with_tasks_pending(struct vhd *vhd); sai_plat_t * sais_builder_from_uuid(struct vhd *vhd, const char *hostname, const char *_file, int _line); +sai_plat_t * +sais_builder_from_host(struct vhd *vhd, const char *host); void sais_builder_disconnected(struct vhd *vhd, struct lws *wsi); +void +sais_set_builder_power_state(struct vhd *vhd, const char *name, int up, int down); void sais_mark_all_builders_offline(struct vhd *vhd); diff --git a/src/server/s-websrv.c b/src/server/s-websrv.c index b51f541..f904009 100644 --- a/src/server/s-websrv.c +++ b/src/server/s-websrv.c @@ -167,6 +167,7 @@ sais_websrv_broadcast(struct lws_ss_handle *hsrv, const char *str, size_t len) int sais_list_builders(struct vhd *vhd) { + lwsl_warn("%s: ENTRY\n", __func__); lws_dll2_owner_t db_builders_owner; struct lwsac *ac = NULL; char *p = vhd->json_builders, *end = p + sizeof(vhd->json_builders), @@ -210,6 +211,21 @@ sais_list_builders(struct vhd *vhd) builder_from_db->ongoing = 0; } + builder_from_db->powering_up = 0; + builder_from_db->powering_down = 0; + + lws_start_foreach_dll(struct lws_dll2 *, p, vhd->server.power_state_owner.head) { + sai_power_state_t *ps = lws_container_of(p, sai_power_state_t, list); + size_t host_len = strlen(ps->host); + + if (!strncmp(builder_from_db->name, ps->host, host_len) && + builder_from_db->name[host_len] == '.') { + builder_from_db->powering_up = ps->powering_up; + builder_from_db->powering_down = ps->powering_down; + break; + } + } lws_end_foreach_dll(p); + js = lws_struct_json_serialize_create( lsm_schema_map_plat_simple, LWS_ARRAY_SIZE(lsm_schema_map_plat_simple), diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c index 69b29df..d9f3cea 100644 --- a/src/server/s-ws-builder.c +++ b/src/server/s-ws-builder.c @@ -258,6 +258,70 @@ sais_builder_from_uuid(struct vhd *vhd, const char *hostname, const char *_file, return NULL; } +sai_plat_t * +sais_builder_from_host(struct vhd *vhd, const char *host) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, + vhd->server.builder_owner.head) { + sai_plat_t *cb = lws_container_of(p, sai_plat_t, + sai_plat_list); + size_t host_len = strlen(host); + + if (!strncmp(cb->name, host, host_len) && + cb->name[host_len] == '.') + return cb; + + } lws_end_foreach_dll(p); + + return NULL; +} + +void +sais_set_builder_power_state(struct vhd *vhd, const char *name, int up, int down) +{ + sai_power_state_t *ps = NULL; + sai_plat_t *live_builder; + + if (up) { + live_builder = sais_builder_from_host(vhd, name); + if (live_builder) + return; + } + + if (down) { + live_builder = sais_builder_from_host(vhd, name); + if (!live_builder) + return; + } + + lws_start_foreach_dll(struct lws_dll2 *, p, vhd->server.power_state_owner.head) { + ps = lws_container_of(p, sai_power_state_t, list); + if (!strcmp(ps->host, name)) + break; + ps = NULL; + } lws_end_foreach_dll(p); + + if (!ps && (up || down)) { + ps = malloc(sizeof(*ps)); + if (!ps) + return; + memset(ps, 0, sizeof(*ps)); + lws_strncpy(ps->host, name, sizeof(ps->host)); + lws_dll2_add_tail(&ps->list, &vhd->server.power_state_owner); + } + + if (ps) { + ps->powering_up = (char)up; + ps->powering_down = (char)down; + if (!ps->powering_up && !ps->powering_down) { + lws_dll2_remove(&ps->list); + free(ps); + } + } + + sais_list_builders(vhd); +} + /* * Called from the builder protocol LWS_CALLBACK_CLOSED handler */ @@ -276,9 +340,28 @@ sais_builder_disconnected(struct vhd *vhd, struct lws *wsi) cb = lws_container_of(p, sai_plat_t, sai_plat_list); if (cb->wsi == wsi) { + char q[256]; + lwsl_notice("%s: Builder '%s' disconnected\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"); + + const char *dot = strchr(cb->name, '.'); + if (dot) { + char host[128]; + lws_strnncpy(host, cb->name, dot - cb->name, sizeof(host)); + lws_start_foreach_dll_safe(struct lws_dll2 *, p2, p3, vhd->server.power_state_owner.head) { + sai_power_state_t *ps = lws_container_of(p2, sai_power_state_t, list); + if (!strcmp(ps->host, host)) { + lws_dll2_remove(&ps->list); + free(ps); + break; + } + } lws_end_foreach_dll_safe(p2, p3); + } + lws_dll2_remove(&cb->sai_plat_list); free(cb); } @@ -433,6 +516,13 @@ handle: lws_dll2_add_tail(&live_cb->sai_plat_list, &vhd->server.builder_owner); } } + + const char *dot = strchr(build->name, '.'); + if (dot) { + char host[128]; + lws_strnncpy(host, build->name, dot - build->name, sizeof(host)); + sais_set_builder_power_state(vhd, host, 0, 0); + } } lws_end_foreach_dll(pb); /* The lwsac from the parsed message is now completely disposable */
Page fetched 0s ago, creation time: 9ms (vhost etag hits: 0%, cache hits: 0%)