]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/initctl.c
clang: fix numerous little issues found with clang-analyzer
[thirdparty/systemd.git] / src / initctl.c
CommitLineData
0b7964b8
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
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)) {
c87eba54 122 log_error("Could not attach target and flag information to signal message.");
0b7964b8
LP
123 goto finish;
124 }
125
0b7964b8
LP
126 if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
127 log_error("Failed to start unit: %s", error.message);
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) {
b574246b 235 dbus_connection_close(s->bus);
fb1af5b0
LP
236 dbus_connection_unref(s->bus);
237 }
0b7964b8
LP
238}
239
240static int server_init(Server *s, unsigned n_sockets) {
241 int r;
242 unsigned i;
243 DBusError error;
244
245 assert(s);
246 assert(n_sockets > 0);
247
248 dbus_error_init(&error);
249
250 zero(*s);
251
252 if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
253 r = -errno;
254 log_error("Failed to create epoll object: %s", strerror(errno));
255 goto fail;
256 }
257
258 for (i = 0; i < n_sockets; i++) {
259 struct epoll_event ev;
260 Fifo *f;
7c394faa
LP
261 int fd;
262
263 fd = SD_LISTEN_FDS_START+i;
264
265 if ((r = sd_is_fifo(fd, NULL)) < 0) {
266 log_error("Failed to determine file descriptor type: %s", strerror(-r));
267 goto fail;
268 }
269
270 if (!r) {
271 log_error("Wrong file descriptor type.");
272 r = -EINVAL;
273 goto fail;
274 }
0b7964b8
LP
275
276 if (!(f = new0(Fifo, 1))) {
277 r = -ENOMEM;
278 log_error("Failed to create fifo object: %s", strerror(errno));
279 goto fail;
280 }
281
282 f->fd = -1;
283
284 zero(ev);
285 ev.events = EPOLLIN;
286 ev.data.ptr = f;
7c394faa 287 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
0b7964b8
LP
288 r = -errno;
289 fifo_free(f);
290 log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
291 goto fail;
292 }
293
63983207 294 f->fd = fd;
0b7964b8
LP
295 LIST_PREPEND(Fifo, fifo, s->fifos, f);
296 f->server = s;
297 s->n_fifos ++;
298 }
299
b574246b 300 if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
0b7964b8
LP
301 log_error("Failed to get D-Bus connection: %s", error.message);
302 goto fail;
303 }
304
305 return 0;
306
307fail:
308 server_done(s);
309
310 dbus_error_free(&error);
311 return r;
312}
313
314static int process_event(Server *s, struct epoll_event *ev) {
315 int r;
316 Fifo *f;
317
318 assert(s);
319
320 if (!(ev->events & EPOLLIN)) {
321 log_info("Got invalid event from epoll. (3)");
322 return -EIO;
323 }
324
325 f = (Fifo*) ev->data.ptr;
326
327 if ((r = fifo_process(f)) < 0) {
328 log_info("Got error on fifo: %s", strerror(-r));
329 fifo_free(f);
330 return r;
331 }
332
333 return 0;
334}
335
336int main(int argc, char *argv[]) {
337 Server server;
8bfcc8ea
LP
338 int r = 3, n;
339
0ca3f374
LP
340 if (getppid() != 1) {
341 log_error("This program should be invoked by init only.");
342 return 1;
343 }
344
345 if (argc > 1) {
346 log_error("This program does not take arguments.");
347 return 1;
348 }
349
8bfcc8ea
LP
350 log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
351 log_parse_environment();
0b7964b8 352
8bfcc8ea
LP
353 if ((n = sd_listen_fds(true)) < 0) {
354 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
0b7964b8 355 return 1;
8bfcc8ea
LP
356 }
357
358 if (n <= 0 || n > SERVER_FD_MAX) {
359 log_error("No or too many file descriptors passed.");
360 return 2;
361 }
0b7964b8 362
8bfcc8ea 363 if (server_init(&server, (unsigned) n) < 0)
0b7964b8
LP
364 return 2;
365
cd6d0a45
LP
366 log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
367
8c47c732
LP
368 sd_notify(false,
369 "READY=1\n"
370 "STATUS=Processing requests...");
371
0b7964b8
LP
372 for (;;) {
373 struct epoll_event event;
374 int k;
375
376 if ((k = epoll_wait(server.epoll_fd,
377 &event, 1,
378 TIMEOUT)) < 0) {
379
380 if (errno == EINTR)
381 continue;
382
383 log_error("epoll_wait() failed: %s", strerror(errno));
384 goto fail;
385 }
386
387 if (k <= 0)
388 break;
389
e364ad06 390 if (process_event(&server, &event) < 0)
0b7964b8
LP
391 goto fail;
392 }
cd6d0a45 393
0b7964b8
LP
394 r = 0;
395
cd6d0a45
LP
396 log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
397
0b7964b8 398fail:
8c47c732
LP
399 sd_notify(false,
400 "STATUS=Shutting down...");
401
0b7964b8
LP
402 server_done(&server);
403
0b7964b8
LP
404 dbus_shutdown();
405
406 return r;
407}