chiark / gitweb /
driverd: implement AddMatch/RemoveMatch logic
[elogind.git] / src / journal / journald-stream.c
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>
24 #include <stddef.h>
25
26 #ifdef HAVE_SELINUX
27 #include <selinux/selinux.h>
28 #endif
29
30 #include "sd-event.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"
38
39 #define STDOUT_STREAMS_MAX 4096
40
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,
49         STDOUT_STREAM_RUNNING
50 } StdoutStreamState;
51
52 struct StdoutStream {
53         Server *server;
54         StdoutStreamState state;
55
56         int fd;
57
58         struct ucred ucred;
59 #ifdef HAVE_SELINUX
60         security_context_t security_context;
61 #endif
62
63         char *identifier;
64         char *unit_id;
65         int priority;
66         bool level_prefix:1;
67         bool forward_to_syslog:1;
68         bool forward_to_kmsg:1;
69         bool forward_to_console:1;
70
71         char buffer[LINE_MAX+1];
72         size_t length;
73
74         sd_event_source *event_source;
75
76         LIST_FIELDS(StdoutStream, stdout_stream);
77 };
78
79 static int stdout_stream_log(StdoutStream *s, const char *p) {
80         struct iovec iovec[N_IOVEC_META_FIELDS + 5];
81         int priority;
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;
85         unsigned n = 0;
86         char *label = NULL;
87         size_t label_len = 0;
88
89         assert(s);
90         assert(p);
91
92         if (isempty(p))
93                 return 0;
94
95         priority = s->priority;
96
97         if (s->level_prefix)
98                 syslog_parse_priority(&p, &priority, false);
99
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);
102
103         if (s->forward_to_kmsg || s->server->forward_to_kmsg)
104                 server_forward_kmsg(s->server, priority, s->identifier, p, &s->ucred);
105
106         if (s->forward_to_console || s->server->forward_to_console)
107                 server_forward_console(s->server, priority, s->identifier, p, &s->ucred);
108
109         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout");
110
111         syslog_priority[strlen("PRIORITY=")] = '0' + LOG_PRI(priority);
112         IOVEC_SET_STRING(iovec[n++], syslog_priority);
113
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);
117         }
118
119         if (s->identifier) {
120                 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier);
121                 if (syslog_identifier)
122                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
123         }
124
125         message = strappend("MESSAGE=", p);
126         if (message)
127                 IOVEC_SET_STRING(iovec[n++], message);
128
129 #ifdef HAVE_SELINUX
130         if (s->security_context) {
131                 label = (char*) s->security_context;
132                 label_len = strlen((char*) s->security_context);
133         }
134 #endif
135
136         server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, label, label_len, s->unit_id, priority, 0);
137         return 0;
138 }
139
140 static 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);
178                 if (r < 0 || s->priority < 0 || s->priority > 999) {
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
237 static 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
287 static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
288         StdoutStream *s = userdata;
289         ssize_t l;
290         int r;
291
292         assert(s);
293
294         if ((revents|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) {
295                 log_error("Got invalid event from epoll for stdout stream: %"PRIx32, revents);
296                 goto terminate;
297         }
298
299         l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length);
300         if (l < 0) {
301
302                 if (errno == EAGAIN)
303                         return 0;
304
305                 log_warning("Failed to read from stream: %m");
306                 goto terminate;
307         }
308
309         if (l == 0) {
310                 stdout_stream_scan(s, true);
311                 goto terminate;
312         }
313
314         s->length += l;
315         r = stdout_stream_scan(s, false);
316         if (r < 0)
317                 goto terminate;
318
319         return 1;
320
321 terminate:
322         stdout_stream_free(s);
323         return 0;
324 }
325
326 void stdout_stream_free(StdoutStream *s) {
327         assert(s);
328
329         if (s->server) {
330                 assert(s->server->n_stdout_streams > 0);
331                 s->server->n_stdout_streams --;
332                 LIST_REMOVE(stdout_stream, s->server->stdout_streams, s);
333         }
334
335         if (s->event_source) {
336                 sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF);
337                 s->event_source = sd_event_source_unref(s->event_source);
338         }
339
340         if (s->fd >= 0)
341                 close_nointr_nofail(s->fd);
342
343 #ifdef HAVE_SELINUX
344         if (s->security_context)
345                 freecon(s->security_context);
346 #endif
347
348         free(s->identifier);
349         free(s->unit_id);
350         free(s);
351 }
352
353 static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revents, void *userdata) {
354         Server *s = userdata;
355         StdoutStream *stream;
356         int fd, r;
357         socklen_t len;
358
359         assert(s);
360
361         if (revents != EPOLLIN) {
362                 log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents);
363                 return -EIO;
364         }
365
366         fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
367         if (fd < 0) {
368                 if (errno == EAGAIN)
369                         return 0;
370
371                 log_error("Failed to accept stdout connection: %m");
372                 return -errno;
373         }
374
375         if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
376                 log_warning("Too many stdout streams, refusing connection.");
377                 close_nointr_nofail(fd);
378                 return 0;
379         }
380
381         stream = new0(StdoutStream, 1);
382         if (!stream) {
383                 close_nointr_nofail(fd);
384                 return log_oom();
385         }
386
387         stream->fd = fd;
388
389         len = sizeof(stream->ucred);
390         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &stream->ucred, &len) < 0) {
391                 log_error("Failed to determine peer credentials: %m");
392                 goto fail;
393         }
394
395 #ifdef HAVE_SELINUX
396         if (use_selinux()) {
397                 if (getpeercon(fd, &stream->security_context) < 0 && errno != ENOPROTOOPT)
398                         log_error("Failed to determine peer security context: %m");
399         }
400 #endif
401
402         if (shutdown(fd, SHUT_WR) < 0) {
403                 log_error("Failed to shutdown writing side of socket: %m");
404                 goto fail;
405         }
406
407         r = sd_event_add_io(s->event, fd, EPOLLIN, stdout_stream_process, stream, &stream->event_source);
408         if (r < 0) {
409                 log_error("Failed to add stream to event loop: %s", strerror(-r));
410                 goto fail;
411         }
412
413         r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5);
414         if (r < 0) {
415                 log_error("Failed to adjust stdout event source priority: %s", strerror(-r));
416                 goto fail;
417         }
418
419         stream->server = s;
420         LIST_PREPEND(stdout_stream, s->stdout_streams, stream);
421         s->n_stdout_streams ++;
422
423         return 0;
424
425 fail:
426         stdout_stream_free(stream);
427         return 0;
428 }
429
430 int server_open_stdout_socket(Server *s) {
431         int r;
432
433         assert(s);
434
435         if (s->stdout_fd < 0) {
436                 union sockaddr_union sa = {
437                         .un.sun_family = AF_UNIX,
438                         .un.sun_path = "/run/systemd/journal/stdout",
439                 };
440
441                 s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
442                 if (s->stdout_fd < 0) {
443                         log_error("socket() failed: %m");
444                         return -errno;
445                 }
446
447                 unlink(sa.un.sun_path);
448
449                 r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
450                 if (r < 0) {
451                         log_error("bind() failed: %m");
452                         return -errno;
453                 }
454
455                 chmod(sa.un.sun_path, 0666);
456
457                 if (listen(s->stdout_fd, SOMAXCONN) < 0) {
458                         log_error("listen() failed: %m");
459                         return -errno;
460                 }
461         } else
462                 fd_nonblock(s->stdout_fd, 1);
463
464         r = sd_event_add_io(s->event, s->stdout_fd, EPOLLIN, stdout_stream_new, s, &s->stdout_event_source);
465         if (r < 0) {
466                 log_error("Failed to add stdout server fd to event source: %s", strerror(-r));
467                 return r;
468         }
469
470         r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+10);
471         if (r < 0) {
472                 log_error("Failed to adjust priority of stdout server event source: %s", strerror(-r));
473                 return r;
474         }
475
476         return 0;
477 }