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