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