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