]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/initctl.c
target: add implicit target/unit ordering deps only if both sides have been fully...
[thirdparty/systemd.git] / src / initctl.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
0b7964b8
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/socket.h>
23#include <sys/types.h>
24#include <assert.h>
25#include <time.h>
26#include <string.h>
27#include <stdio.h>
28#include <errno.h>
29#include <unistd.h>
30#include <sys/poll.h>
31#include <sys/epoll.h>
32#include <sys/un.h>
33#include <fcntl.h>
34#include <ctype.h>
35
36#include <dbus/dbus.h>
37
38#include "util.h"
39#include "log.h"
40#include "list.h"
41#include "initreq.h"
514f4ef5 42#include "special.h"
8bfcc8ea 43#include "sd-daemon.h"
a822056b 44#include "dbus-common.h"
0b7964b8 45
0b7964b8
LP
46#define SERVER_FD_MAX 16
47#define TIMEOUT ((int) (10*MSEC_PER_SEC))
48
49typedef struct Fifo Fifo;
50
51typedef struct Server {
52 int epoll_fd;
53
54 LIST_HEAD(Fifo, fifos);
55 unsigned n_fifos;
56
57 DBusConnection *bus;
58} Server;
59
60struct Fifo {
61 Server *server;
62
63 int fd;
64
65 struct init_request buffer;
66 size_t bytes_read;
67
68 LIST_FIELDS(Fifo, fifo);
69};
70
71static const char *translate_runlevel(int runlevel) {
6542952f
LP
72 static const struct {
73 const int runlevel;
74 const char *special;
75 } table[] = {
514f4ef5
LP
76 { '0', SPECIAL_POWEROFF_TARGET },
77 { '1', SPECIAL_RESCUE_TARGET },
78 { 's', SPECIAL_RESCUE_TARGET },
79 { 'S', SPECIAL_RESCUE_TARGET },
6542952f
LP
80 { '2', SPECIAL_RUNLEVEL2_TARGET },
81 { '3', SPECIAL_RUNLEVEL3_TARGET },
82 { '4', SPECIAL_RUNLEVEL4_TARGET },
83 { '5', SPECIAL_RUNLEVEL5_TARGET },
514f4ef5 84 { '6', SPECIAL_REBOOT_TARGET },
6542952f
LP
85 };
86
87 unsigned i;
88
89 for (i = 0; i < ELEMENTSOF(table); i++)
90 if (table[i].runlevel == runlevel)
91 return table[i].special;
92
93 return NULL;
0b7964b8
LP
94}
95
96static void change_runlevel(Server *s, int runlevel) {
97 const char *target;
98 DBusMessage *m = NULL, *reply = NULL;
99 DBusError error;
c87eba54 100 const char *replace = "replace";
0b7964b8
LP
101
102 assert(s);
103
104 dbus_error_init(&error);
105
106 if (!(target = translate_runlevel(runlevel))) {
107 log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
108 goto finish;
109 }
110
111 log_debug("Running request %s", target);
112
c87eba54 113 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) {
0b7964b8
LP
114 log_error("Could not allocate message.");
115 goto finish;
116 }
117
118 if (!dbus_message_append_args(m,
119 DBUS_TYPE_STRING, &target,
0b7964b8
LP
120 DBUS_TYPE_STRING, &replace,
121 DBUS_TYPE_INVALID)) {
5192bd19 122 log_error("Could not attach target and flag information to message.");
0b7964b8
LP
123 goto finish;
124 }
125
0b7964b8 126 if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
4cf5d675 127 log_error("Failed to start unit: %s", bus_error_message(&error));
0b7964b8
LP
128 goto finish;
129 }
130
131finish:
132 if (m)
133 dbus_message_unref(m);
134
135 if (reply)
136 dbus_message_unref(reply);
137
138 dbus_error_free(&error);
139}
140
141static void request_process(Server *s, const struct init_request *req) {
142 assert(s);
143 assert(req);
144
145 if (req->magic != INIT_MAGIC) {
146 log_error("Got initctl request with invalid magic. Ignoring.");
147 return;
148 }
149
150 switch (req->cmd) {
151
152 case INIT_CMD_RUNLVL:
153 if (!isprint(req->runlevel))
154 log_error("Got invalid runlevel. Ignoring.");
155 else
156 change_runlevel(s, req->runlevel);
157 return;
158
159 case INIT_CMD_POWERFAIL:
160 case INIT_CMD_POWERFAILNOW:
161 case INIT_CMD_POWEROK:
162 log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
163 return;
164
165 case INIT_CMD_CHANGECONS:
166 log_warning("Received console change initctl request. This is not implemented in systemd.");
167 return;
168
169 case INIT_CMD_SETENV:
170 case INIT_CMD_UNSETENV:
171 log_warning("Received environment initctl request. This is not implemented in systemd.");
172 return;
173
174 default:
175 log_warning("Received unknown initctl request. Ignoring.");
176 return;
177 }
178}
179
180static int fifo_process(Fifo *f) {
181 ssize_t l;
182
183 assert(f);
184
185 errno = EIO;
186 if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
187
188 if (errno == EAGAIN)
189 return 0;
190
191 log_warning("Failed to read from fifo: %s", strerror(errno));
192 return -1;
193 }
194
195 f->bytes_read += l;
196 assert(f->bytes_read <= sizeof(f->buffer));
197
198 if (f->bytes_read == sizeof(f->buffer)) {
199 request_process(f->server, &f->buffer);
200 f->bytes_read = 0;
201 }
202
203 return 0;
204}
205
206static void fifo_free(Fifo *f) {
207 assert(f);
208
209 if (f->server) {
210 assert(f->server->n_fifos > 0);
211 f->server->n_fifos--;
212 LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
213 }
214
215 if (f->fd >= 0) {
216 if (f->server)
217 epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
218
a16e1123 219 close_nointr_nofail(f->fd);
0b7964b8
LP
220 }
221
222 free(f);
223}
224
0b7964b8
LP
225static void server_done(Server *s) {
226 assert(s);
227
228 while (s->fifos)
229 fifo_free(s->fifos);
230
231 if (s->epoll_fd >= 0)
a16e1123 232 close_nointr_nofail(s->epoll_fd);
0b7964b8 233
fb1af5b0 234 if (s->bus) {
5d452f9c
LP
235 dbus_connection_flush(s->bus);
236 dbus_connection_close(s->bus);
237 dbus_connection_unref(s->bus);
fb1af5b0 238 }
0b7964b8
LP
239}
240
241static int server_init(Server *s, unsigned n_sockets) {
242 int r;
243 unsigned i;
244 DBusError error;
245
246 assert(s);
247 assert(n_sockets > 0);
248
249 dbus_error_init(&error);
250
251 zero(*s);
252
253 if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
254 r = -errno;
255 log_error("Failed to create epoll object: %s", strerror(errno));
256 goto fail;
257 }
258
259 for (i = 0; i < n_sockets; i++) {
260 struct epoll_event ev;
261 Fifo *f;
7c394faa
LP
262 int fd;
263
264 fd = SD_LISTEN_FDS_START+i;
265
266 if ((r = sd_is_fifo(fd, NULL)) < 0) {
267 log_error("Failed to determine file descriptor type: %s", strerror(-r));
268 goto fail;
269 }
270
271 if (!r) {
272 log_error("Wrong file descriptor type.");
273 r = -EINVAL;
274 goto fail;
275 }
0b7964b8
LP
276
277 if (!(f = new0(Fifo, 1))) {
278 r = -ENOMEM;
279 log_error("Failed to create fifo object: %s", strerror(errno));
280 goto fail;
281 }
282
283 f->fd = -1;
284
285 zero(ev);
286 ev.events = EPOLLIN;
287 ev.data.ptr = f;
7c394faa 288 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
0b7964b8
LP
289 r = -errno;
290 fifo_free(f);
291 log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
292 goto fail;
293 }
294
63983207 295 f->fd = fd;
0b7964b8
LP
296 LIST_PREPEND(Fifo, fifo, s->fifos, f);
297 f->server = s;
298 s->n_fifos ++;
299 }
300
b574246b 301 if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
4cf5d675 302 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
0b7964b8
LP
303 goto fail;
304 }
305
306 return 0;
307
308fail:
309 server_done(s);
310
311 dbus_error_free(&error);
312 return r;
313}
314
315static int process_event(Server *s, struct epoll_event *ev) {
316 int r;
317 Fifo *f;
318
319 assert(s);
320
321 if (!(ev->events & EPOLLIN)) {
322 log_info("Got invalid event from epoll. (3)");
323 return -EIO;
324 }
325
326 f = (Fifo*) ev->data.ptr;
327
328 if ((r = fifo_process(f)) < 0) {
329 log_info("Got error on fifo: %s", strerror(-r));
330 fifo_free(f);
331 return r;
332 }
333
334 return 0;
335}
336
337int main(int argc, char *argv[]) {
338 Server server;
22f4096c 339 int r = EXIT_FAILURE, n;
8bfcc8ea 340
0ca3f374
LP
341 if (getppid() != 1) {
342 log_error("This program should be invoked by init only.");
22f4096c 343 return EXIT_FAILURE;
0ca3f374
LP
344 }
345
346 if (argc > 1) {
347 log_error("This program does not take arguments.");
22f4096c 348 return EXIT_FAILURE;
0ca3f374
LP
349 }
350
8bfcc8ea
LP
351 log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
352 log_parse_environment();
2396fb04 353 log_open();
0b7964b8 354
8bfcc8ea
LP
355 if ((n = sd_listen_fds(true)) < 0) {
356 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
22f4096c 357 return EXIT_FAILURE;
8bfcc8ea
LP
358 }
359
360 if (n <= 0 || n > SERVER_FD_MAX) {
361 log_error("No or too many file descriptors passed.");
22f4096c 362 return EXIT_FAILURE;
8bfcc8ea 363 }
0b7964b8 364
8bfcc8ea 365 if (server_init(&server, (unsigned) n) < 0)
22f4096c 366 return EXIT_FAILURE;
0b7964b8 367
cd6d0a45
LP
368 log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
369
8c47c732
LP
370 sd_notify(false,
371 "READY=1\n"
372 "STATUS=Processing requests...");
373
0b7964b8
LP
374 for (;;) {
375 struct epoll_event event;
376 int k;
377
378 if ((k = epoll_wait(server.epoll_fd,
379 &event, 1,
380 TIMEOUT)) < 0) {
381
382 if (errno == EINTR)
383 continue;
384
385 log_error("epoll_wait() failed: %s", strerror(errno));
386 goto fail;
387 }
388
389 if (k <= 0)
390 break;
391
e364ad06 392 if (process_event(&server, &event) < 0)
0b7964b8
LP
393 goto fail;
394 }
cd6d0a45 395
22f4096c 396 r = EXIT_SUCCESS;
0b7964b8 397
cd6d0a45
LP
398 log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
399
0b7964b8 400fail:
8c47c732
LP
401 sd_notify(false,
402 "STATUS=Shutting down...");
403
0b7964b8
LP
404 server_done(&server);
405
0b7964b8
LP
406 dbus_shutdown();
407
408 return r;
409}