]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-syslog.c
Merge pull request #2287 from dandedrick/journal-gatewayd-timeout-fix
[thirdparty/systemd.git] / src / journal / journald-syslog.c
CommitLineData
35e2e347
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
4871690d 22#include <stddef.h>
35e2e347 23#include <sys/epoll.h>
07630cea 24#include <unistd.h>
35e2e347 25
07630cea
LP
26#include "sd-messages.h"
27
b5efdb8a 28#include "alloc-util.h"
3ffd4af2 29#include "fd-util.h"
07630cea 30#include "formats-util.h"
afc5dbf3 31#include "io-util.h"
3b7124a8 32#include "journald-console.h"
07630cea
LP
33#include "journald-kmsg.h"
34#include "journald-server.h"
3ffd4af2 35#include "journald-syslog.h"
40b71e89 36#include "journald-wall.h"
0b452006 37#include "process-util.h"
07630cea
LP
38#include "selinux-util.h"
39#include "socket-util.h"
15a5e950 40#include "stdio-util.h"
07630cea 41#include "string-util.h"
7ccbd1ae 42#include "syslog-util.h"
35e2e347 43
178cc770
LP
44/* Warn once every 30s if we missed syslog message */
45#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
46
3b3154df 47static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
b92bea5d 48
46b13157 49 static const union sockaddr_union sa = {
b92bea5d
ZJS
50 .un.sun_family = AF_UNIX,
51 .un.sun_path = "/run/systemd/journal/syslog",
52 };
53 struct msghdr msghdr = {
54 .msg_iov = (struct iovec *) iovec,
55 .msg_iovlen = n_iovec,
46b13157 56 .msg_name = (struct sockaddr*) &sa.sa,
b92bea5d 57 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
f8294e41 58 + strlen("/run/systemd/journal/syslog"),
b92bea5d 59 };
35e2e347
LP
60 struct cmsghdr *cmsg;
61 union {
62 struct cmsghdr cmsghdr;
63 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
64 } control;
35e2e347
LP
65
66 assert(s);
67 assert(iovec);
68 assert(n_iovec > 0);
69
35e2e347
LP
70 if (ucred) {
71 zero(control);
72 msghdr.msg_control = &control;
73 msghdr.msg_controllen = sizeof(control);
74
75 cmsg = CMSG_FIRSTHDR(&msghdr);
76 cmsg->cmsg_level = SOL_SOCKET;
77 cmsg->cmsg_type = SCM_CREDENTIALS;
78 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
79 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
80 msghdr.msg_controllen = cmsg->cmsg_len;
81 }
82
83 /* Forward the syslog message we received via /dev/log to
84 * /run/systemd/syslog. Unfortunately we currently can't set
85 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
86
87 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
88 return;
89
90 /* The socket is full? I guess the syslog implementation is
91 * too slow, and we shouldn't wait for that... */
178cc770
LP
92 if (errno == EAGAIN) {
93 s->n_forward_syslog_missed++;
35e2e347 94 return;
178cc770 95 }
35e2e347 96
ccf23ad5 97 if (ucred && (errno == ESRCH || errno == EPERM)) {
35e2e347
LP
98 struct ucred u;
99
100 /* Hmm, presumably the sender process vanished
ccf23ad5
CS
101 * by now, or we don't have CAP_SYS_AMDIN, so
102 * let's fix it as good as we can, and retry */
35e2e347
LP
103
104 u = *ucred;
105 u.pid = getpid();
106 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
107
108 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
109 return;
110
178cc770
LP
111 if (errno == EAGAIN) {
112 s->n_forward_syslog_missed++;
35e2e347 113 return;
178cc770 114 }
35e2e347
LP
115 }
116
117 if (errno != ENOENT)
56f64d95 118 log_debug_errno(errno, "Failed to forward syslog message: %m");
35e2e347
LP
119}
120
3b3154df 121static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) {
35e2e347
LP
122 struct iovec iovec;
123
124 assert(s);
125 assert(buffer);
126
127 if (LOG_PRI(priority) > s->max_level_syslog)
128 return;
129
130 IOVEC_SET_STRING(iovec, buffer);
131 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
132}
133
3b3154df 134void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
35e2e347 135 struct iovec iovec[5];
3b97fcbd 136 char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
5ffa8c81 137 header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
35e2e347
LP
138 int n = 0;
139 time_t t;
140 struct tm *tm;
141 char *ident_buf = NULL;
142
143 assert(s);
144 assert(priority >= 0);
145 assert(priority <= 999);
146 assert(message);
147
148 if (LOG_PRI(priority) > s->max_level_syslog)
149 return;
150
151 /* First: priority field */
5ffa8c81 152 xsprintf(header_priority, "<%i>", priority);
35e2e347
LP
153 IOVEC_SET_STRING(iovec[n++], header_priority);
154
155 /* Second: timestamp */
156 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
157 tm = localtime(&t);
158 if (!tm)
159 return;
160 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
161 return;
162 IOVEC_SET_STRING(iovec[n++], header_time);
163
164 /* Third: identifier and PID */
165 if (ucred) {
166 if (!identifier) {
167 get_process_comm(ucred->pid, &ident_buf);
168 identifier = ident_buf;
169 }
170
5ffa8c81 171 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
35e2e347
LP
172
173 if (identifier)
174 IOVEC_SET_STRING(iovec[n++], identifier);
175
176 IOVEC_SET_STRING(iovec[n++], header_pid);
177 } else if (identifier) {
178 IOVEC_SET_STRING(iovec[n++], identifier);
179 IOVEC_SET_STRING(iovec[n++], ": ");
180 }
181
182 /* Fourth: message */
183 IOVEC_SET_STRING(iovec[n++], message);
184
185 forward_syslog_iovec(s, iovec, n, ucred, tv);
186
187 free(ident_buf);
188}
189
190int syslog_fixup_facility(int priority) {
191
192 if ((priority & LOG_FACMASK) == 0)
193 return (priority & LOG_PRIMASK) | LOG_USER;
194
195 return priority;
196}
197
e88baee8 198size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
35e2e347
LP
199 const char *p;
200 char *t;
201 size_t l, e;
202
203 assert(buf);
204 assert(identifier);
205 assert(pid);
206
207 p = *buf;
208
209 p += strspn(p, WHITESPACE);
210 l = strcspn(p, WHITESPACE);
211
212 if (l <= 0 ||
213 p[l-1] != ':')
e88baee8 214 return 0;
35e2e347
LP
215
216 e = l;
217 l--;
218
219 if (p[l-1] == ']') {
220 size_t k = l-1;
221
222 for (;;) {
223
224 if (p[k] == '[') {
225 t = strndup(p+k+1, l-k-2);
226 if (t)
227 *pid = t;
228
229 l = k;
230 break;
231 }
232
233 if (k == 0)
234 break;
235
236 k--;
237 }
238 }
239
240 t = strndup(p, l);
241 if (t)
242 *identifier = t;
243
ec5ff444
FB
244 if (strchr(WHITESPACE, p[e]))
245 e++;
35e2e347 246 *buf = p + e;
e88baee8 247 return e;
35e2e347
LP
248}
249
5809560d
LP
250static void syslog_skip_date(char **buf) {
251 enum {
252 LETTER,
253 SPACE,
254 NUMBER,
255 SPACE_OR_NUMBER,
256 COLON
257 } sequence[] = {
258 LETTER, LETTER, LETTER,
259 SPACE,
260 SPACE_OR_NUMBER, NUMBER,
261 SPACE,
262 SPACE_OR_NUMBER, NUMBER,
263 COLON,
264 SPACE_OR_NUMBER, NUMBER,
265 COLON,
266 SPACE_OR_NUMBER, NUMBER,
267 SPACE
268 };
269
270 char *p;
271 unsigned i;
272
273 assert(buf);
274 assert(*buf);
275
276 p = *buf;
277
278 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
279
280 if (!*p)
281 return;
282
283 switch (sequence[i]) {
284
285 case SPACE:
286 if (*p != ' ')
287 return;
288 break;
289
290 case SPACE_OR_NUMBER:
291 if (*p == ' ')
292 break;
293
294 /* fall through */
295
296 case NUMBER:
297 if (*p < '0' || *p > '9')
298 return;
299
300 break;
301
302 case LETTER:
303 if (!(*p >= 'A' && *p <= 'Z') &&
304 !(*p >= 'a' && *p <= 'z'))
305 return;
306
307 break;
308
309 case COLON:
310 if (*p != ':')
311 return;
312 break;
313
314 }
315 }
316
317 *buf = p;
318}
319
35e2e347
LP
320void server_process_syslog_message(
321 Server *s,
322 const char *buf,
3b3154df
LP
323 const struct ucred *ucred,
324 const struct timeval *tv,
35e2e347
LP
325 const char *label,
326 size_t label_len) {
327
8457f8d6
LP
328 char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
329 syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
330 const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
35e2e347
LP
331 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
332 unsigned n = 0;
333 int priority = LOG_USER | LOG_INFO;
8457f8d6 334 _cleanup_free_ char *identifier = NULL, *pid = NULL;
35e2e347
LP
335 const char *orig;
336
337 assert(s);
338 assert(buf);
339
340 orig = buf;
e3bfb7be 341 syslog_parse_priority(&buf, &priority, true);
35e2e347
LP
342
343 if (s->forward_to_syslog)
344 forward_syslog_raw(s, priority, orig, ucred, tv);
345
346 syslog_skip_date((char**) &buf);
5809560d 347 syslog_parse_identifier(&buf, &identifier, &pid);
35e2e347
LP
348
349 if (s->forward_to_kmsg)
350 server_forward_kmsg(s, priority, identifier, buf, ucred);
351
352 if (s->forward_to_console)
353 server_forward_console(s, priority, identifier, buf, ucred);
354
40b71e89
ST
355 if (s->forward_to_wall)
356 server_forward_wall(s, priority, identifier, buf, ucred);
357
35e2e347
LP
358 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
359
8457f8d6
LP
360 sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
361 IOVEC_SET_STRING(iovec[n++], syslog_priority);
35e2e347 362
8457f8d6
LP
363 if (priority & LOG_FACMASK) {
364 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
365 IOVEC_SET_STRING(iovec[n++], syslog_facility);
366 }
35e2e347
LP
367
368 if (identifier) {
63c372cb 369 syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
35e2e347
LP
370 if (syslog_identifier)
371 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
372 }
373
374 if (pid) {
63c372cb 375 syslog_pid = strjoina("SYSLOG_PID=", pid);
35e2e347
LP
376 if (syslog_pid)
377 IOVEC_SET_STRING(iovec[n++], syslog_pid);
378 }
379
63c372cb 380 message = strjoina("MESSAGE=", buf);
35e2e347
LP
381 if (message)
382 IOVEC_SET_STRING(iovec[n++], message);
383
968f3196 384 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
35e2e347
LP
385}
386
387int server_open_syslog_socket(Server *s) {
3b3154df
LP
388 static const int one = 1;
389 int r;
35e2e347
LP
390
391 assert(s);
392
393 if (s->syslog_fd < 0) {
46b13157 394 static const union sockaddr_union sa = {
b92bea5d 395 .un.sun_family = AF_UNIX,
03ee5c38 396 .un.sun_path = "/run/systemd/journal/dev-log",
b92bea5d 397 };
35e2e347
LP
398
399 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
4a62c710
MS
400 if (s->syslog_fd < 0)
401 return log_error_errno(errno, "socket() failed: %m");
35e2e347 402
35e2e347
LP
403 unlink(sa.un.sun_path);
404
405 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
4a62c710
MS
406 if (r < 0)
407 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
35e2e347 408
4a61c3e5 409 (void) chmod(sa.un.sun_path, 0666);
35e2e347
LP
410 } else
411 fd_nonblock(s->syslog_fd, 1);
412
35e2e347 413 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
4a62c710
MS
414 if (r < 0)
415 return log_error_errno(errno, "SO_PASSCRED failed: %m");
35e2e347
LP
416
417#ifdef HAVE_SELINUX
6355e756 418 if (mac_selinux_have()) {
d682b3a7
LP
419 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
420 if (r < 0)
56f64d95 421 log_warning_errno(errno, "SO_PASSSEC failed: %m");
d682b3a7 422 }
35e2e347
LP
423#endif
424
35e2e347 425 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
4a62c710
MS
426 if (r < 0)
427 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
35e2e347 428
8531ae70 429 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
23bbb0de
MS
430 if (r < 0)
431 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
35e2e347
LP
432
433 return 0;
434}
178cc770
LP
435
436void server_maybe_warn_forward_syslog_missed(Server *s) {
437 usec_t n;
438 assert(s);
439
440 if (s->n_forward_syslog_missed <= 0)
441 return;
442
443 n = now(CLOCK_MONOTONIC);
444 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
445 return;
446
447 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
448
449 s->n_forward_syslog_missed = 0;
450 s->last_warn_forward_syslog_missed = n;
451}