]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/initctl/initctl.c
tree-wide: remove Lennart's copyright lines
[thirdparty/systemd.git] / src / initctl / initctl.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <stdio.h>
6 #include <sys/epoll.h>
7 #include <unistd.h>
8
9 #include "sd-bus.h"
10 #include "sd-daemon.h"
11
12 #include "alloc-util.h"
13 #include "bus-error.h"
14 #include "bus-util.h"
15 #include "def.h"
16 #include "fd-util.h"
17 #include "format-util.h"
18 #include "initreq.h"
19 #include "list.h"
20 #include "log.h"
21 #include "special.h"
22 #include "util.h"
23 #include "process-util.h"
24
25 #define SERVER_FD_MAX 16
26 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
27
28 typedef struct Fifo Fifo;
29
30 typedef struct Server {
31 int epoll_fd;
32
33 LIST_HEAD(Fifo, fifos);
34 unsigned n_fifos;
35
36 sd_bus *bus;
37
38 bool quit;
39 } Server;
40
41 struct Fifo {
42 Server *server;
43
44 int fd;
45
46 struct init_request buffer;
47 size_t bytes_read;
48
49 LIST_FIELDS(Fifo, fifo);
50 };
51
52 static const char *translate_runlevel(int runlevel, bool *isolate) {
53 static const struct {
54 const int runlevel;
55 const char *special;
56 bool isolate;
57 } table[] = {
58 { '0', SPECIAL_POWEROFF_TARGET, false },
59 { '1', SPECIAL_RESCUE_TARGET, true },
60 { 's', SPECIAL_RESCUE_TARGET, true },
61 { 'S', SPECIAL_RESCUE_TARGET, true },
62 { '2', SPECIAL_MULTI_USER_TARGET, true },
63 { '3', SPECIAL_MULTI_USER_TARGET, true },
64 { '4', SPECIAL_MULTI_USER_TARGET, true },
65 { '5', SPECIAL_GRAPHICAL_TARGET, true },
66 { '6', SPECIAL_REBOOT_TARGET, false },
67 };
68
69 unsigned i;
70
71 assert(isolate);
72
73 for (i = 0; i < ELEMENTSOF(table); i++)
74 if (table[i].runlevel == runlevel) {
75 *isolate = table[i].isolate;
76 if (runlevel == '6' && kexec_loaded())
77 return SPECIAL_KEXEC_TARGET;
78 return table[i].special;
79 }
80
81 return NULL;
82 }
83
84 static void change_runlevel(Server *s, int runlevel) {
85 const char *target;
86 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
87 const char *mode;
88 bool isolate = false;
89 int r;
90
91 assert(s);
92
93 target = translate_runlevel(runlevel, &isolate);
94 if (!target) {
95 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
96 return;
97 }
98
99 if (isolate)
100 mode = "isolate";
101 else
102 mode = "replace-irreversibly";
103
104 log_debug("Running request %s/start/%s", target, mode);
105
106 r = sd_bus_call_method(
107 s->bus,
108 "org.freedesktop.systemd1",
109 "/org/freedesktop/systemd1",
110 "org.freedesktop.systemd1.Manager",
111 "StartUnit",
112 &error,
113 NULL,
114 "ss", target, mode);
115 if (r < 0) {
116 log_error("Failed to change runlevel: %s", bus_error_message(&error, -r));
117 return;
118 }
119 }
120
121 static void request_process(Server *s, const struct init_request *req) {
122 assert(s);
123 assert(req);
124
125 if (req->magic != INIT_MAGIC) {
126 log_error("Got initctl request with invalid magic. Ignoring.");
127 return;
128 }
129
130 switch (req->cmd) {
131
132 case INIT_CMD_RUNLVL:
133 if (!isprint(req->runlevel))
134 log_error("Got invalid runlevel. Ignoring.");
135 else
136 switch (req->runlevel) {
137
138 /* we are async anyway, so just use kill for reexec/reload */
139 case 'u':
140 case 'U':
141 if (kill(1, SIGTERM) < 0)
142 log_error_errno(errno, "kill() failed: %m");
143
144 /* The bus connection will be
145 * terminated if PID 1 is reexecuted,
146 * hence let's just exit here, and
147 * rely on that we'll be restarted on
148 * the next request */
149 s->quit = true;
150 break;
151
152 case 'q':
153 case 'Q':
154 if (kill(1, SIGHUP) < 0)
155 log_error_errno(errno, "kill() failed: %m");
156 break;
157
158 default:
159 change_runlevel(s, req->runlevel);
160 }
161 return;
162
163 case INIT_CMD_POWERFAIL:
164 case INIT_CMD_POWERFAILNOW:
165 case INIT_CMD_POWEROK:
166 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
167 return;
168
169 case INIT_CMD_CHANGECONS:
170 log_warning("Received console change initctl request. This is not implemented in systemd.");
171 return;
172
173 case INIT_CMD_SETENV:
174 case INIT_CMD_UNSETENV:
175 log_warning("Received environment initctl request. This is not implemented in systemd.");
176 return;
177
178 default:
179 log_warning("Received unknown initctl request. Ignoring.");
180 return;
181 }
182 }
183
184 static int fifo_process(Fifo *f) {
185 ssize_t l;
186
187 assert(f);
188
189 errno = EIO;
190 l = read(f->fd,
191 ((uint8_t*) &f->buffer) + f->bytes_read,
192 sizeof(f->buffer) - f->bytes_read);
193 if (l <= 0) {
194 if (errno == EAGAIN)
195 return 0;
196
197 return log_warning_errno(errno, "Failed to read from fifo: %m");
198 }
199
200 f->bytes_read += l;
201 assert(f->bytes_read <= sizeof(f->buffer));
202
203 if (f->bytes_read == sizeof(f->buffer)) {
204 request_process(f->server, &f->buffer);
205 f->bytes_read = 0;
206 }
207
208 return 0;
209 }
210
211 static void fifo_free(Fifo *f) {
212 assert(f);
213
214 if (f->server) {
215 assert(f->server->n_fifos > 0);
216 f->server->n_fifos--;
217 LIST_REMOVE(fifo, f->server->fifos, f);
218 }
219
220 if (f->fd >= 0) {
221 if (f->server)
222 epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
223
224 safe_close(f->fd);
225 }
226
227 free(f);
228 }
229
230 static void server_done(Server *s) {
231 assert(s);
232
233 while (s->fifos)
234 fifo_free(s->fifos);
235
236 safe_close(s->epoll_fd);
237
238 if (s->bus) {
239 sd_bus_flush(s->bus);
240 sd_bus_unref(s->bus);
241 }
242 }
243
244 static int server_init(Server *s, unsigned n_sockets) {
245 int r;
246 unsigned i;
247
248 assert(s);
249 assert(n_sockets > 0);
250
251 zero(*s);
252
253 s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
254 if (s->epoll_fd < 0) {
255 r = log_error_errno(errno,
256 "Failed to create epoll object: %m");
257 goto fail;
258 }
259
260 for (i = 0; i < n_sockets; i++) {
261 struct epoll_event ev;
262 Fifo *f;
263 int fd;
264
265 fd = SD_LISTEN_FDS_START+i;
266
267 r = sd_is_fifo(fd, NULL);
268 if (r < 0) {
269 log_error_errno(r, "Failed to determine file descriptor type: %m");
270 goto fail;
271 }
272
273 if (!r) {
274 log_error("Wrong file descriptor type.");
275 r = -EINVAL;
276 goto fail;
277 }
278
279 f = new0(Fifo, 1);
280 if (!f) {
281 r = -ENOMEM;
282 log_error_errno(errno, "Failed to create fifo object: %m");
283 goto fail;
284 }
285
286 f->fd = -1;
287
288 zero(ev);
289 ev.events = EPOLLIN;
290 ev.data.ptr = f;
291 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
292 r = -errno;
293 fifo_free(f);
294 log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
295 goto fail;
296 }
297
298 f->fd = fd;
299 LIST_PREPEND(fifo, s->fifos, f);
300 f->server = s;
301 s->n_fifos++;
302 }
303
304 r = bus_connect_system_systemd(&s->bus);
305 if (r < 0) {
306 log_error_errno(r, "Failed to get D-Bus connection: %m");
307 r = -EIO;
308 goto fail;
309 }
310
311 return 0;
312
313 fail:
314 server_done(s);
315
316 return r;
317 }
318
319 static int process_event(Server *s, struct epoll_event *ev) {
320 int r;
321 Fifo *f;
322
323 assert(s);
324
325 if (!(ev->events & EPOLLIN)) {
326 log_info("Got invalid event from epoll. (3)");
327 return -EIO;
328 }
329
330 f = (Fifo*) ev->data.ptr;
331 r = fifo_process(f);
332 if (r < 0) {
333 log_info_errno(r, "Got error on fifo: %m");
334 fifo_free(f);
335 return r;
336 }
337
338 return 0;
339 }
340
341 int main(int argc, char *argv[]) {
342 Server server;
343 int r = EXIT_FAILURE, n;
344
345 if (getppid() != 1) {
346 log_error("This program should be invoked by init only.");
347 return EXIT_FAILURE;
348 }
349
350 if (argc > 1) {
351 log_error("This program does not take arguments.");
352 return EXIT_FAILURE;
353 }
354
355 log_set_target(LOG_TARGET_AUTO);
356 log_parse_environment();
357 log_open();
358
359 umask(0022);
360
361 n = sd_listen_fds(true);
362 if (n < 0) {
363 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
364 return EXIT_FAILURE;
365 }
366
367 if (n <= 0 || n > SERVER_FD_MAX) {
368 log_error("No or too many file descriptors passed.");
369 return EXIT_FAILURE;
370 }
371
372 if (server_init(&server, (unsigned) n) < 0)
373 return EXIT_FAILURE;
374
375 log_debug("systemd-initctl running as pid "PID_FMT, getpid_cached());
376
377 sd_notify(false,
378 "READY=1\n"
379 "STATUS=Processing requests...");
380
381 while (!server.quit) {
382 struct epoll_event event;
383 int k;
384
385 k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC);
386 if (k < 0) {
387 if (errno == EINTR)
388 continue;
389 log_error_errno(errno, "epoll_wait() failed: %m");
390 goto fail;
391 }
392
393 if (k <= 0)
394 break;
395
396 if (process_event(&server, &event) < 0)
397 goto fail;
398 }
399
400 r = EXIT_SUCCESS;
401
402 log_debug("systemd-initctl stopped as pid "PID_FMT, getpid_cached());
403
404 fail:
405 sd_notify(false,
406 "STOPPING=1\n"
407 "STATUS=Shutting down...");
408
409 server_done(&server);
410
411 return r;
412 }