/* SPDX-License-Identifier: ISC */ #include "init.h" static void respawn(svc_run_data_t *rt) { if (rt->svc->rspwn_limit > 0) { rt->rspwn_count += 1; if (rt->rspwn_count >= rt->svc->rspwn_limit) goto fail; } rt->pid = runsvc(rt->svc); if (rt->pid == -1) goto fail; rt->state = STATE_RUNNING; return; fail: rt->state = STATE_FAILED; print_status(rt); return; } static svc_run_data_t *wait_for_process(void) { svc_run_data_t *rt; int status; pid_t pid; do { pid = wait(&status); if (pid == -1) { if (errno == EINTR) return NULL; if (errno == ECHILD) exit(EXIT_FAILURE); } rt = config_rt_data_by_pid(pid); } while (rt == NULL); rt->status = WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE; rt->pid = -1; if (rt->svc->type == SVC_RESPAWN) { if (config_should_respawn()) respawn(rt); } else { if (rt->status == EXIT_SUCCESS) { rt->state = STATE_COMPLETED; } else { rt->state = STATE_FAILED; } print_status(rt); if (rt->svc->type == SVC_ONCE) config_singleshot_terminated(); } return rt; } static void handle_signal(int signo) { switch (signo) { case SIGTERM: config_set_target(TGT_SHUTDOWN); break; case SIGINT: config_set_target(TGT_REBOOT); break; case SIGHUP: break; case SIGUSR1: break; } } static void check_target_completion(void) { if (!config_is_current_target_complete()) return; switch (config_get_current_target()) { case TGT_BOOT: break; case TGT_SHUTDOWN: for (;;) reboot(RB_POWER_OFF); break; case TGT_REBOOT: for (;;) reboot(RB_AUTOBOOT); break; } } int main(void) { svc_run_data_t *rt, *terminated; struct sigaction act; if (config_load()) return EXIT_FAILURE; memset(&act, 0, sizeof(act)); act.sa_handler = handle_signal; sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGUSR1, &act, NULL); if (reboot(LINUX_REBOOT_CMD_CAD_OFF)) perror("cannot disable CTRL+ALT+DEL"); for (;;) { rt = config_dequeue(); if (rt == NULL) { terminated = wait_for_process(); if (terminated == NULL) continue; } else if (rt->svc->flags & SVC_FLAG_HAS_EXEC) { rt->pid = runsvc(rt->svc); if (rt->pid == -1) { rt->state = STATE_FAILED; print_status(rt); } else { rt->state = STATE_RUNNING; print_status(rt); switch (rt->svc->type) { case SVC_WAIT: do { terminated = wait_for_process(); } while (terminated != rt); break; case SVC_ONCE: config_singleshot_started(); break; } } } else { rt->status = EXIT_SUCCESS; rt->state = STATE_COMPLETED; print_status(rt); } check_target_completion(); } return EXIT_SUCCESS; }