]> git.proxmox.com Git - systemd.git/blame - src/journal/journald-syslog.c
Imported Upstream version 218
[systemd.git] / src / journal / journald-syslog.c
CommitLineData
663996b3
MS
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>
23#include <stddef.h>
24#include <sys/epoll.h>
25
26#include "systemd/sd-messages.h"
27#include "socket-util.h"
60f067b4 28#include "selinux-util.h"
663996b3
MS
29#include "journald-server.h"
30#include "journald-syslog.h"
31#include "journald-kmsg.h"
32#include "journald-console.h"
60f067b4 33#include "journald-wall.h"
663996b3
MS
34
35/* Warn once every 30s if we missed syslog message */
36#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
37
f47781d8 38static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
663996b3 39
5eef597e 40 static const union sockaddr_union sa = {
663996b3
MS
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,
5eef597e 47 .msg_name = (struct sockaddr*) &sa.sa,
663996b3 48 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
60f067b4 49 + strlen("/run/systemd/journal/syslog"),
663996b3
MS
50 };
51 struct cmsghdr *cmsg;
52 union {
53 struct cmsghdr cmsghdr;
54 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
55 } control;
56
57 assert(s);
58 assert(iovec);
59 assert(n_iovec > 0);
60
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... */
83 if (errno == EAGAIN) {
84 s->n_forward_syslog_missed++;
85 return;
86 }
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
102 if (errno == EAGAIN) {
103 s->n_forward_syslog_missed++;
104 return;
105 }
106 }
107
108 if (errno != ENOENT)
f47781d8 109 log_debug_errno(errno, "Failed to forward syslog message: %m");
663996b3
MS
110}
111
f47781d8 112static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) {
663996b3
MS
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
f47781d8 125void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
663996b3
MS
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
60f067b4 162 snprintf(header_pid, sizeof(header_pid), "["PID_FMT"]: ", ucred->pid);
663996b3
MS
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
190size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
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] != ':')
206 return 0;
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
236 e += strspn(p + e, WHITESPACE);
237 *buf = p + e;
238 return e;
239}
240
60f067b4 241void syslog_parse_priority(const char **p, int *priority, bool with_facility) {
663996b3
MS
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
14228c0d
MB
270 if (a < 0 || b < 0 || c < 0 ||
271 (!with_facility && (a || b || c > 7)))
663996b3
MS
272 return;
273
14228c0d
MB
274 if (with_facility)
275 *priority = a*100 + b*10 + c;
276 else
277 *priority = (*priority & LOG_FACMASK) | c;
663996b3
MS
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
351void server_process_syslog_message(
352 Server *s,
353 const char *buf,
f47781d8
MP
354 const struct ucred *ucred,
355 const struct timeval *tv,
663996b3
MS
356 const char *label,
357 size_t label_len) {
358
f47781d8
MP
359 char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
360 syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
361 const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
663996b3
MS
362 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
363 unsigned n = 0;
364 int priority = LOG_USER | LOG_INFO;
f47781d8 365 _cleanup_free_ char *identifier = NULL, *pid = NULL;
663996b3
MS
366 const char *orig;
367
368 assert(s);
369 assert(buf);
370
371 orig = buf;
60f067b4 372 syslog_parse_priority(&buf, &priority, true);
663996b3
MS
373
374 if (s->forward_to_syslog)
375 forward_syslog_raw(s, priority, orig, ucred, tv);
376
377 syslog_skip_date((char**) &buf);
378 syslog_parse_identifier(&buf, &identifier, &pid);
379
380 if (s->forward_to_kmsg)
381 server_forward_kmsg(s, priority, identifier, buf, ucred);
382
383 if (s->forward_to_console)
384 server_forward_console(s, priority, identifier, buf, ucred);
385
60f067b4
JS
386 if (s->forward_to_wall)
387 server_forward_wall(s, priority, identifier, buf, ucred);
388
663996b3
MS
389 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
390
f47781d8
MP
391 sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
392 IOVEC_SET_STRING(iovec[n++], syslog_priority);
663996b3 393
f47781d8
MP
394 if (priority & LOG_FACMASK) {
395 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
396 IOVEC_SET_STRING(iovec[n++], syslog_facility);
397 }
663996b3
MS
398
399 if (identifier) {
f47781d8 400 syslog_identifier = strappenda("SYSLOG_IDENTIFIER=", identifier);
663996b3
MS
401 if (syslog_identifier)
402 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
403 }
404
405 if (pid) {
f47781d8 406 syslog_pid = strappenda("SYSLOG_PID=", pid);
663996b3
MS
407 if (syslog_pid)
408 IOVEC_SET_STRING(iovec[n++], syslog_pid);
409 }
410
f47781d8 411 message = strappenda("MESSAGE=", buf);
663996b3
MS
412 if (message)
413 IOVEC_SET_STRING(iovec[n++], message);
414
14228c0d 415 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
663996b3
MS
416}
417
418int server_open_syslog_socket(Server *s) {
f47781d8
MP
419 static const int one = 1;
420 int r;
663996b3
MS
421
422 assert(s);
423
424 if (s->syslog_fd < 0) {
5eef597e 425 static const union sockaddr_union sa = {
663996b3 426 .un.sun_family = AF_UNIX,
60f067b4 427 .un.sun_path = "/run/systemd/journal/dev-log",
663996b3
MS
428 };
429
430 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
f47781d8
MP
431 if (s->syslog_fd < 0)
432 return log_error_errno(errno, "socket() failed: %m");
663996b3
MS
433
434 unlink(sa.un.sun_path);
435
436 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
f47781d8
MP
437 if (r < 0)
438 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
663996b3
MS
439
440 chmod(sa.un.sun_path, 0666);
441 } else
442 fd_nonblock(s->syslog_fd, 1);
443
663996b3 444 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
f47781d8
MP
445 if (r < 0)
446 return log_error_errno(errno, "SO_PASSCRED failed: %m");
663996b3
MS
447
448#ifdef HAVE_SELINUX
5eef597e 449 if (mac_selinux_use()) {
60f067b4
JS
450 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
451 if (r < 0)
f47781d8 452 log_warning_errno(errno, "SO_PASSSEC failed: %m");
60f067b4 453 }
663996b3
MS
454#endif
455
663996b3 456 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
f47781d8
MP
457 if (r < 0)
458 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
663996b3 459
60f067b4 460 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, process_datagram, s);
f47781d8
MP
461 if (r < 0)
462 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
663996b3
MS
463
464 return 0;
465}
466
467void server_maybe_warn_forward_syslog_missed(Server *s) {
468 usec_t n;
469 assert(s);
470
471 if (s->n_forward_syslog_missed <= 0)
472 return;
473
474 n = now(CLOCK_MONOTONIC);
475 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
476 return;
477
478 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
479
480 s->n_forward_syslog_missed = 0;
481 s->last_warn_forward_syslog_missed = n;
482}