init/initd/supervisor.c

167 lines
3.0 KiB
C

/* SPDX-License-Identifier: ISC */
#include "init.h"
static service_list_t cfg;
static int target = -1;
static service_t *running = NULL;
static service_t *terminated = NULL;
static service_t *queue = NULL;
static int singleshot = 0;
static bool waiting = false;
static int start_service(service_t *svc)
{
svc->pid = runsvc(svc);
if (svc->pid == -1) {
print_status(svc->desc, STATUS_FAIL, false);
delsvc(svc);
return -1;
}
svc->next = running;
running = svc;
return 0;
}
static void handle_terminated_service(service_t *svc)
{
switch (svc->type) {
case SVC_RESPAWN:
if (target == TGT_REBOOT || target == TGT_SHUTDOWN)
break;
if (svc->rspwn_limit > 0) {
svc->rspwn_limit -= 1;
if (svc->rspwn_limit == 0) {
print_status(svc->desc, STATUS_FAIL, false);
break;
}
}
start_service(svc);
return;
case SVC_WAIT:
waiting = false;
print_status(svc->desc,
svc->status == EXIT_SUCCESS ?
STATUS_OK : STATUS_FAIL, true);
if (singleshot == 0 && queue == NULL)
target_completed(target);
break;
case SVC_ONCE:
singleshot -= 1;
print_status(svc->desc,
svc->status == EXIT_SUCCESS ?
STATUS_OK : STATUS_FAIL, false);
if (singleshot == 0 && queue == NULL && !waiting)
target_completed(target);
break;
}
delsvc(svc);
}
void supervisor_handle_exited(pid_t pid, int status)
{
service_t *prev = NULL, *svc = running;
while (svc != NULL && svc->pid != pid) {
prev = svc;
svc = svc->next;
}
if (svc == NULL)
return;
if (prev != NULL) {
prev->next = svc->next;
} else {
running = svc->next;
}
svc->status = status;
svc->next = terminated;
terminated = svc;
}
void supervisor_set_target(int next)
{
service_t *svc;
if (target == TGT_REBOOT || target == TGT_SHUTDOWN || next == target)
return;
if (next == TGT_REBOOT || next == TGT_SHUTDOWN) {
while (queue != NULL) {
svc = queue;
queue = queue->next;
delsvc(svc);
}
}
if (queue != NULL) {
for (svc = queue; svc->next != NULL; svc = svc->next)
;
svc->next = cfg.targets[next];
} else {
queue = cfg.targets[next];
}
cfg.targets[next] = NULL;
target = next;
}
void supervisor_init(void)
{
int status = STATUS_OK;
if (svcscan(SVCDIR, &cfg, RDSVC_NO_EXEC | RDSVC_NO_CTTY))
status = STATUS_FAIL;
target = TGT_BOOT;
queue = cfg.targets[TGT_BOOT];
cfg.targets[TGT_BOOT] = NULL;
print_status("reading configuration from " SVCDIR, status, false);
}
bool supervisor_process_queues(void)
{
service_t *svc;
if (terminated != NULL) {
svc = terminated;
terminated = terminated->next;
handle_terminated_service(svc);
return true;
}
if (waiting || queue == NULL)
return false;
svc = queue;
queue = queue->next;
if (start_service(svc) != 0)
return true;
switch (svc->type) {
case SVC_WAIT:
print_status(svc->desc, STATUS_WAIT, false);
waiting = true;
break;
case SVC_RESPAWN:
print_status(svc->desc, STATUS_STARTED, false);
break;
case SVC_ONCE:
singleshot += 1;
break;
}
if (singleshot == 0 && queue == NULL && !waiting)
target_completed(target);
return true;
}