]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-stream.c
treewide: use log_*_errno whenever %m is in the format string
[thirdparty/systemd.git] / src / journal / journald-stream.c
CommitLineData
a45b9fca
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
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.
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 Lesser General Public License for more details.
17
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/>.
20***/
21
22#include <fcntl.h>
23#include <unistd.h>
4871690d 24#include <stddef.h>
a45b9fca
LP
25
26#ifdef HAVE_SELINUX
27#include <selinux/selinux.h>
28#endif
29
f9a810be 30#include "sd-event.h"
a45b9fca 31#include "socket-util.h"
d682b3a7 32#include "selinux-util.h"
d025f1e4 33#include "journald-server.h"
a45b9fca
LP
34#include "journald-stream.h"
35#include "journald-syslog.h"
36#include "journald-kmsg.h"
3b7124a8 37#include "journald-console.h"
40b71e89 38#include "journald-wall.h"
a45b9fca
LP
39
40#define STDOUT_STREAMS_MAX 4096
41
42typedef enum StdoutStreamState {
43 STDOUT_STREAM_IDENTIFIER,
44 STDOUT_STREAM_UNIT_ID,
45 STDOUT_STREAM_PRIORITY,
46 STDOUT_STREAM_LEVEL_PREFIX,
47 STDOUT_STREAM_FORWARD_TO_SYSLOG,
48 STDOUT_STREAM_FORWARD_TO_KMSG,
49 STDOUT_STREAM_FORWARD_TO_CONSOLE,
50 STDOUT_STREAM_RUNNING
51} StdoutStreamState;
52
53struct StdoutStream {
54 Server *server;
55 StdoutStreamState state;
56
57 int fd;
58
59 struct ucred ucred;
60#ifdef HAVE_SELINUX
61 security_context_t security_context;
62#endif
63
64 char *identifier;
65 char *unit_id;
66 int priority;
67 bool level_prefix:1;
68 bool forward_to_syslog:1;
69 bool forward_to_kmsg:1;
70 bool forward_to_console:1;
71
72 char buffer[LINE_MAX+1];
73 size_t length;
74
f9a810be
LP
75 sd_event_source *event_source;
76
a45b9fca
LP
77 LIST_FIELDS(StdoutStream, stdout_stream);
78};
79
80static int stdout_stream_log(StdoutStream *s, const char *p) {
81 struct iovec iovec[N_IOVEC_META_FIELDS + 5];
a45b9fca 82 int priority;
e3bfb7be
ZJS
83 char syslog_priority[] = "PRIORITY=\0";
84 char syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(priority)];
85 _cleanup_free_ char *message = NULL, *syslog_identifier = NULL;
86 unsigned n = 0;
a45b9fca
LP
87 char *label = NULL;
88 size_t label_len = 0;
89
90 assert(s);
91 assert(p);
92
93 if (isempty(p))
94 return 0;
95
96 priority = s->priority;
97
98 if (s->level_prefix)
e3bfb7be 99 syslog_parse_priority(&p, &priority, false);
a45b9fca
LP
100
101 if (s->forward_to_syslog || s->server->forward_to_syslog)
102 server_forward_syslog(s->server, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL);
103
104 if (s->forward_to_kmsg || s->server->forward_to_kmsg)
105 server_forward_kmsg(s->server, priority, s->identifier, p, &s->ucred);
106
107 if (s->forward_to_console || s->server->forward_to_console)
108 server_forward_console(s->server, priority, s->identifier, p, &s->ucred);
109
40b71e89
ST
110 if (s->server->forward_to_wall)
111 server_forward_wall(s->server, priority, s->identifier, p, &s->ucred);
112
a45b9fca
LP
113 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout");
114
e3bfb7be
ZJS
115 syslog_priority[strlen("PRIORITY=")] = '0' + LOG_PRI(priority);
116 IOVEC_SET_STRING(iovec[n++], syslog_priority);
a45b9fca 117
e3bfb7be
ZJS
118 if (priority & LOG_FACMASK) {
119 snprintf(syslog_facility, sizeof(syslog_facility), "SYSLOG_FACILITY=%i", LOG_FAC(priority));
120 IOVEC_SET_STRING(iovec[n++], syslog_facility);
121 }
a45b9fca
LP
122
123 if (s->identifier) {
124 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier);
125 if (syslog_identifier)
126 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
127 }
128
129 message = strappend("MESSAGE=", p);
130 if (message)
131 IOVEC_SET_STRING(iovec[n++], message);
132
133#ifdef HAVE_SELINUX
134 if (s->security_context) {
135 label = (char*) s->security_context;
136 label_len = strlen((char*) s->security_context);
137 }
138#endif
139
968f3196 140 server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, label, label_len, s->unit_id, priority, 0);
a45b9fca
LP
141 return 0;
142}
143
144static int stdout_stream_line(StdoutStream *s, char *p) {
145 int r;
146
147 assert(s);
148 assert(p);
149
150 p = strstrip(p);
151
152 switch (s->state) {
153
154 case STDOUT_STREAM_IDENTIFIER:
155 if (isempty(p))
156 s->identifier = NULL;
157 else {
158 s->identifier = strdup(p);
159 if (!s->identifier)
160 return log_oom();
161 }
162
163 s->state = STDOUT_STREAM_UNIT_ID;
164 return 0;
165
166 case STDOUT_STREAM_UNIT_ID:
167 if (s->ucred.uid == 0) {
168 if (isempty(p))
169 s->unit_id = NULL;
170 else {
171 s->unit_id = strdup(p);
172 if (!s->unit_id)
173 return log_oom();
174 }
175 }
176
177 s->state = STDOUT_STREAM_PRIORITY;
178 return 0;
179
180 case STDOUT_STREAM_PRIORITY:
181 r = safe_atoi(p, &s->priority);
41891700 182 if (r < 0 || s->priority < 0 || s->priority > 999) {
a45b9fca
LP
183 log_warning("Failed to parse log priority line.");
184 return -EINVAL;
185 }
186
187 s->state = STDOUT_STREAM_LEVEL_PREFIX;
188 return 0;
189
190 case STDOUT_STREAM_LEVEL_PREFIX:
191 r = parse_boolean(p);
192 if (r < 0) {
193 log_warning("Failed to parse level prefix line.");
194 return -EINVAL;
195 }
196
197 s->level_prefix = !!r;
198 s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG;
199 return 0;
200
201 case STDOUT_STREAM_FORWARD_TO_SYSLOG:
202 r = parse_boolean(p);
203 if (r < 0) {
204 log_warning("Failed to parse forward to syslog line.");
205 return -EINVAL;
206 }
207
208 s->forward_to_syslog = !!r;
209 s->state = STDOUT_STREAM_FORWARD_TO_KMSG;
210 return 0;
211
212 case STDOUT_STREAM_FORWARD_TO_KMSG:
213 r = parse_boolean(p);
214 if (r < 0) {
215 log_warning("Failed to parse copy to kmsg line.");
216 return -EINVAL;
217 }
218
219 s->forward_to_kmsg = !!r;
220 s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE;
221 return 0;
222
223 case STDOUT_STREAM_FORWARD_TO_CONSOLE:
224 r = parse_boolean(p);
225 if (r < 0) {
226 log_warning("Failed to parse copy to console line.");
227 return -EINVAL;
228 }
229
230 s->forward_to_console = !!r;
231 s->state = STDOUT_STREAM_RUNNING;
232 return 0;
233
234 case STDOUT_STREAM_RUNNING:
235 return stdout_stream_log(s, p);
236 }
237
238 assert_not_reached("Unknown stream state");
239}
240
241static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
242 char *p;
243 size_t remaining;
244 int r;
245
246 assert(s);
247
248 p = s->buffer;
249 remaining = s->length;
250 for (;;) {
251 char *end;
252 size_t skip;
253
254 end = memchr(p, '\n', remaining);
255 if (end)
256 skip = end - p + 1;
257 else if (remaining >= sizeof(s->buffer) - 1) {
258 end = p + sizeof(s->buffer) - 1;
259 skip = remaining;
260 } else
261 break;
262
263 *end = 0;
264
265 r = stdout_stream_line(s, p);
266 if (r < 0)
267 return r;
268
269 remaining -= skip;
270 p += skip;
271 }
272
273 if (force_flush && remaining > 0) {
274 p[remaining] = 0;
275 r = stdout_stream_line(s, p);
276 if (r < 0)
277 return r;
278
279 p += remaining;
280 remaining = 0;
281 }
282
283 if (p > s->buffer) {
284 memmove(s->buffer, p, remaining);
285 s->length = remaining;
286 }
287
288 return 0;
289}
290
f9a810be
LP
291static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
292 StdoutStream *s = userdata;
a45b9fca
LP
293 ssize_t l;
294 int r;
295
296 assert(s);
297
f9a810be
LP
298 if ((revents|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) {
299 log_error("Got invalid event from epoll for stdout stream: %"PRIx32, revents);
91bf3b3e 300 goto terminate;
f9a810be
LP
301 }
302
a45b9fca
LP
303 l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length);
304 if (l < 0) {
305
306 if (errno == EAGAIN)
307 return 0;
308
56f64d95 309 log_warning_errno(errno, "Failed to read from stream: %m");
91bf3b3e 310 goto terminate;
a45b9fca
LP
311 }
312
313 if (l == 0) {
7b77ed8c 314 stdout_stream_scan(s, true);
91bf3b3e 315 goto terminate;
a45b9fca
LP
316 }
317
318 s->length += l;
319 r = stdout_stream_scan(s, false);
320 if (r < 0)
91bf3b3e 321 goto terminate;
a45b9fca
LP
322
323 return 1;
324
91bf3b3e 325terminate:
f9a810be
LP
326 stdout_stream_free(s);
327 return 0;
a45b9fca
LP
328}
329
330void stdout_stream_free(StdoutStream *s) {
331 assert(s);
332
333 if (s->server) {
334 assert(s->server->n_stdout_streams > 0);
335 s->server->n_stdout_streams --;
71fda00f 336 LIST_REMOVE(stdout_stream, s->server->stdout_streams, s);
a45b9fca
LP
337 }
338
21c6dc33
LP
339 if (s->event_source) {
340 sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF);
f9a810be 341 s->event_source = sd_event_source_unref(s->event_source);
21c6dc33 342 }
a45b9fca 343
03e334a1 344 safe_close(s->fd);
a45b9fca
LP
345
346#ifdef HAVE_SELINUX
347 if (s->security_context)
348 freecon(s->security_context);
349#endif
350
351 free(s->identifier);
f92ae496 352 free(s->unit_id);
a45b9fca
LP
353 free(s);
354}
355
f9a810be
LP
356static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revents, void *userdata) {
357 Server *s = userdata;
a45b9fca
LP
358 StdoutStream *stream;
359 int fd, r;
a45b9fca
LP
360
361 assert(s);
362
f9a810be
LP
363 if (revents != EPOLLIN) {
364 log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents);
365 return -EIO;
366 }
367
a45b9fca
LP
368 fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
369 if (fd < 0) {
370 if (errno == EAGAIN)
371 return 0;
372
56f64d95 373 log_error_errno(errno, "Failed to accept stdout connection: %m");
a45b9fca
LP
374 return -errno;
375 }
376
377 if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
378 log_warning("Too many stdout streams, refusing connection.");
03e334a1 379 safe_close(fd);
a45b9fca
LP
380 return 0;
381 }
382
383 stream = new0(StdoutStream, 1);
384 if (!stream) {
03e334a1 385 safe_close(fd);
a45b9fca
LP
386 return log_oom();
387 }
388
389 stream->fd = fd;
390
eff05270
LP
391 r = getpeercred(fd, &stream->ucred);
392 if (r < 0) {
56f64d95 393 log_error_errno(errno, "Failed to determine peer credentials: %m");
a45b9fca
LP
394 goto fail;
395 }
396
397#ifdef HAVE_SELINUX
6baa7db0 398 if (mac_selinux_use()) {
d682b3a7 399 if (getpeercon(fd, &stream->security_context) < 0 && errno != ENOPROTOOPT)
56f64d95 400 log_error_errno(errno, "Failed to determine peer security context: %m");
d682b3a7 401 }
a45b9fca
LP
402#endif
403
404 if (shutdown(fd, SHUT_WR) < 0) {
56f64d95 405 log_error_errno(errno, "Failed to shutdown writing side of socket: %m");
a45b9fca
LP
406 goto fail;
407 }
408
151b9b96 409 r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream);
f9a810be 410 if (r < 0) {
da927ba9 411 log_error_errno(r, "Failed to add stream to event loop: %m");
f9a810be
LP
412 goto fail;
413 }
414
415 r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
416 if (r < 0) {
da927ba9 417 log_error_errno(r, "Failed to adjust stdout event source priority: %m");
a45b9fca
LP
418 goto fail;
419 }
420
421 stream->server = s;
71fda00f 422 LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
a45b9fca
LP
423 s->n_stdout_streams ++;
424
425 return 0;
426
427fail:
428 stdout_stream_free(stream);
7b77ed8c 429 return 0;
a45b9fca
LP
430}
431
432int server_open_stdout_socket(Server *s) {
a45b9fca 433 int r;
a45b9fca
LP
434
435 assert(s);
436
437 if (s->stdout_fd < 0) {
b92bea5d
ZJS
438 union sockaddr_union sa = {
439 .un.sun_family = AF_UNIX,
440 .un.sun_path = "/run/systemd/journal/stdout",
441 };
a45b9fca
LP
442
443 s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
444 if (s->stdout_fd < 0) {
56f64d95 445 log_error_errno(errno, "socket() failed: %m");
a45b9fca
LP
446 return -errno;
447 }
448
a45b9fca
LP
449 unlink(sa.un.sun_path);
450
451 r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
452 if (r < 0) {
56f64d95 453 log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
a45b9fca
LP
454 return -errno;
455 }
456
457 chmod(sa.un.sun_path, 0666);
458
459 if (listen(s->stdout_fd, SOMAXCONN) < 0) {
56f64d95 460 log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path);
a45b9fca
LP
461 return -errno;
462 }
463 } else
464 fd_nonblock(s->stdout_fd, 1);
465
151b9b96 466 r = sd_event_add_io(s->event, &s->stdout_event_source, s->stdout_fd, EPOLLIN, stdout_stream_new, s);
23bbb0de
MS
467 if (r < 0)
468 return log_error_errno(r, "Failed to add stdout server fd to event source: %m");
f9a810be
LP
469
470 r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+10);
23bbb0de
MS
471 if (r < 0)
472 return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m");
a45b9fca
LP
473
474 return 0;
475}