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