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