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