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-07-30 06:22 UTC
Committer[]Andy Green <andy@warmcat.com> 2025-07-30 10:51 UTC
Treed0c504502eedabebfb980f496c6e322175e03209   Raw Patch
 
introduce power dependencies
introduce power dependencies
diff --git a/src/builder/b-sai.c b/src/builder/b-sai.c index d944a98..ca4af05 100644 --- a/src/builder/b-sai.c +++ b/src/builder/b-sai.c @@ -547,7 +547,7 @@ LWS_SS_USER_TYPEDEF } saib_power_link_t; static lws_ss_state_return_t -sai_power_link_state(void *userobj, void *sh, lws_ss_constate_t state, +saib_power_link_state(void *userobj, void *sh, lws_ss_constate_t state, lws_ss_tx_ordinal_t ack) { saib_power_link_t *g = (saib_power_link_t *)userobj; @@ -558,7 +558,7 @@ sai_power_link_state(void *userobj, void *sh, lws_ss_constate_t state, switch (state) { case LWSSSCS_CREATING: - snprintf(path, sizeof(path) - 1, "%s/power-off/%s", + snprintf(path, sizeof(path) - 1, "%s/auto-power-off/%s", builder.url_sai_power, (const char *)lws_ss_opaque_from_user(g)); @@ -568,7 +568,13 @@ sai_power_link_state(void *userobj, void *sh, lws_ss_constate_t state, if (r) lwsl_err("%s: set_metadata said %d\n", __func__, (int)r); + lws_ss_start_timeout(lws_ss_from_user(g), 3000); /* 3 sec */ + return lws_ss_request_tx(lws_ss_from_user(g)); + + case LWSSSCS_TIMEOUT: + break; + default: break; } @@ -576,8 +582,39 @@ sai_power_link_state(void *userobj, void *sh, lws_ss_constate_t state, return LWSSSSRET_OK; } +static lws_ss_state_return_t +saib_power_link_rx(void *userobj, const uint8_t *buf, size_t len, int flags) +{ + uint8_t te = 0; + ssize_t n; + + if (len < 4 || !(flags & LWSSS_FLAG_SOM)) + return 0; + + if (memcmp(buf, "ACK:", 4)) { + lwsl_warn("%s: sai-power didn't start power-off: %.*s\n", + __func__, (int)len, (const char *)buf); + return LWSSSSRET_OK; + } + + lwsl_notice("%s: sai-power scheduling power-off: doing shutdown...\n", __func__); + + /* + * In the grace time for actioning the power-off, we should shutdown + * cleanly + */ + + n = write(lws_spawn_get_fd_stdxxx(lsp_suspender, 0), &te, 1); + if (n != 1) + lwsl_err("%s: unable to request shutdown\n", __func__); + + return LWSSSSRET_OK; +} + + LWS_SS_INFO("sai_power", saib_power_link_t) - .state = sai_power_link_state, + .rx = saib_power_link_rx, + .state = saib_power_link_state, }; @@ -630,7 +667,11 @@ sul_idle_cb(lws_sorted_usec_list_t *sul) return; /* - * The plan is ask sai-power to turn us off... + * The plan is ask sai-power to turn us off... we don't know our + * dependency situation since we're just a standalone builder. + * + * We will have to let sai-power figure the deps out and say if + * it's willing to auto power-off or not. */ lwsl_notice("%s: creating sai-power ss...\n", __func__); @@ -641,26 +682,6 @@ sul_idle_cb(lws_sorted_usec_list_t *sul) return; } - /* - * Give the http action some time to complete (else we will kill - * everything including the http as soon as we progress on to shutdown) - */ - -#if defined(WIN32) - Sleep(2000); -#else - sleep(2); -#endif - - lwsl_notice("%s: doing shutdown...\n", __func__); - - /* - * In the grace time for actioning the power-off, we should shutdown - * cleanly - */ - - te = 0; - n = write(lws_spawn_get_fd_stdxxx(lsp_suspender, 0), &te, 1); #endif } diff --git a/src/power/p-comms.c b/src/power/p-comms.c index b7794ff..4571f0b 100644 --- a/src/power/p-comms.c +++ b/src/power/p-comms.c @@ -47,6 +47,11 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) lwsl_info("%s: len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps); lwsl_hexdump_info(buf, len); + lws_start_foreach_dll(struct lws_dll2 *, px, sps->sai_plat_owner.head) { + saip_server_plat_t *sp = lws_container_of(px, saip_server_plat_t, list); + sp->needed = 0; + } lws_end_foreach_dll(px); + while (p < end) { n = 0; while (p < end && *p != ',') @@ -58,41 +63,68 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) p++; /* - * Does this server list this platform? + * Does this server list this platform as having startable or ongoing + * tasks? */ lws_start_foreach_dll(struct lws_dll2 *, px, sps->sai_plat_owner.head) { saip_server_plat_t *sp = lws_container_of(px, saip_server_plat_t, list); + int hit = 0; - if (!strcmp(sp->name, plat)) { - /* - * Server said this platform has pending jobs. - * sai-power config says this builder can do - * jobs on that platform. Let's make sure it - * is powered on. - */ - if (!strcmp(sp->power_on_type, "wol")) { - lwsl_notice("%s: triggering WOL\n", __func__); - write(lws_spawn_get_fd_stdxxx(lsp_wol, 0), - sp->power_on_mac, strlen(sp->power_on_mac)); - } + /* + * How about any dependency listed? + */ + + lws_start_foreach_dll(struct lws_dll2 *, px1, sp->dependencies_owner.head) { + saip_server_plat_t *sp1 = lws_container_of(px1, saip_server_plat_t, dependencies_list); - if (!strcmp(sp->power_on_type, "tasmota")) { - if (lws_ss_create(lws_ss_cx_from_user(pss), - 0, &ssi_saip_smartplug_t, - (void *)sp->power_on_url, NULL, NULL, NULL)) { - lwsl_err("%s: failed to create smartplug secure stream\n", - __func__); - } + if (!strcmp(sp1->name, plat)) { + sp->needed = 1; + break; } - } + + } lws_end_foreach_dll(px1); + + if (hit || !strcmp(sp->name, plat)) + sp->needed = 1; } lws_end_foreach_dll(px); } - (void)sps; + lws_start_foreach_dll(struct lws_dll2 *, px, sps->sai_plat_owner.head) { + saip_server_plat_t *sp = lws_container_of(px, saip_server_plat_t, list); + + if (sp->needed) { + lwsl_notice("%s: %s: needed\n", __func__, sp->name); + + /* + * Server said this platform or at least one dependency + * has pending jobs. sai-power config says this builder + * can do jobs on that platform. Let's make sure it + * is powered on. + */ + if (!strcmp(sp->power_on_type, "wol")) { + lwsl_notice("%s: triggering WOL\n", __func__); + write(lws_spawn_get_fd_stdxxx(lsp_wol, 0), + sp->power_on_mac, strlen(sp->power_on_mac)); + } + if (!strcmp(sp->power_on_type, "tasmota")) { + lwsl_notice("%s: starting tasmota\n", __func__); + if (lws_ss_create(lws_ss_cx_from_user(pss), + 0, &ssi_saip_smartplug_t, + (void *)sp->power_on_url, NULL, NULL, NULL)) { + lwsl_err("%s: failed to create smartplug secure stream\n", + __func__); + } + } + } else + lwsl_notice("%s: (%s not needed)\n", __func__, sp->name); + + } lws_end_foreach_dll(px); + + (void)sps; return 0; } diff --git a/src/power/p-conf.c b/src/power/p-conf.c index a907a80..2cd50a6 100644 --- a/src/power/p-conf.c +++ b/src/power/p-conf.c @@ -35,6 +35,7 @@ static const char * const paths_global[] = { "servers[].url", "servers[].platforms[].name", "servers[].platforms[].host", + "servers[].platforms[].depends", "servers[].platforms[].power-on.type", "servers[].platforms[].power-on.mac", "servers[].platforms[].power-on.url", @@ -52,6 +53,7 @@ enum enum_paths_global { LEJPM_SERVERS_URL, LEJPM_SERVERS_PLATFORMS_NAME, LEJPM_SERVERS_PLATFORMS_HOST, + LEJPM_SERVERS_PLATFORMS_DEPENDS, LEJPM_SERVERS_PLATFORMS_POWER_ON_TYPE, LEJPM_SERVERS_PLATFORMS_POWER_ON_MAC, LEJPM_SERVERS_PLATFORMS_POWER_ON_URL, @@ -149,6 +151,10 @@ saip_conf_global_cb(struct lejp_ctx *ctx, char reason) pp = &a->sai_server_plat->host; break; + case LEJPM_SERVERS_PLATFORMS_DEPENDS: + pp = &a->sai_server_plat->depends; + break; + case LEJPM_SERVERS_PLATFORMS_POWER_ON_TYPE: pp = &a->sai_server_plat->power_on_type; break; @@ -221,6 +227,29 @@ saip_config_global(struct sai_power *power, const char *d) n = (int)ctx.line; lejp_destruct(&ctx); + /* let's wire up any dependencies so the dependent knows easily */ + + 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 (sp->depends) { + saip_server_plat_t *d = find_platform(power, sp->depends); + + if (!d) { + lwsl_err("%s: can't find dependency '%s' on conf for '%s'\n", __func__, + sp->depends, sp->host); + return 1; + } + + lws_dll2_add_tail(&d->dependencies_list, &sp->dependencies_owner); + } + + } lws_end_foreach_dll(px1); + } lws_end_foreach_dll(px); + return 0; } diff --git a/src/power/p-private.h b/src/power/p-private.h index 0a3bf94..fe08d82 100644 --- a/src/power/p-private.h +++ b/src/power/p-private.h @@ -58,11 +58,14 @@ struct saip_ws_pss; typedef struct saip_server_plat { struct lws_dll2 list; + lws_dll2_owner_t dependencies_owner; + lws_dll2_t dependencies_list; lws_sorted_usec_list_t sul_delay_off; const char *name; const char *host; + const char *depends; /* depended-on plat must stay powered if we need power */ const char *power_on_type; const char *power_on_url; const char *power_on_mac; @@ -70,6 +73,7 @@ typedef struct saip_server_plat { const char *power_off_url; char stay; + char needed; } saip_server_plat_t; @@ -109,6 +113,10 @@ struct sai_power { const char *port; /* port we listen on */ }; +saip_server_plat_t * +find_platform(struct sai_power *pwr, const char *host); + + struct jpargs { struct sai_power *power; diff --git a/src/power/p-sai.c b/src/power/p-sai.c index 999b982..b9bda4a 100644 --- a/src/power/p-sai.c +++ b/src/power/p-sai.c @@ -182,6 +182,7 @@ static const struct lws_protocols *pprotocols[] = { &protocol_std, NULL }; + static void saip_sul_action_power_off(struct lws_sorted_usec_list *sul) { @@ -202,6 +203,25 @@ saip_sul_action_power_off(struct lws_sorted_usec_list *sul) } } +saip_server_plat_t * +find_platform(struct sai_power *pwr, const char *host) +{ + lws_start_foreach_dll(struct lws_dll2 *, px, pwr->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 (!strcmp(host, sp->host)) + return sp; + + } lws_end_foreach_dll(px1); + } lws_end_foreach_dll(px); + + return NULL; +} + + /* * local-side h1 server for builders to connect to */ @@ -242,31 +262,14 @@ local_srv_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, return r; } -static saip_server_plat_t * -find_platform(const char *host) -{ - 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 (!strcmp(host, sp->host)) - return sp; - - } lws_end_foreach_dll(px1); - } lws_end_foreach_dll(px); - - return NULL; -} - static lws_ss_state_return_t local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, lws_ss_tx_ordinal_t ack) { local_srv_t *g = (local_srv_t *)userobj; - saip_server_plat_t *sp; char *path = NULL, pn[128]; + saip_server_plat_t *sp; + int apo = 0; size_t len; // lwsl_ss_user(lws_ss_from_user(g), "state %s", lws_ss_state_name((int)state)); @@ -301,7 +304,7 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, if (len > 6 && !strncmp(path, "/stay/", 6)) { lws_strnncpy(pn, &path[6], len - 6, sizeof(pn)); - sp = find_platform(pn); + sp = find_platform(&power, pn); if (sp) g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), @@ -314,7 +317,7 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, if (len > 10 && !strncmp(path, "/power-on/", 10)) { lws_strnncpy(pn, &path[10], len - 10, sizeof(pn)); - sp = find_platform(pn); + sp = find_platform(&power, pn); if (!sp) { g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), "Unable to find host %s", pn); @@ -355,6 +358,12 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, goto bail; } + if (len > 16 && !strncmp(path, "/auto-power-off/", 16)) { + apo = 1; + lws_strnncpy(pn, &path[16], len - 16, sizeof(pn)); + goto power_off; + } + if (len < 11 || strncmp(path, "/power-off/", 11)) { g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), "URL path needs to start with /power-off/"); @@ -363,6 +372,8 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, lws_strnncpy(pn, &path[11], len - 11, sizeof(pn)); +power_off: + /* * Let's have a look at the platform */ @@ -370,11 +381,36 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), "Unable to find host %s", pn); - sp = find_platform(pn); + sp = find_platform(&power, pn); if (sp) { + + if (apo) { + char needs[128]; + + /* + * Since it's not a manual request, + * we should deny it if any deps still need us + */ + + needs[0] = '\0'; + lws_start_foreach_dll(struct lws_dll2 *, px1, sp->dependencies_owner.head) { + saip_server_plat_t *sp1 = lws_container_of(px1, saip_server_plat_t, dependencies_list); + + if (sp1->needed) + lws_snprintf(needs, sizeof(needs) - 1 - strlen(needs), "%s ", sp1->name); + + } lws_end_foreach_dll(px1); + + if (needs[0] || sp->needed) { + g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), + "NAK: %s and/or dependencies still active: %s", pn, needs); + goto bail; + } + } + /* - * OK this is it, schedule it to happen - */ + * OK this is it, schedule it to happen + */ lws_sul_schedule(lws_ss_cx_from_user(g), 0, &sp->sul_delay_off, saip_sul_action_power_off, @@ -384,7 +420,7 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state, __func__, sp->host); g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload), - "Scheduled powering off host %s", sp->host); + "ACK: Scheduled powering off host %s", sp->host); sp->stay = 0; /* reset any manual power up */ } diff --git a/src/power/p-smartplug.c b/src/power/p-smartplug.c index 3a035dd..a0503f3 100644 --- a/src/power/p-smartplug.c +++ b/src/power/p-smartplug.c @@ -38,13 +38,13 @@ saip_spc_state(void *userobj, void *sh, lws_ss_constate_t state, saip_smartplug_t *pss = (saip_smartplug_t *)userobj; const char *op_url = (const char *)lws_ss_opaque_from_user(pss); -// lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state), -// (unsigned int)ack); + // lwsl_user("%s: %s, ord 0x%x\n", __func__, lws_ss_state_name((int)state), + // (unsigned int)ack); switch (state) { case LWSSSCS_CREATING: -// lwsl_notice("%s: binding ss to %s\n", __func__, op_url); + lwsl_user("%s: binding ss to %s\n", __func__, op_url); if (lws_ss_set_metadata(lws_ss_from_user(pss), "url", op_url, strlen(op_url))) @@ -52,6 +52,11 @@ saip_spc_state(void *userobj, void *sh, lws_ss_constate_t state, return lws_ss_client_connect(lws_ss_from_user(pss)); + case LWSSSCS_QOS_ACK_REMOTE: + case LWSSSCS_QOS_NACK_REMOTE: + case LWSSSCS_ALL_RETRIES_FAILED: + return LWSSSSRET_DESTROY_ME; + default: break; } diff --git a/src/server/s-task.c b/src/server/s-task.c index 74455d4..e0a6f58 100644 --- a/src/server/s-task.c +++ b/src/server/s-task.c @@ -584,7 +584,7 @@ sais_platforms_with_tasks_pending(struct vhd *vhd) /* * Iterate through the events looking at his event-specific database - * for platforms that have pending tasks... + * for platforms that have pending or ongoing tasks... */ lws_start_foreach_dll(struct lws_dll2 *, p, o.head) { @@ -597,7 +597,7 @@ sais_platforms_with_tasks_pending(struct vhd *vhd) if (sqlite3_prepare_v2(pdb, "select distinct platform " "from tasks where " - "(state == 0 or state == 1)", -1, &sm, + "(state = 0 or state = 1 or state = 2)", -1, &sm, NULL) != SQLITE_OK) { lwsl_err("%s: Unable to %s\n", __func__, sqlite3_errmsg(pdb));
Page fetched 0s ago, creation time: 5ms (vhost etag hits: 0%, cache hits: 0%)