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/>.
24 #include <sys/epoll.h>
27 #include <selinux/selinux.h>
30 #include "socket-util.h"
32 #include "journald-stream.h"
33 #include "journald-syslog.h"
34 #include "journald-kmsg.h"
35 #include "journald-console.h"
37 #define STDOUT_STREAMS_MAX 4096
39 typedef enum StdoutStreamState {
40 STDOUT_STREAM_IDENTIFIER,
41 STDOUT_STREAM_UNIT_ID,
42 STDOUT_STREAM_PRIORITY,
43 STDOUT_STREAM_LEVEL_PREFIX,
44 STDOUT_STREAM_FORWARD_TO_SYSLOG,
45 STDOUT_STREAM_FORWARD_TO_KMSG,
46 STDOUT_STREAM_FORWARD_TO_CONSOLE,
52 StdoutStreamState state;
58 security_context_t security_context;
65 bool forward_to_syslog:1;
66 bool forward_to_kmsg:1;
67 bool forward_to_console:1;
69 char buffer[LINE_MAX+1];
72 LIST_FIELDS(StdoutStream, stdout_stream);
75 static int stdout_stream_log(StdoutStream *s, const char *p) {
76 struct iovec iovec[N_IOVEC_META_FIELDS + 5];
77 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL;
89 priority = s->priority;
92 syslog_parse_priority((char**) &p, &priority);
94 if (s->forward_to_syslog || s->server->forward_to_syslog)
95 server_forward_syslog(s->server, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL);
97 if (s->forward_to_kmsg || s->server->forward_to_kmsg)
98 server_forward_kmsg(s->server, priority, s->identifier, p, &s->ucred);
100 if (s->forward_to_console || s->server->forward_to_console)
101 server_forward_console(s->server, priority, s->identifier, p, &s->ucred);
103 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout");
105 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
106 IOVEC_SET_STRING(iovec[n++], syslog_priority);
108 if (priority & LOG_FACMASK)
109 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
110 IOVEC_SET_STRING(iovec[n++], syslog_facility);
113 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier);
114 if (syslog_identifier)
115 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
118 message = strappend("MESSAGE=", p);
120 IOVEC_SET_STRING(iovec[n++], message);
123 if (s->security_context) {
124 label = (char*) s->security_context;
125 label_len = strlen((char*) s->security_context);
129 server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, label, label_len, s->unit_id, priority);
132 free(syslog_priority);
133 free(syslog_facility);
134 free(syslog_identifier);
139 static int stdout_stream_line(StdoutStream *s, char *p) {
149 case STDOUT_STREAM_IDENTIFIER:
151 s->identifier = NULL;
153 s->identifier = strdup(p);
158 s->state = STDOUT_STREAM_UNIT_ID;
161 case STDOUT_STREAM_UNIT_ID:
162 if (s->ucred.uid == 0) {
166 s->unit_id = strdup(p);
172 s->state = STDOUT_STREAM_PRIORITY;
175 case STDOUT_STREAM_PRIORITY:
176 r = safe_atoi(p, &s->priority);
177 if (r < 0 || s->priority <= 0 || s->priority >= 999) {
178 log_warning("Failed to parse log priority line.");
182 s->state = STDOUT_STREAM_LEVEL_PREFIX;
185 case STDOUT_STREAM_LEVEL_PREFIX:
186 r = parse_boolean(p);
188 log_warning("Failed to parse level prefix line.");
192 s->level_prefix = !!r;
193 s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG;
196 case STDOUT_STREAM_FORWARD_TO_SYSLOG:
197 r = parse_boolean(p);
199 log_warning("Failed to parse forward to syslog line.");
203 s->forward_to_syslog = !!r;
204 s->state = STDOUT_STREAM_FORWARD_TO_KMSG;
207 case STDOUT_STREAM_FORWARD_TO_KMSG:
208 r = parse_boolean(p);
210 log_warning("Failed to parse copy to kmsg line.");
214 s->forward_to_kmsg = !!r;
215 s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE;
218 case STDOUT_STREAM_FORWARD_TO_CONSOLE:
219 r = parse_boolean(p);
221 log_warning("Failed to parse copy to console line.");
225 s->forward_to_console = !!r;
226 s->state = STDOUT_STREAM_RUNNING;
229 case STDOUT_STREAM_RUNNING:
230 return stdout_stream_log(s, p);
233 assert_not_reached("Unknown stream state");
236 static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
244 remaining = s->length;
249 end = memchr(p, '\n', remaining);
252 else if (remaining >= sizeof(s->buffer) - 1) {
253 end = p + sizeof(s->buffer) - 1;
260 r = stdout_stream_line(s, p);
268 if (force_flush && remaining > 0) {
270 r = stdout_stream_line(s, p);
279 memmove(s->buffer, p, remaining);
280 s->length = remaining;
286 int stdout_stream_process(StdoutStream *s) {
292 l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length);
298 log_warning("Failed to read from stream: %m");
303 r = stdout_stream_scan(s, true);
311 r = stdout_stream_scan(s, false);
319 void stdout_stream_free(StdoutStream *s) {
323 assert(s->server->n_stdout_streams > 0);
324 s->server->n_stdout_streams --;
325 LIST_REMOVE(StdoutStream, stdout_stream, s->server->stdout_streams, s);
330 epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL);
332 close_nointr_nofail(s->fd);
336 if (s->security_context)
337 freecon(s->security_context);
344 int stdout_stream_new(Server *s) {
345 StdoutStream *stream;
348 struct epoll_event ev;
352 fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
357 log_error("Failed to accept stdout connection: %m");
361 if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
362 log_warning("Too many stdout streams, refusing connection.");
363 close_nointr_nofail(fd);
367 stream = new0(StdoutStream, 1);
369 close_nointr_nofail(fd);
375 len = sizeof(stream->ucred);
376 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &stream->ucred, &len) < 0) {
377 log_error("Failed to determine peer credentials: %m");
383 if (getpeercon(fd, &stream->security_context) < 0 && errno != ENOPROTOOPT)
384 log_error("Failed to determine peer security context: %m");
387 if (shutdown(fd, SHUT_WR) < 0) {
388 log_error("Failed to shutdown writing side of socket: %m");
394 ev.data.ptr = stream;
396 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
397 log_error("Failed to add stream to event loop: %m");
403 LIST_PREPEND(StdoutStream, stdout_stream, s->stdout_streams, stream);
404 s->n_stdout_streams ++;
409 stdout_stream_free(stream);
413 int server_open_stdout_socket(Server *s) {
414 union sockaddr_union sa;
416 struct epoll_event ev;
420 if (s->stdout_fd < 0) {
422 s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
423 if (s->stdout_fd < 0) {
424 log_error("socket() failed: %m");
429 sa.un.sun_family = AF_UNIX;
430 strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
432 unlink(sa.un.sun_path);
434 r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
436 log_error("bind() failed: %m");
440 chmod(sa.un.sun_path, 0666);
442 if (listen(s->stdout_fd, SOMAXCONN) < 0) {
443 log_error("liste() failed: %m");
447 fd_nonblock(s->stdout_fd, 1);
451 ev.data.fd = s->stdout_fd;
452 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->stdout_fd, &ev) < 0) {
453 log_error("Failed to add stdout server fd to epoll object: %m");