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