diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1de9f1c..fe0b18f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -130,7 +130,12 @@ set (CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${LIBWEBSOCKETS_INCLUDE
set(requirements 1)
require_lws_config(LWS_ROLE_H1 1 requirements)
require_lws_config(LWS_ROLE_WS 1 requirements)
-require_lws_config(LWS_WITH_JOSE 1 requirements)
+if (SAI_SERVER)
+ require_lws_config(LWS_WITH_JOSE 1 requirements)
+endif()
+if (SAI_BUILDER)
+ require_lws_config(LWS_WITH_THREADPOOL 1 requirements)
+endif()
#require_lws_config(LWS_WITH_PLUGINS_BUILTIN 1 requirements)
diff --git a/src/builder/b-conf.c b/src/builder/b-conf.c
index f50004e..ef87972 100644
--- a/src/builder/b-conf.c
+++ b/src/builder/b-conf.c
@@ -35,7 +35,8 @@ static const char * const paths_global[] = {
"host",
"metrics_uri",
"metrics_path",
- "metrics_secret"
+ "metrics_secret",
+ "power-off"
};
enum enum_paths_global {
@@ -45,6 +46,7 @@ enum enum_paths_global {
LEJPM_METRICS_URI,
LEJPM_METRICS_PATH,
LEJPM_METRICS_SECRET,
+ LEJPM_POWER_OFF,
};
/* platform-related part */
@@ -285,6 +287,10 @@ saib_conf_global_cb(struct lejp_ctx *ctx, char reason)
pp = &a->builder->metrics_secret;
break;
+ case LEJPM_POWER_OFF:
+ pp = &a->builder->power_off;
+ break;
+
default:
return 0;
}
diff --git a/src/builder/b-nspawn.c b/src/builder/b-nspawn.c
index af510d8..57a4a26 100644
--- a/src/builder/b-nspawn.c
+++ b/src/builder/b-nspawn.c
@@ -49,7 +49,7 @@ saib_log_chunk_create(struct sai_nspawn *ns, void *buf, size_t len, int channel)
{
struct ws_capture_chunk *chunk;
- if (!ns->spm)
+ if (!ns || !ns->spm)
return NULL;
chunk = malloc(sizeof(*chunk) + len);
@@ -95,8 +95,6 @@ callback_sai_stdwsi(struct lws *wsi, enum lws_callback_reasons reason,
break;
case LWS_CALLBACK_RAW_RX_FILE:
- if (!ns->spm)
- return -1;
#if defined(WIN32)
{
DWORD rb;
@@ -116,7 +114,10 @@ callback_sai_stdwsi(struct lws *wsi, enum lws_callback_reasons reason,
len = (unsigned int)ilen;
- // printf("(%d) %.*s\n", (int)len, (int)len, buf);
+ if (!ns->spm) {
+ printf("%s: (%d) %.*s\n", __func__, (int)lws_spawn_get_stdfd(wsi), (int)len, buf);
+ return -1;
+ }
if (!saib_log_chunk_create(ns, buf, len, lws_spawn_get_stdfd(wsi)))
return -1;
diff --git a/src/builder/b-private.h b/src/builder/b-private.h
index f80ed28..c481c15 100644
--- a/src/builder/b-private.h
+++ b/src/builder/b-private.h
@@ -39,6 +39,8 @@
#include <pthread.h>
#include <git2.h>
+#define SAI_IDLE_GRACE_US (20 * LWS_US_PER_SEC)
+
typedef enum {
PHASE_IDLE,
@@ -156,14 +158,20 @@ struct sai_builder {
lws_dll2_owner_t sai_plat_server_owner; /* servers we connect to */
lws_dll2_owner_t devices_owner; /* sai_serial_t */
+ struct sai_nspawn suspend_nspawn;
+
struct lwsac *conf_head;
struct lws_context *context;
struct lws_vhost *vhost;
+ lws_sorted_usec_list_t sul_idle;
+
const char *metrics_uri;
const char *metrics_path;
const char *metrics_secret;
+ const char *power_off;
+
const char *home; /* home dir, usually /sai/home */
const char *perms; /* user:group */
@@ -250,6 +258,10 @@ extern const struct lws_protocols protocol_logproxy, protocol_resproxy;
void *
thread_repo(void *d);
+void *
+thread_suspend(void *d);
+
+
int
saib_create_resproxy_listen_uds(struct lws_context *context,
struct sai_plat_server *spm);
diff --git a/src/builder/b-sai.c b/src/builder/b-sai.c
index 2dc3c0c..1287bfe 100644
--- a/src/builder/b-sai.c
+++ b/src/builder/b-sai.c
@@ -52,6 +52,7 @@ int getpid(void) { return 0; }
static const char *config_dir = "/etc/sai/builder";
static int interrupted;
static lws_state_notify_link_t nl;
+static struct lws_spawn_piped *lsp_suspender;
struct sai_builder builder;
@@ -386,6 +387,32 @@ app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
return 0;
}
+/*
+ * The grace time is up, ask for the suspend
+ */
+
+void
+sul_idle_cb(lws_sorted_usec_list_t *sul)
+{
+ ssize_t n;
+ uint8_t te = 1;
+
+ lwsl_notice("%s: requesting suspend...\n", __func__);
+
+ n = write(lws_spawn_get_fd_stdxxx(lsp_suspender, 0), &te, 1);
+ if (n == 1) {
+ sleep(2);
+ /*
+ * There were 0 tasks ongoing for us to suspend, start off
+ * with the same assumption and set the idle grace time
+ */
+ lws_sul_schedule(builder.context, 0, &builder.sul_idle,
+ sul_idle_cb, SAI_IDLE_GRACE_US);
+ lwsl_notice("%s: resuming after suspend\n", __func__);
+ } else
+ lwsl_err("%s: failed to request suspend\n", __func__);
+}
+
static lws_state_notify_link_t * const app_notifier_list[] = {
&nl, NULL
};
@@ -428,6 +455,48 @@ int main(int argc, const char **argv)
const char *p;
void *retval;
+
+ if ((p = lws_cmdline_option(argc, argv, "-s"))) {
+ ssize_t n = 0;
+
+ printf("%s: Spawn process creation entry...\n", __func__);
+
+ /*
+ * A new process gets started with this option before we drop
+ * privs. This allows us to suspend with root privs later.
+ *
+ * We just wait until we get a byte on stdin from the main
+ * process indicating we should suspend.
+ */
+
+ while (n >= 0) {
+ int status;
+ uint8_t d;
+ pid_t p;
+
+ n = read(0, &d, 1);
+ lwsl_notice("%s: suspend process read returned %d\n", __func__, (int)n);
+
+ if (n <= 0)
+ continue;
+
+ if (n == 1 && d == 2) {
+ lwsl_warn("%s: suspend process ending\n", __func__);
+ break;
+ }
+
+ p = fork();
+ if (!p)
+ execl("/usr/bin/systemctl", "/usr/bin/systemctl", "suspend", NULL);
+ else
+ waitpid(p, &status, 0);
+ }
+
+ lwsl_notice("%s: exiting suspend process\n", __func__);
+
+ return 0;
+ }
+
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
@@ -562,6 +631,31 @@ int main(int argc, const char **argv)
goto bail;
}
+ if (!strcmp(builder.power_off, "suspend")) {
+ struct lws_spawn_piped_info info;
+ const char * const ea[] = { argv[0], "-s", NULL };
+
+ memset(&info, 0, sizeof(info));
+ memset(&builder.suspend_nspawn, 0, sizeof(builder.suspend_nspawn));
+
+ info.vh = builder.vhost;
+ info.exec_array = ea;
+ info.max_log_lines = 100;
+ info.opaque = (void *)&builder.suspend_nspawn;
+
+ lsp_suspender = lws_spawn_piped(&info);
+ if (!lsp_suspender)
+ lwsl_notice("%s: suspend spawn failed\n", __func__);
+
+ /*
+ * We start off idle, with no tasks on any platform and doing
+ * the grace time before suspend
+ */
+
+ lws_sul_schedule(builder.context, 0, &builder.sul_idle,
+ sul_idle_cb, SAI_IDLE_GRACE_US);
+ }
+
pthread_mutex_init(&builder.mi.mut, NULL);
pthread_cond_init(&builder.mi.cond, NULL);
@@ -589,6 +683,16 @@ int main(int argc, const char **argv)
bail:
+ if (!strcmp(builder.power_off, "suspend") && lsp_suspender) {
+ uint8_t te = 2;
+
+ /*
+ * Clean up after the suspend process
+ */
+
+ write(lws_spawn_get_fd_stdxxx(lsp_suspender, 0), &te, 1);
+ }
+
/* destroy the unique servers */
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
@@ -621,13 +725,15 @@ bail:
saib_config_destroy(&builder);
+ if (!strcmp(builder.power_off, "suspend"))
+ lws_sul_cancel(&builder.sul_idle);
+
/*
- * Clean up after the threads
+ * Clean up after the spawn threads
*/
- builder.mi.finish = 1;
-
pthread_mutex_lock(&builder.mi.mut);
+ builder.mi.finish = 1;
pthread_cond_broadcast(&builder.mi.cond);
pthread_mutex_unlock(&builder.mi.mut);
@@ -636,8 +742,6 @@ bail:
pthread_mutex_destroy(&builder.mi.mut);
pthread_cond_destroy(&builder.mi.cond);
-
-
lws_context_destroy(builder.context);
return 0;
diff --git a/src/builder/b-task.c b/src/builder/b-task.c
index 5bb14fc..3b42437 100644
--- a/src/builder/b-task.c
+++ b/src/builder/b-task.c
@@ -165,6 +165,32 @@ saib_task_destroy(struct sai_nspawn *ns)
ns->sp->ongoing, ns->sp->ongoing - 1);
ns->sp->ongoing--;
+ if (!strcmp(builder.power_off, "suspend") && !ns->sp->ongoing) {
+ int m = 0;
+
+ /*
+ * Is it the case that none of the platforms have
+ * any ongoing jobs then? We don't any more.
+ *
+ * If nobody does, start the grace time for suspend.
+ */
+
+ lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
+ builder.sai_plat_owner.head) {
+ struct sai_plat *sp = lws_container_of(d,
+ struct sai_plat, sai_plat_list);
+ if (sp->ongoing)
+ m++;
+ } lws_end_foreach_dll_safe(d, d1);
+
+ if (!m) {
+ lwsl_notice("%s: scheduling suspend grace time\n", __func__);
+ lws_sul_schedule(builder.context, 0,
+ &builder.sul_idle, sul_idle_cb,
+ SAI_IDLE_GRACE_US);
+ }
+ }
+
/*
* Schedule informing all the servers we're connected to
*/
@@ -692,6 +718,12 @@ saib_ws_json_rx_builder(struct sai_plat_server *spm, const void *in, size_t len)
sp->ongoing++;
ns->task->told_ongoing = 1;
+ if (!strcmp(builder.power_off, "suspend")) {
+ /* we're busy, we're not in the mood for suspending */
+ lwsl_notice("%s: cancelling suspend grace time\n", __func__);
+ lws_sul_cancel(&ns->builder->sul_idle);
+ }
+
/*
* Let the mirror thread get on with things...
*
diff --git a/src/common/include/private.h b/src/common/include/private.h
index 3d114c8..4a024e7 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -298,6 +298,7 @@ typedef struct sai_plat {
typedef struct sai_plat_owner {
lws_dll2_owner_t plat_owner;
+
} sai_plat_owner_t;
typedef struct sai_repo {
@@ -357,3 +358,6 @@ saicom_lp_ss_from_env(struct lws_context *context, const char *env_name);
int
saicom_lp_callback_on_drain(saicom_drain_cb cb, void *opaque);
+
+void
+sul_idle_cb(lws_sorted_usec_list_t *sul);