1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
/*
 * Sai builder definitions src/builder/b-private.h
 *
 * Copyright (C) 2019 - 2025 Andy Green <andy@warmcat.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation:
 *  version 2.1 of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA  02110-1301  USA
 */

#include "../common/include/private.h"

#if !defined(WIN32)
#include <pwd.h>
#include <grp.h>
#endif

#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#endif

#if defined(__APPLE__)
#include <sys/stat.h>	/* for mkdir() */
#include <sys/wait.h>
#endif

#if defined(WIN32)
#include <initguid.h>
#include <KnownFolders.h>
#include <Shlobj.h>
#include <processthreadsapi.h>
#include <handleapi.h>


#if !defined(PATH_MAX)
#define PATH_MAX MAX_PATH
#endif
#endif

#include <sys/stat.h>
#if defined(WIN32)
#include <direct.h>
#define read _read
#define open _open
#define close _close
#define write _write
#define mkdir(x,y) _mkdir(x)
#define rmdir _rmdir
#define unlink _unlink
#if defined(pid_t)
#undef pid_t
#endif
#endif
#include <pthread.h>

extern char suspender_exists;

struct lws_spawn_piped;
struct lws_stub_manager;

struct saib_opaque_spawn {
	struct sai_nspawn	*ns;
	struct lws_spawn_piped	*lsp;
	char			*spawn;
	lws_usec_t		start_time;
};

#define SAI_LOAD_REPORT_US			(1 * LWS_US_PER_SEC)
#define SAI_IDLE_GRACE_US			(builder.one_shot_active ? (10 * LWS_US_PER_SEC) : \
						 builder.event_affinity_active ? (15 * LWS_US_PER_SEC) : \
						 (30 * LWS_US_PER_SEC))
/* let the powering_down plats reach the servers before we act */
#define SAI_POWER_NOTIFY_FLUSH_US		(2 * LWS_US_PER_SEC)
/* suspend byte written: if we are still awake after this, it failed */
#define SAI_POWER_SUSPEND_DEADLINE_US		(15 * LWS_US_PER_SEC)
/* how long to wait for sai-power to ACK / NAK an auto-power-off */
#define SAI_POWER_OFF_REPLY_US			(10 * LWS_US_PER_SEC)
/* sai-power ACKed and we asked for shutdown: sai-power's own holdoff is 50s */
#define SAI_POWER_OFF_DEADLINE_US		(90 * LWS_US_PER_SEC)
/* after a failed power action, before we consider going idle again */
#define SAI_POWER_RETRY_HOLDOFF_US		(5 * 60 * LWS_US_PER_SEC)
#define SAI_STAY_POLL_US			(20 * LWS_US_PER_SEC)
#define SAI_CLEANUP_JOBS_INTERVAL_US		(60ULL * 60ULL * LWS_US_PER_SEC)
#define SAI_CLEANUP_JOB_DIR_MIN_AGE_SECS	(24ull * 3600u)


struct saib_ws_pss;

enum nsstate {
	NSSTATE_INIT,
	NSSTATE_MOUNTING,
	NSSTATE_EXECUTING_STEPS,
	NSSTATE_DONE,
	NSSTATE_UPLOADING_ARTIFACTS,
	NSSTATE_FAILED,
};

/*
 * Auto power management, see saib_power_event() in b-power.c
 */

enum saib_power_state {
	SAIB_PWR_ACTIVE,	/* tasks, shells or stay: nothing pending */
	SAIB_PWR_IDLE,		/* nothing running, idle grace timer running */
	SAIB_PWR_HOLDOFF,	/* a power action failed, waiting before retry */
	SAIB_PWR_SUSPEND_WAIT,	/* servers told we're going, flushing before suspend */
	SAIB_PWR_SUSPENDING,	/* suspend requested, waiting for it to happen */
	SAIB_PWR_OFF_REQ,	/* asked sai-power to power us off, awaiting reply */
	SAIB_PWR_OFF_WAIT,	/* sai-power agreed, shutdown requested, waiting */
};

enum saib_power_event {
	SAIB_PWR_EV_BUSY,	/* a task or shell started, or stay asserted */
	SAIB_PWR_EV_IDLE,	/* nothing running and no stay */
	SAIB_PWR_EV_TIMER,	/* the current state's deadline expired */
	SAIB_PWR_EV_POWER_ACK,	/* sai-power scheduled our power-off */
	SAIB_PWR_EV_POWER_NAK,	/* sai-power declined or is unreachable */
};

/*
 * This represents this builder process as a whole
 */

struct sai_shell {
	struct lws_dll2		list;
	char			task_uuid[33];
	struct sai_plat_server	*spm;
	struct lws_spawn_piped	*lsp;
	struct lws		*stdwsi[3];
	char			stdwsi_paused[3];
	int			user_cancel;
};

struct sai_builder {
	lws_dll2_owner_t	sai_plat_owner; /* list of platforms we offer */
	lws_dll2_owner_t	sai_plat_server_owner; /* servers we connect to */
	lws_dll2_owner_t	devices_owner; /* sai_serial_t */
	lws_dll2_owner_t	lsp_owner; /* list of lws_spawn_piped */
	lws_dll2_owner_t	shell_owner; /* list of sai_shell */

	struct lws_ss_handle	*ss_stay;
	struct lws_ss_handle	*ss_power_off;


	struct sai_nspawn	suspend_nspawn;

	struct lwsac		*conf_head;
	struct lws_context	*context;
	struct lws_vhost	*vhost;

	lws_sorted_usec_list_t	sul_power; /* current power state's deadline */
	lws_sorted_usec_list_t	sul_stay;
	lws_sorted_usec_list_t	sul_cleanup_jobs;
	lws_sorted_usec_list_t	sul_deletion_respawn;

#if defined(__APPLE__)
	lws_sorted_usec_list_t	sul_release_wakelock;
	pid_t			wakelock_pid;
#endif

	const char		*metrics_uri;
	const char		*metrics_path;
	const char		*metrics_secret;

	const char		*url_sai_power;
	const char		*power_controller_name;

	const char		*power_off_type;
	const char		*power_off_url;
	const char		*power_on_type;
	const char		*power_on_url;
	const char		*power_on_mac;
	const char		*power_monitor_url;

	const char		*home;		/* home dir, usually /sai/home */
	const char		*perms;		/* user:group */

	const char		*host;		/* prepended before hostname */
	const char		*rebuild_script_user;
	const char		*rebuild_script_root;

	char			path[256];
	char			path_power_off[256];

#if defined(__linux__) || defined(__APPLE__)
	/* For system-wide load calculation */
	uint64_t		last_sys_total;
	uint64_t		last_sys_idle;
#elif defined(WIN32)
	ULARGE_INTEGER		last_sys_total;
	ULARGE_INTEGER		last_sys_idle;
	ULARGE_INTEGER		last_sys_kernel;
	ULARGE_INTEGER		last_sys_user;
#endif
	char			stay;

	enum saib_power_state	power_state;
	time_t			power_action_time; /* wall clock at last command */
	lws_usec_t		power_action_us; /* monotonic at last command */
	int			power_fail_count;
	char			power_unavailable_logged;

	char			event_affinity[65];
	char			event_affinity_active;
	char			one_shot_task_uuid[65];
	char			one_shot_active;

	/* resource management */

	uint64_t		ram_limit_kib;
	uint64_t		ram_reserved_kib;
	uint64_t		disk_total_kib;
	uint64_t		disk_reserved_kib;

	uint16_t		wrap14;
	unsigned int		build_timeout_secs;

#if !defined(WIN32)
	int			pipe_suspender_wr;
#else
	void			*pipe_suspender_wr;
#endif
	struct lws_stub_manager	*mgr_deletion;
};

struct jpargs {
	struct sai_builder	*builder;

	struct sai_nspawn	*nspawn;
	struct sai_plat		*sai_plat;

	struct sai_platform	*pl;

	sai_plat_server_ref_t	*mref;

	int			next_server_index;
	int			next_plat_index;
};

struct ws_capture_chunk {
	struct lws_dll2 list;

	lws_usec_t	us;	/* builder time that we saw this */
	size_t		len;
	uint8_t		stdfd;	/* 1 = stdout, 2 = stderr */

	/* len bytes of data is overallocated after this */
};


extern struct sai_builder builder;
extern const lws_ss_info_t ssi_sai_builder, ssi_sai_mirror, ssi_sai_artifact;
extern const struct lws_protocols protocol_com_warmcat_sai;
int
saib_config_global(struct sai_builder *builder, const char *d);
extern int saib_config(struct sai_builder *builder, const char *d);
extern void saib_config_destroy(struct sai_builder *builder);

int
saib_overlay_mount(struct sai_builder *b, struct sai_nspawn *ns);

int
saib_overlay_unmount(struct sai_nspawn *ns);

int
saib_spawn_script(struct sai_nspawn *ns);

int
saib_prepare_mount(struct sai_builder *b, struct sai_nspawn *ns);

int
saib_ws_json_rx_builder(struct sai_plat_server *spm, const void *in, size_t len);

int
saib_generate(struct sai_plat *sp, char *buf, int len);

enum lws_threadpool_task_return
saib_mirror_task(void *user, enum lws_threadpool_task_status s);

int
saib_set_ns_state(struct sai_nspawn *ns, int state);

void
saib_task_destroy(struct sai_nspawn *ns);

void
saib_task_grace(struct sai_nspawn *ns);

int
saib_log_chunk_create(struct sai_nspawn *ns, void *buf, size_t len, int channel);

int
rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde);

extern const struct lws_protocols protocol_logproxy, protocol_resproxy;

void *
saib_thread_suspend(void *d);


int
saib_create_resproxy_listen_uds(struct lws_context *context,
				struct sai_plat_server *spm);

int
saib_handle_resource_result(struct sai_plat_server *spm, const char *in, size_t len);

void
saib_sul_load_report_cb(struct lws_sorted_usec_list *sul);

int
saib_get_cgroup_cpu(struct sai_nspawn *ns);

int
saib_get_system_cpu(struct sai_builder *b);

int saib_get_cpu_count(void);

unsigned int
saib_get_free_ram_kib(void);

unsigned int
saib_get_total_ram_kib(void);

unsigned int
saib_get_free_disk_kib(const char *path);

unsigned int
saib_get_total_disk_kib(const char *path);

int
saib_create_listen_uds(struct lws_context *context, struct saib_logproxy *lp, struct lws_vhost **);

int
saib_srv_queue_tx(struct lws_ss_handle *h, void *buf, size_t len, unsigned int ss_flags);

int
saib_srv_queue_json_fragments_helper(struct lws_ss_handle *h,
				     const lws_struct_map_t *map,
                                     size_t map_entries, void *object);

int
saib_queue_task_status_update(sai_plat_t *sp, struct sai_plat_server *spm,
			      const char *rej_task_uuid, unsigned int ecode,
			      unsigned int reason);

int
saib_consider_allocating_task(struct sai_plat_server *spm, lws_struct_args_t *a,
			      const uint8_t *in, size_t len, int flags);

void
saib_sul_task_cancel(struct lws_sorted_usec_list *sul);

int
saib_suspender_get_pipe(void);

extern int
saib_suspender_fork(const char *path);
extern int
sai_deletion_worker(const char *home_dir);
extern int
saib_suspender_start(void);
extern int
saib_power_init(void);
extern int
saib_deletion_init(const char *argv0);
int
saib_deletion_request(const char *job);
extern void
suspender_destroy(void);
int
saib_deletion_free_kib(unsigned int needed_kib);
int
saib_reassess_idle_situation(void);
void
saib_power_event(enum saib_power_event ev);
void
saib_power_shutdown(void);

extern int interrupted;

int
saib_app_run(int argc, const char **argv);

void
saib_app_stop(void);

#if defined(__APPLE__)
int
saib_need_wakelock(void);

void
sul_release_wakelock_cb(lws_sorted_usec_list_t *sul);

void
saib_wakelock(void);
#endif