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