This repository has been archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinit_cmds.c
219 lines (185 loc) · 6.26 KB
/
init_cmds.c
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
#include <string.h>
#include <signal.h>
#include <sys/reboot.h>
#include "init.h"
/* Possible runlevel change commands here:
4 switch to runlevel 4, leaving sublevels unchanged
4- switch to runlevel 4, clear sublevels
4ab switch to runlevel 4, clear sublevels, activate a and b
+ab activate sublevels a and b
-ac deactivate sublevels a and c
The whole thing is an awful overkill for the purposse.
It could and probably should be limited to
* switching a single primary runlevel
* adding a single secondary runlevel
* removing a single secondary runlevel
However, for the sake of completeness (since init switches
between two arbitrary runlevel masks), and because doing
the above simplification does not really remove that much code,
it is left as is, accepting back the same format as used
for telinit ? reports. */
static void setrunlevel(const char* p)
{
int next = nextlevel;
int mask = 0;
char op = *(p++);
if(op >= '0' && op <= '9')
next = (next & SUBMASK) | (1 << (op - '0'));
/* should have checked for +/- here, but it's done in parsecmd
before calling setrunlevel */
if(*p == '-') {
/* "4-" or similar */
next &= ~SUBMASK;
if(*(++p))
retwarn_("no runlevels allowed past trailing -");
} else for(; *p; p++) {
/* "4abc", or "+abc", or "-abc" */
if(*p >= 'a' && *p <= 'f')
mask |= (1 << (*p - 'a' + 0xa));
else
retwarn_("bad runlevel %c", *p);
}
switch(op) {
default: if(mask) next &= ~SUBMASK;
case '+': next |= mask; break;
case '-': next &= ~mask; break;
}
nextlevel = next;
}
/* User commands like start or stop should prompt immediate action
(well, next-initpass-immediate that is), disregarding any possible
time_to_* timeouts. To achieve that, entry timestamps are reset. */
static void clearts(struct initrec* p)
{
p->lastrun = 0;
p->lastsig = 0;
}
/* What this does is a forced start of a service,
unlike enable which is more like "un-stop" and may leave
the service stopped if it is not in current runlevel.
The idea is to change runlevel mask so that the entry
would be started in current runlevel, *and* let the
service go down after a runlevel switch if it is
configured to do so. Forcing always-on state with something
like P_ENABLED may break sleep modes and expected shutdown
routine.
There is no dostop because P_MANUAL is enough to
force-stop a process regardless of its configured runlevels */
static void dostart(struct initrec* p)
{
/* C_INVERT is never used with anything that can be started */
if(p->flags & C_INVERT) return;
clearts(p);
p->rlvl |= (nextlevel & PRIMASK);
p->rlvl &= (nextlevel & SUBMASK) | PRIMASK;
p->flags &= ~(P_MANUAL | P_FAILED);
}
/* To restart a process, it's enough to kill it without touching
the flags. On the next initpass, the process will be re-spawned.
This won't work with non-respawning entries of cource, but it does
not make much sense to restart those anyway. */
static void dorestart(struct initrec* p)
{
clearts(p);
stop(p);
}
/* Enabling/disabling p may prompt some action on the next initpass,
either spawn() or stop(), and we'd like to have that done immediately
regardless of the timestamps. The action itself will happen more
or less naturally once shouldberunning() flips over P_MANUAL. */
static void dodisable(struct initrec* p, int v)
{
clearts(p);
p->flags &= ~P_FAILED;
if(v)
p->flags |= P_MANUAL;
else
p->flags &= ~P_MANUAL;
}
/* When signalling HUP, we only want to target the immediate init child.
With SIGSTOP/SIGCONT however, targeting the whole group makes more sense
(but may break programs that use SIGSTOP internally, hm)
The effects of SIGSTOP/SIGCONT are tracked in waitpids(). Init is not
the only entity that can sent SIGSTOP, and it needs to know for sure
whether the process is stopped or not. */
static void killrec(struct initrec* p, int sig)
{
pid_t pid = p->pid;
if(pid <= 0)
retwarn_("%s is not running", p->name);
if(sig < 0) {
pid = -pid;
sig = -sig;
}
if(kill(pid, sig))
retwarn_("%s[%i]: kill failed: %e", p->name, p->pid);
}
/* Here we have a single command (cmd) sent by telinit, stored in some
buffer in readcmd(). And we've got to parse it and take action.
The actual command is always cmd[0], while cmd[1:] is the argument.
Examples:
"c" reconfigure
"9" switch to runlevel 9
"shttpd" start httpd
With exception of kill() calls, all parsecmd does is setting flags,
either per-process or global. The next initpass() is where the rest
happens. Telinit connection is closed right after parsecmd() returns;
this makes it impossible to report entry start success, but keeps
initpass async and simple.
Within parsecmd, warnfd is the open telinit connection, so we send
the text back to telinit using warn(). */
void parsecmd(char* cmd)
{
char* arg = cmd + 1;
struct initrec* p = NULL;
/* Check whether this command needs arguments */
switch(*cmd) {
/* Runlevel switching commands handle argument differently */
case '0' ... '9':
case '+':
case '-':
setrunlevel(cmd);
return;
/* Mandatory argument */
case 'r': /* restart */
case 's': case 't': /* start, stop */
case 'u': /* unstop */
case 'p': case 'w': /* pause, resume */
case 'h': case 'i': /* hup, pidof */
if(!(p = findentry(arg)))
retwarn_("can't find %s in inittab", arg);
break;
/* There are no commands with optional arguments atm */
/* There are few that take no argument at all however */
default:
if(*arg)
retwarn_("no argument allowed for %c", *cmd);
}
/* Now the command itself */
switch(*cmd) {
/* halt */
case 'H': nextlevel = 1; rbcode = RB_HALT_SYSTEM; break;
case 'P': nextlevel = 1; rbcode = RB_POWER_OFF; break;
case 'R': nextlevel = 1; rbcode = RB_AUTOBOOT; break;
/* process ops */
case 'p': killrec(p, -SIGSTOP); break;
case 'w': killrec(p, -SIGCONT); break;
case 'h': killrec(p, SIGHUP); break;
case 's': dostart(p); break;
case 'r': dorestart(p); break;
case 't': dodisable(p, 1); break;
case 'u': dodisable(p, 0); break;
/* state query */
case '?': dumpstate(); break;
case 'i': dumpidof(p); break;
/* reconfigure */
case 'c':
if(configure(STRICT))
warn("Reconfiguration failed");
else
state |= S_RECONFIG;
break;
default:
warn("unknown command %s", cmd);
}
}