aboutsummaryrefslogtreecommitdiff
path: root/initd/config.c
blob: fa12ab2c3bd08c18ff39adac544b058619291c2f (plain)
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
/* SPDX-License-Identifier: ISC */
#include "init.h"

/* service configurations */
static service_list_t cfg;

/* service run time data, sorted by target and topological order */
static svc_run_data_t *rt_data = NULL;
static size_t rt_count = 0;

/* maps a target to range in rt_data */
static size_t queue_start[TGT_MAX];
static size_t queue_count[TGT_MAX];

/* current state */
static size_t queue_idx = 0;
static int target = TGT_BOOT;
static svc_run_data_t *waiting = NULL;

int config_load(void)
{
	service_t *it;
	size_t i, j;

	if (svcscan(SVCDIR, &cfg))
		return -1;

	/* allocate run time data */
	rt_count = 0;

	for (i = 0; i < TGT_MAX; ++i) {
		for (it = cfg.targets[i]; it != NULL; it = it->next)
			++rt_count;
	}

	rt_data = calloc(rt_count, sizeof(rt_data[0]));
	if (rt_data == NULL) {
		rt_count = 0;
		del_svc_list(&cfg);
		return -1;
	}

	/* map runtime data to services */
	j = 0;

	for (i = 0; i < TGT_MAX; ++i) {
		queue_start[i] = j;

		for (it = cfg.targets[i]; it != NULL; it = it->next) {
			rt_data[j].svc = it;
			rt_data[j].state = STATE_OFF;
			rt_data[j].pid = -1;
			++j;
		}

		queue_count[i] = j - queue_start[i];
	}

	/* initialize state */
	for (i = 0; i < queue_count[target]; ++i)
		rt_data[queue_start[target] + i].state = STATE_QUEUED;

	return 0;
}

void config_set_waiting(svc_run_data_t *rt)
{
	assert(waiting == NULL);
	waiting = rt;
}

bool config_is_waiting(void)
{
	if (waiting != NULL) {
		if (waiting->state == STATE_RUNNING)
			return true;

		waiting = NULL;
	}

	return false;
}

svc_run_data_t *config_rt_data_by_pid(pid_t pid)
{
	size_t i;

	if (pid <= 1)
		return NULL;

	for (i = 0; i < rt_count; ++i) {
		if (rt_data[i].pid == pid)
			return rt_data + i;
	}

	return NULL;
}

svc_run_data_t *config_dequeue(void)
{
	if (config_is_waiting())
		return NULL;

	if (queue_idx >= queue_count[target])
		return NULL;

	return rt_data + queue_start[target] + queue_idx++;
}

void config_set_target(int tgt)
{
	if (target == TGT_REBOOT || target == TGT_SHUTDOWN || target == tgt)
		return;

	/* TODO:
	    - if there are services left in the queue of the current target:
	      - refuse unless the new target is shutdown/reboot?
	      - mark pending as "STATE_OFF"
	    - if the new target is reboot/shutdown
	      - enqueue the currently running services as "stop those"?
	 */

	target = tgt;
	queue_idx = 0;
	waiting = NULL;
}

bool config_should_respawn(void)
{
	return target != TGT_REBOOT && target != TGT_SHUTDOWN;
}