]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-syslog.c
login: make sd_session_get_vt() actually work
[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"
d025f1e4 28#include "journald-server.h"
35e2e347
LP
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 36static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
b92bea5d
ZJS
37
38 union sockaddr_union sa = {
39 .un.sun_family = AF_UNIX,
40 .un.sun_path = "/run/systemd/journal/syslog",
41 };
42 struct msghdr msghdr = {
43 .msg_iov = (struct iovec *) iovec,
44 .msg_iovlen = n_iovec,
45 .msg_name = &sa,
46 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
47 + sizeof("/run/systemd/journal/syslog") - 1,
48 };
35e2e347
LP
49 struct cmsghdr *cmsg;
50 union {
51 struct cmsghdr cmsghdr;
52 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
53 } control;
35e2e347
LP
54
55 assert(s);
56 assert(iovec);
57 assert(n_iovec > 0);
58
35e2e347
LP
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
e88baee8 188size_t 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] != ':')
e88baee8 204 return 0;
35e2e347
LP
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
e88baee8 234 e += strspn(p + e, WHITESPACE);
35e2e347 235 *buf = p + e;
e88baee8 236 return e;
35e2e347
LP
237}
238
ac50788b 239void syslog_parse_priority(char **p, int *priority, bool with_facility) {
5809560d
LP
240 int a = 0, b = 0, c = 0;
241 int k;
242
243 assert(p);
244 assert(*p);
245 assert(priority);
246
247 if ((*p)[0] != '<')
248 return;
249
250 if (!strchr(*p, '>'))
251 return;
252
253 if ((*p)[2] == '>') {
254 c = undecchar((*p)[1]);
255 k = 3;
256 } else if ((*p)[3] == '>') {
257 b = undecchar((*p)[1]);
258 c = undecchar((*p)[2]);
259 k = 4;
260 } else if ((*p)[4] == '>') {
261 a = undecchar((*p)[1]);
262 b = undecchar((*p)[2]);
263 c = undecchar((*p)[3]);
264 k = 5;
265 } else
266 return;
267
ac50788b
ZJS
268 if (a < 0 || b < 0 || c < 0 ||
269 (!with_facility && (a || b || c > 7)))
5809560d
LP
270 return;
271
ac50788b
ZJS
272 if (with_facility)
273 *priority = a*100 + b*10 + c;
274 else
275 *priority = (*priority & LOG_FACMASK) | c;
5809560d
LP
276 *p += k;
277}
278
279static void syslog_skip_date(char **buf) {
280 enum {
281 LETTER,
282 SPACE,
283 NUMBER,
284 SPACE_OR_NUMBER,
285 COLON
286 } sequence[] = {
287 LETTER, LETTER, LETTER,
288 SPACE,
289 SPACE_OR_NUMBER, NUMBER,
290 SPACE,
291 SPACE_OR_NUMBER, NUMBER,
292 COLON,
293 SPACE_OR_NUMBER, NUMBER,
294 COLON,
295 SPACE_OR_NUMBER, NUMBER,
296 SPACE
297 };
298
299 char *p;
300 unsigned i;
301
302 assert(buf);
303 assert(*buf);
304
305 p = *buf;
306
307 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
308
309 if (!*p)
310 return;
311
312 switch (sequence[i]) {
313
314 case SPACE:
315 if (*p != ' ')
316 return;
317 break;
318
319 case SPACE_OR_NUMBER:
320 if (*p == ' ')
321 break;
322
323 /* fall through */
324
325 case NUMBER:
326 if (*p < '0' || *p > '9')
327 return;
328
329 break;
330
331 case LETTER:
332 if (!(*p >= 'A' && *p <= 'Z') &&
333 !(*p >= 'a' && *p <= 'z'))
334 return;
335
336 break;
337
338 case COLON:
339 if (*p != ':')
340 return;
341 break;
342
343 }
344 }
345
346 *buf = p;
347}
348
35e2e347
LP
349void server_process_syslog_message(
350 Server *s,
351 const char *buf,
352 struct ucred *ucred,
353 struct timeval *tv,
354 const char *label,
355 size_t label_len) {
356
357 char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
358 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
359 unsigned n = 0;
360 int priority = LOG_USER | LOG_INFO;
361 char *identifier = NULL, *pid = NULL;
362 const char *orig;
363
364 assert(s);
365 assert(buf);
366
367 orig = buf;
ac50788b 368 syslog_parse_priority((char**) &buf, &priority, true);
35e2e347
LP
369
370 if (s->forward_to_syslog)
371 forward_syslog_raw(s, priority, orig, ucred, tv);
372
373 syslog_skip_date((char**) &buf);
5809560d 374 syslog_parse_identifier(&buf, &identifier, &pid);
35e2e347
LP
375
376 if (s->forward_to_kmsg)
377 server_forward_kmsg(s, priority, identifier, buf, ucred);
378
379 if (s->forward_to_console)
380 server_forward_console(s, priority, identifier, buf, ucred);
381
382 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
383
384 if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
385 IOVEC_SET_STRING(iovec[n++], syslog_priority);
386
387 if (priority & LOG_FACMASK)
388 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
389 IOVEC_SET_STRING(iovec[n++], syslog_facility);
390
391 if (identifier) {
392 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
393 if (syslog_identifier)
394 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
395 }
396
397 if (pid) {
398 syslog_pid = strappend("SYSLOG_PID=", pid);
399 if (syslog_pid)
400 IOVEC_SET_STRING(iovec[n++], syslog_pid);
401 }
402
403 message = strappend("MESSAGE=", buf);
404 if (message)
405 IOVEC_SET_STRING(iovec[n++], message);
406
968f3196 407 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
35e2e347
LP
408
409 free(message);
410 free(identifier);
411 free(pid);
412 free(syslog_priority);
413 free(syslog_facility);
414 free(syslog_identifier);
415 free(syslog_pid);
416}
417
418int server_open_syslog_socket(Server *s) {
35e2e347
LP
419 int one, r;
420 struct epoll_event ev;
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
456 one = 1;
457 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
458 if (r < 0)
459 log_warning("SO_PASSSEC failed: %m");
460#endif
461
462 one = 1;
463 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
464 if (r < 0) {
465 log_error("SO_TIMESTAMP failed: %m");
466 return -errno;
467 }
468
469 zero(ev);
470 ev.events = EPOLLIN;
471 ev.data.fd = s->syslog_fd;
472 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
473 log_error("Failed to add syslog server fd to epoll object: %m");
474 return -errno;
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}