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