1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
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 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <selinux/selinux.h>
31 #include "socket-util.h"
32 #include "selinux-util.h"
33 #include "journald-server.h"
34 #include "journald-stream.h"
35 #include "journald-syslog.h"
36 #include "journald-kmsg.h"
37 #include "journald-console.h"
39 #define STDOUT_STREAMS_MAX 4096
41 typedef enum StdoutStreamState {
42 STDOUT_STREAM_IDENTIFIER,
43 STDOUT_STREAM_UNIT_ID,
44 STDOUT_STREAM_PRIORITY,
45 STDOUT_STREAM_LEVEL_PREFIX,
46 STDOUT_STREAM_FORWARD_TO_SYSLOG,
47 STDOUT_STREAM_FORWARD_TO_KMSG,
48 STDOUT_STREAM_FORWARD_TO_CONSOLE,
54 StdoutStreamState state;
60 security_context_t security_context;
67 bool forward_to_syslog:1;
68 bool forward_to_kmsg:1;
69 bool forward_to_console:1;
71 char buffer[LINE_MAX+1];
74 sd_event_source *event_source;
76 LIST_FIELDS(StdoutStream, stdout_stream);
79 static int stdout_stream_log(StdoutStream *s, const char *p) {
80 struct iovec iovec[N_IOVEC_META_FIELDS + 5];
82 char syslog_priority[] = "PRIORITY=\0";
83 char syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(priority)];
84 _cleanup_free_ char *message = NULL, *syslog_identifier = NULL;
95 priority = s->priority;
98 syslog_parse_priority(&p, &priority, false);
100 if (s->forward_to_syslog || s->server->forward_to_syslog)
101 server_forward_syslog(s->server, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL);
103 if (s->forward_to_kmsg || s->server->forward_to_kmsg)
104 server_forward_kmsg(s->server, priority, s->identifier, p, &s->ucred);
106 if (s->forward_to_console || s->server->forward_to_console)
107 server_forward_console(s->server, priority, s->identifier, p, &s->ucred);
109 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout");
111 syslog_priority[strlen("PRIORITY=")] = '0' + LOG_PRI(priority);
112 IOVEC_SET_STRING(iovec[n++], syslog_priority);
114 if (priority & LOG_FACMASK) {
115 snprintf(syslog_facility, sizeof(syslog_facility), "SYSLOG_FACILITY=%i", LOG_FAC(priority));
116 IOVEC_SET_STRING(iovec[n++], syslog_facility);
120 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier);
121 if (syslog_identifier)
122 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
125 message = strappend("MESSAGE=", p);
127 IOVEC_SET_STRING(iovec[n++], message);
130 if (s->security_context) {
131 label = (char*) s->security_context;
132 label_len = strlen((char*) s->security_context);
136 server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, label, label_len, s->unit_id, priority, 0);
140 static int stdout_stream_line(StdoutStream *s, char *p) {
150 case STDOUT_STREAM_IDENTIFIER:
152 s->identifier = NULL;
154 s->identifier = strdup(p);
159 s->state = STDOUT_STREAM_UNIT_ID;
162 case STDOUT_STREAM_UNIT_ID:
163 if (s->ucred.uid == 0) {
167 s->unit_id = strdup(p);
173 s->state = STDOUT_STREAM_PRIORITY;
176 case STDOUT_STREAM_PRIORITY:
177 r = safe_atoi(p, &s->priority);
178 if (r < 0 || s->priority < 0 || s->priority > 999) {
179 log_warning("Failed to parse log priority line.");
183 s->state = STDOUT_STREAM_LEVEL_PREFIX;
186 case STDOUT_STREAM_LEVEL_PREFIX:
187 r = parse_boolean(p);
189 log_warning("Failed to parse level prefix line.");
193 s->level_prefix = !!r;
194 s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG;
197 case STDOUT_STREAM_FORWARD_TO_SYSLOG:
198 r = parse_boolean(p);
200 log_warning("Failed to parse forward to syslog line.");
204 s->forward_to_syslog = !!r;
205 s->state = STDOUT_STREAM_FORWARD_TO_KMSG;
208 case STDOUT_STREAM_FORWARD_TO_KMSG:
209 r = parse_boolean(p);
211 log_warning("Failed to parse copy to kmsg line.");
215 s->forward_to_kmsg = !!r;
216 s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE;
219 case STDOUT_STREAM_FORWARD_TO_CONSOLE:
220 r = parse_boolean(p);
222 log_warning("Failed to parse copy to console line.");
226 s->forward_to_console = !!r;
227 s->state = STDOUT_STREAM_RUNNING;
230 case STDOUT_STREAM_RUNNING:
231 return stdout_stream_log(s, p);
234 assert_not_reached("Unknown stream state");
237 static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
245 remaining = s->length;
250 end = memchr(p, '\n', remaining);
253 else if (remaining >= sizeof(s->buffer) - 1) {
254 end = p + sizeof(s->buffer) - 1;
261 r = stdout_stream_line(s, p);
269 if (force_flush && remaining > 0) {
271 r = stdout_stream_line(s, p);
280 memmove(s->buffer, p, remaining);
281 s->length = remaining;
287 static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
288 StdoutStream *s = userdata;
294 if ((revents|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) {
295 log_error("Got invalid event from epoll for stdout stream: %"PRIx32, revents);
300 l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length);
306 log_warning("Failed to read from stream: %m");
312 r = stdout_stream_scan(s, true);
317 r = stdout_stream_scan(s, false);
324 stdout_stream_free(s);
328 void stdout_stream_free(StdoutStream *s) {
332 assert(s->server->n_stdout_streams > 0);
333 s->server->n_stdout_streams --;
334 LIST_REMOVE(stdout_stream, s->server->stdout_streams, s);
337 if (s->event_source) {
338 sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF);
339 s->event_source = sd_event_source_unref(s->event_source);
343 close_nointr_nofail(s->fd);
346 if (s->security_context)
347 freecon(s->security_context);
355 static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revents, void *userdata) {
356 Server *s = userdata;
357 StdoutStream *stream;
363 if (revents != EPOLLIN) {
364 log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents);
368 fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
373 log_error("Failed to accept stdout connection: %m");
377 if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
378 log_warning("Too many stdout streams, refusing connection.");
379 close_nointr_nofail(fd);
383 stream = new0(StdoutStream, 1);
385 close_nointr_nofail(fd);
391 len = sizeof(stream->ucred);
392 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &stream->ucred, &len) < 0) {
393 log_error("Failed to determine peer credentials: %m");
400 if (getpeercon(fd, &stream->security_context) < 0 && errno != ENOPROTOOPT)
401 log_error("Failed to determine peer security context: %m");
405 if (shutdown(fd, SHUT_WR) < 0) {
406 log_error("Failed to shutdown writing side of socket: %m");
411 r = sd_event_add_io(s->event, fd, EPOLLIN, stdout_stream_process, stream, &stream->event_source);
413 log_error("Failed to add stream to event loop: %s", strerror(-r));
417 r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
419 log_error("Failed to adjust stdout event source priority: %s", strerror(-r));
424 LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
425 s->n_stdout_streams ++;
430 stdout_stream_free(stream);
434 int server_open_stdout_socket(Server *s) {
439 if (s->stdout_fd < 0) {
440 union sockaddr_union sa = {
441 .un.sun_family = AF_UNIX,
442 .un.sun_path = "/run/systemd/journal/stdout",
445 s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
446 if (s->stdout_fd < 0) {
447 log_error("socket() failed: %m");
451 unlink(sa.un.sun_path);
453 r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
455 log_error("bind() failed: %m");
459 chmod(sa.un.sun_path, 0666);
461 if (listen(s->stdout_fd, SOMAXCONN) < 0) {
462 log_error("listen() failed: %m");
466 fd_nonblock(s->stdout_fd, 1);
468 r = sd_event_add_io(s->event, s->stdout_fd, EPOLLIN, stdout_stream_new, s, &s->stdout_event_source);
470 log_error("Failed to add stdout server fd to event source: %s", strerror(-r));
474 r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+10);
476 log_error("Failed to adjust priority of stdout server event source: %s", strerror(-r));