chiark / gitweb /
journald: add missing includes
[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 #include <sys/epoll.h>
26
27 #ifdef HAVE_SELINUX
28 #include <selinux/selinux.h>
29 #endif
30
31 #include "socket-util.h"
32 #include "journald.h"
33 #include "journald-stream.h"
34 #include "journald-syslog.h"
35 #include "journald-kmsg.h"
36 #include "journald-console.h"
37
38 #define STDOUT_STREAMS_MAX 4096
39
40 typedef 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
51 struct 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
76 static 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
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 int 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
320 void 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
345 int 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
409 fail:
410         stdout_stream_free(stream);
411         return r;
412 }
413
414 int server_open_stdout_socket(Server *s) {
415         union sockaddr_union sa;
416         int r;
417         struct epoll_event ev;
418
419         assert(s);
420
421         if (s->stdout_fd < 0) {
422
423                 s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
424                 if (s->stdout_fd < 0) {
425                         log_error("socket() failed: %m");
426                         return -errno;
427                 }
428
429                 zero(sa);
430                 sa.un.sun_family = AF_UNIX;
431                 strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
432
433                 unlink(sa.un.sun_path);
434
435                 r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
436                 if (r < 0) {
437                         log_error("bind() failed: %m");
438                         return -errno;
439                 }
440
441                 chmod(sa.un.sun_path, 0666);
442
443                 if (listen(s->stdout_fd, SOMAXCONN) < 0) {
444                         log_error("liste() failed: %m");
445                         return -errno;
446                 }
447         } else
448                 fd_nonblock(s->stdout_fd, 1);
449
450         zero(ev);
451         ev.events = EPOLLIN;
452         ev.data.fd = s->stdout_fd;
453         if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->stdout_fd, &ev) < 0) {
454                 log_error("Failed to add stdout server fd to epoll object: %m");
455                 return -errno;
456         }
457
458         return 0;
459 }