]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/kmsg-syslogd.c
service: rework killing logic so that we always kill the main process, even if it...
[thirdparty/systemd.git] / src / kmsg-syslogd.c
CommitLineData
addab137
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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 <sys/signalfd.h>
35
36#include "util.h"
37#include "log.h"
38#include "sd-daemon.h"
39#include "fdset.h"
40
41#define SERVER_FD_MAX 16
42#define TIMEOUT ((int) (10*MSEC_PER_SEC))
43
44typedef struct Stream Stream;
45
46typedef struct Server {
47 FDSet *syslog_fds;
48 int kmsg_fd;
49 int epoll_fd;
50 int signal_fd;
51} Server;
52
53static void server_done(Server *s) {
54 assert(s);
55
56 if (s->epoll_fd >= 0)
57 close_nointr_nofail(s->epoll_fd);
58
59 if (s->kmsg_fd >= 0)
60 close_nointr_nofail(s->kmsg_fd);
61
62 if (s->signal_fd >= 0)
63 close_nointr_nofail(s->signal_fd);
64
65 if (s->syslog_fds)
66 fdset_free(s->syslog_fds);
67}
68
69static int server_init(Server *s, unsigned n_sockets) {
70 int r;
71 unsigned i;
72 struct epoll_event ev;
73 sigset_t mask;
74
75 assert(s);
76 assert(n_sockets > 0);
77
78 zero(*s);
79
80 s->kmsg_fd = s->signal_fd = -1;
81
82 if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
83 r = -errno;
84 log_error("Failed to create epoll object: %s", strerror(errno));
85 goto fail;
86 }
87
88 if (!(s->syslog_fds = fdset_new())) {
89 r = -ENOMEM;
90 log_error("Failed to allocate file descriptor set: %s", strerror(errno));
91 goto fail;
92 }
93
94 for (i = 0; i < n_sockets; i++) {
95 int fd, one = 1;
96
97 fd = SD_LISTEN_FDS_START+i;
98
99 if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_DGRAM, -1)) < 0) {
100 log_error("Failed to determine file descriptor type: %s", strerror(-r));
101 goto fail;
102 }
103
104 if (!r) {
105 log_error("Wrong file descriptor type.");
106 r = -EINVAL;
107 goto fail;
108 }
109
110 if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
111 log_error("SO_PASSCRED failed: %m");
112
113 zero(ev);
114 ev.events = EPOLLIN;
115 ev.data.fd = fd;
116 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
117 r = -errno;
118 log_error("Failed to add server fd to epoll object: %s", strerror(errno));
119 goto fail;
120 }
121
122 if ((r = fdset_put(s->syslog_fds, fd)) < 0) {
123 log_error("Failed to store file descriptor in set: %s", strerror(-r));
124 goto fail;
125 }
126 }
127
128 if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
129 log_error("Failed to open /dev/kmsg for logging: %m");
130 return -errno;
131 }
132
133 assert_se(sigemptyset(&mask) == 0);
134 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
135 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
136
137 if ((s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
138 log_error("signalfd(): %m");
139 return -errno;
140 }
141
142 zero(ev);
143 ev.events = EPOLLIN;
144 ev.data.fd = s->signal_fd;
145
146 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->signal_fd, &ev) < 0) {
147 log_error("epoll_ctl(): %m");
148 return -errno;
149 }
150
151 return 0;
152
153fail:
154 server_done(s);
155 return r;
156}
157
158static int read_priority(const char **buf) {
159 int priority;
160 size_t n;
161 const char *p;
162 int a, b, c;
163
164 assert(buf);
165 assert(*buf);
166
167 p = *buf;
168 n = strlen(p);
169
170 if (n < 3 || p[0] != '<')
171 goto fail;
172
173 if (p[2] == '>') {
174 a = b = 0;
175 c = undecchar(p[1]);
176 p += 3;
177 } else if (n >= 4 && p[3] == '>') {
178 a = 0;
179 b = undecchar(p[1]);
180 c = undecchar(p[2]);
181 p += 4;
182 } else if (n >= 5 && p[4] == '>') {
183 a = undecchar(p[1]);
184 b = undecchar(p[2]);
185 c = undecchar(p[3]);
186 p += 5;
187 } else
188 goto fail;
189
190 if (a < 0 || b < 0 || c < 0)
191 goto fail;
192
193 *buf = p;
194
195 priority = 100*a + 10*b + c;
196 return LOG_PRI(priority);
197
198fail:
199 return LOG_INFO;
200}
201
202static void skip_date(const char **buf) {
203 enum {
204 LETTER,
205 SPACE,
206 NUMBER,
207 SPACE_OR_NUMBER,
208 COLON
209 } sequence[] = {
210 LETTER, LETTER, LETTER,
211 SPACE,
212 SPACE_OR_NUMBER, NUMBER,
213 SPACE,
214 SPACE_OR_NUMBER, NUMBER,
215 COLON,
216 SPACE_OR_NUMBER, NUMBER,
217 COLON,
218 SPACE_OR_NUMBER, NUMBER,
219 SPACE
220 };
221
222 const char *p;
223 unsigned i;
224
225 assert(buf);
226 assert(*buf);
227
228 p = *buf;
229
230 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
231
232 if (!*p)
233 return;
234
235 switch (sequence[i]) {
236
237 case SPACE:
238 if (*p != ' ')
239 return;
240 break;
241
242 case SPACE_OR_NUMBER:
243 if (*p == ' ')
244 break;
245
246 /* fall through */
247
248 case NUMBER:
249 if (*p < '0' || *p > '9')
250 return;
251
252 break;
253
254 case LETTER:
255 if (!(*p >= 'A' && *p <= 'Z') &&
256 !(*p >= 'a' && *p <= 'z'))
257 return;
258
259 break;
260
261 case COLON:
262 if (*p != ':')
263 return;
264 break;
265
266 }
267 }
268
269 *buf = p;
270}
271
272static int read_process(const char **buf, struct iovec *iovec) {
273 const char *p;
274 size_t l;
275
276 assert(buf);
277 assert(*buf);
278 assert(iovec);
279
280 p = *buf;
281
282 p += strspn(p, WHITESPACE);
283 l = strcspn(p, WHITESPACE);
284
285 if (l <= 0 ||
286 p[l-1] != ':')
287 return 0;
288
289 l--;
290
291 if (p[l-1] == ']') {
292 size_t k = l-1;
293
294 for (;;) {
295
296 if (p[k] == '[') {
297 l = k;
298 break;
299 }
300
301 if (k == 0)
302 break;
303
304 k--;
305 }
306 }
307
308 iovec->iov_base = (char*) p;
309 iovec->iov_len = l;
310 *buf = p + l;
311 return 1;
312}
313
314static void skip_pid(const char **buf) {
315 const char *p;
316
317 assert(buf);
318 assert(*buf);
319
320 p = *buf;
321
322 if (*p != '[')
323 return;
324
325 p++;
326 p += strspn(p, "0123456789");
327
328 if (*p != ']')
329 return;
330
331 p++;
332
333 *buf = p;
334}
335
336static int write_message(Server *s, const char *buf, struct ucred *ucred) {
337 ssize_t k;
338 char priority[4], pid[16];
339 struct iovec iovec[5];
340 unsigned i = 0;
341 char *process = NULL;
342 int r = 0;
343
344 assert(s);
345 assert(buf);
346
347 /* First, set priority field */
348 snprintf(priority, sizeof(priority), "<%i>", read_priority(&buf));
349 char_array_0(priority);
350 IOVEC_SET_STRING(iovec[i++], priority);
351
352 /* Second, skip date */
353 skip_date(&buf);
354
355 /* Then, add process if set */
356 if (read_process(&buf, &iovec[i]) > 0)
357 i++;
358 else if (ucred && get_process_name(ucred->pid, &process) >= 0)
359 IOVEC_SET_STRING(iovec[i++], process);
360
361 /* Skip the stored PID if we have a better one */
362 if (ucred) {
363 snprintf(pid, sizeof(pid), "[%lu]: ", (unsigned long) ucred->pid);
364 char_array_0(pid);
365 IOVEC_SET_STRING(iovec[i++], pid);
366
367 skip_pid(&buf);
368
369 if (*buf == ':')
370 buf++;
371
372 buf += strspn(buf, WHITESPACE);
373 }
374
375 /* Is the remaining message empty? */
376 if (*buf) {
377
378 /* And the rest is the message */
379 IOVEC_SET_STRING(iovec[i++], buf);
380 IOVEC_SET_STRING(iovec[i++], "\n");
381
382 if ((k = writev(s->kmsg_fd, iovec, i)) <= 0) {
383 log_error("Failed to write log message to kmsg: %s", k < 0 ? strerror(errno) : "short write");
384 r = k < 0 ? -errno : -EIO;
385 }
386 }
387
388 free(process);
389
390 return r;
391}
392
393static int process_event(Server *s, struct epoll_event *ev) {
394 assert(s);
395
396 if (ev->events != EPOLLIN) {
397 log_info("Got invalid event from epoll.");
398 return -EIO;
399 }
400
401 if (ev->data.fd == s->signal_fd) {
402 struct signalfd_siginfo sfsi;
403 ssize_t n;
404
405 if ((n = read(s->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
406
407 if (n >= 0)
408 return -EIO;
409
410 if (errno == EINTR || errno == EAGAIN)
411 return 0;
412
413 return -errno;
414 }
415
416 log_debug("Received SIG%s", strna(signal_to_string(sfsi.ssi_signo)));
417 return 0;
418
419 } else {
420 for (;;) {
421 char buf[LINE_MAX+1];
422 struct msghdr msghdr;
423 struct iovec iovec;
424 struct ucred *ucred;
425 union {
426 struct cmsghdr cmsghdr;
427 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
428 } control;
429 ssize_t n;
430 int k;
431 char *e;
432
433 zero(iovec);
434 iovec.iov_base = buf;
435 iovec.iov_len = sizeof(buf)-1;
436
437 zero(control);
438 zero(msghdr);
439 msghdr.msg_iov = &iovec;
440 msghdr.msg_iovlen = 1;
441 msghdr.msg_control = &control;
442 msghdr.msg_controllen = sizeof(control);
443
444 if ((n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT)) < 0) {
445
446 if (errno == EINTR || errno == EAGAIN)
447 return 1;
448
449 log_error("recvmsg() failed: %m");
450 return -errno;
451 }
452
453 if (msghdr.msg_controllen >= CMSG_LEN(sizeof(struct ucred)) &&
454 control.cmsghdr.cmsg_level == SOL_SOCKET &&
455 control.cmsghdr.cmsg_type == SCM_CREDENTIALS &&
456 control.cmsghdr.cmsg_len == CMSG_LEN(sizeof(struct ucred)))
457 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
458 else
459 ucred = NULL;
460
461 if ((e = memchr(buf, '\n', n)))
462 *e = 0;
463 else
464 buf[n] = 0;
465
466 if ((k = write_message(s, strstrip(buf), ucred)) < 0)
467 return k;
468 }
469 }
470
471 return 1;
472}
473
474int main(int argc, char *argv[]) {
475 Server server;
22f4096c 476 int r = EXIT_FAILURE, n;
addab137
LP
477
478 if (getppid() != 1) {
479 log_error("This program should be invoked by init only.");
22f4096c 480 return EXIT_FAILURE;
addab137
LP
481 }
482
483 if (argc > 1) {
484 log_error("This program does not take arguments.");
22f4096c 485 return EXIT_FAILURE;
addab137
LP
486 }
487
488 log_set_target(LOG_TARGET_KMSG);
489 log_parse_environment();
490 log_open();
491
492 if ((n = sd_listen_fds(true)) < 0) {
493 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
22f4096c 494 return EXIT_FAILURE;
addab137
LP
495 }
496
497 if (n <= 0 || n > SERVER_FD_MAX) {
498 log_error("No or too many file descriptors passed.");
22f4096c 499 return EXIT_FAILURE;
addab137
LP
500 }
501
502 if (server_init(&server, (unsigned) n) < 0)
22f4096c 503 return EXIT_FAILURE;
addab137
LP
504
505 log_debug("systemd-kmsg-syslogd running as pid %lu", (unsigned long) getpid());
506
507 sd_notify(false,
508 "READY=1\n"
509 "STATUS=Processing messages...");
510
511 for (;;) {
512 struct epoll_event event;
513 int k;
514
515 if ((k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT)) < 0) {
516
517 if (errno == EINTR)
518 continue;
519
520 log_error("epoll_wait() failed: %m");
521 goto fail;
522 }
523
524 if (k <= 0)
525 break;
526
527 if ((k = process_event(&server, &event)) < 0)
528 goto fail;
529
530 if (k == 0)
531 break;
532 }
533
22f4096c 534 r = EXIT_SUCCESS;
addab137
LP
535
536 log_debug("systemd-kmsg-syslogd stopped as pid %lu", (unsigned long) getpid());
537
538fail:
539 sd_notify(false,
540 "STATUS=Shutting down...");
541
542 server_done(&server);
543
544 return r;
545}