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