]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-syslog.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal / journald-syslog.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <stddef.h>
22 #include <sys/epoll.h>
23 #include <unistd.h>
24
25 #include "sd-messages.h"
26
27 #include "alloc-util.h"
28 #include "fd-util.h"
29 #include "format-util.h"
30 #include "io-util.h"
31 #include "journald-console.h"
32 #include "journald-kmsg.h"
33 #include "journald-server.h"
34 #include "journald-syslog.h"
35 #include "journald-wall.h"
36 #include "process-util.h"
37 #include "selinux-util.h"
38 #include "socket-util.h"
39 #include "stdio-util.h"
40 #include "string-util.h"
41 #include "syslog-util.h"
42
43 /* Warn once every 30s if we missed syslog message */
44 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
45
46 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
47
48 static const union sockaddr_union sa = {
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,
55 .msg_name = (struct sockaddr*) &sa.sa,
56 .msg_namelen = SOCKADDR_UN_LEN(sa.un),
57 };
58 struct cmsghdr *cmsg;
59 union {
60 struct cmsghdr cmsghdr;
61 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
62 } control;
63
64 assert(s);
65 assert(iovec);
66 assert(n_iovec > 0);
67
68 if (ucred) {
69 zero(control);
70 msghdr.msg_control = &control;
71 msghdr.msg_controllen = sizeof(control);
72
73 cmsg = CMSG_FIRSTHDR(&msghdr);
74 cmsg->cmsg_level = SOL_SOCKET;
75 cmsg->cmsg_type = SCM_CREDENTIALS;
76 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
77 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
78 msghdr.msg_controllen = cmsg->cmsg_len;
79 }
80
81 /* Forward the syslog message we received via /dev/log to
82 * /run/systemd/syslog. Unfortunately we currently can't set
83 * the SO_TIMESTAMP auxiliary data, and hence we don't. */
84
85 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
86 return;
87
88 /* The socket is full? I guess the syslog implementation is
89 * too slow, and we shouldn't wait for that... */
90 if (errno == EAGAIN) {
91 s->n_forward_syslog_missed++;
92 return;
93 }
94
95 if (ucred && IN_SET(errno, ESRCH, EPERM)) {
96 struct ucred u;
97
98 /* Hmm, presumably the sender process vanished
99 * by now, or we don't have CAP_SYS_AMDIN, so
100 * let's fix it as good as we can, and retry */
101
102 u = *ucred;
103 u.pid = getpid_cached();
104 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
105
106 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
107 return;
108
109 if (errno == EAGAIN) {
110 s->n_forward_syslog_missed++;
111 return;
112 }
113 }
114
115 if (errno != ENOENT)
116 log_debug_errno(errno, "Failed to forward syslog message: %m");
117 }
118
119 static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) {
120 struct iovec iovec;
121
122 assert(s);
123 assert(buffer);
124
125 if (LOG_PRI(priority) > s->max_level_syslog)
126 return;
127
128 iovec = IOVEC_MAKE_STRING(buffer);
129 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
130 }
131
132 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
133 struct iovec iovec[5];
134 char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
135 header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
136 int n = 0;
137 time_t t;
138 struct tm *tm;
139 _cleanup_free_ char *ident_buf = NULL;
140
141 assert(s);
142 assert(priority >= 0);
143 assert(priority <= 999);
144 assert(message);
145
146 if (LOG_PRI(priority) > s->max_level_syslog)
147 return;
148
149 /* First: priority field */
150 xsprintf(header_priority, "<%i>", priority);
151 iovec[n++] = IOVEC_MAKE_STRING(header_priority);
152
153 /* Second: timestamp */
154 t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
155 tm = localtime(&t);
156 if (!tm)
157 return;
158 if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
159 return;
160 iovec[n++] = IOVEC_MAKE_STRING(header_time);
161
162 /* Third: identifier and PID */
163 if (ucred) {
164 if (!identifier) {
165 get_process_comm(ucred->pid, &ident_buf);
166 identifier = ident_buf;
167 }
168
169 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
170
171 if (identifier)
172 iovec[n++] = IOVEC_MAKE_STRING(identifier);
173
174 iovec[n++] = IOVEC_MAKE_STRING(header_pid);
175 } else if (identifier) {
176 iovec[n++] = IOVEC_MAKE_STRING(identifier);
177 iovec[n++] = IOVEC_MAKE_STRING(": ");
178 }
179
180 /* Fourth: message */
181 iovec[n++] = IOVEC_MAKE_STRING(message);
182
183 forward_syslog_iovec(s, iovec, n, ucred, tv);
184 }
185
186 int 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
194 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
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] != ':')
210 return 0;
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
240 if (strchr(WHITESPACE, p[e]))
241 e++;
242 *buf = p + e;
243 return e;
244 }
245
246 static 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
316 void server_process_syslog_message(
317 Server *s,
318 const char *buf,
319 const struct ucred *ucred,
320 const struct timeval *tv,
321 const char *label,
322 size_t label_len) {
323
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;
327 _cleanup_free_ char *identifier = NULL, *pid = NULL;
328 int priority = LOG_USER | LOG_INFO, r;
329 ClientContext *context = NULL;
330 struct iovec *iovec;
331 const char *orig;
332 size_t n = 0, m;
333
334 assert(s);
335 assert(buf);
336
337 if (ucred && pid_is_valid(ucred->pid)) {
338 r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
339 if (r < 0)
340 log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
341 }
342
343 orig = buf;
344 syslog_parse_priority(&buf, &priority, true);
345
346 if (!client_context_test_priority(context, priority))
347 return;
348
349 if (s->forward_to_syslog)
350 forward_syslog_raw(s, priority, orig, ucred, tv);
351
352 syslog_skip_date((char**) &buf);
353 syslog_parse_identifier(&buf, &identifier, &pid);
354
355 if (s->forward_to_kmsg)
356 server_forward_kmsg(s, priority, identifier, buf, ucred);
357
358 if (s->forward_to_console)
359 server_forward_console(s, priority, identifier, buf, ucred);
360
361 if (s->forward_to_wall)
362 server_forward_wall(s, priority, identifier, buf, ucred);
363
364 m = N_IOVEC_META_FIELDS + 6 + client_context_extra_fields_n_iovec(context);
365 iovec = newa(struct iovec, m);
366
367 iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog");
368
369 xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
370 iovec[n++] = IOVEC_MAKE_STRING(syslog_priority);
371
372 if (priority & LOG_FACMASK) {
373 xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
374 iovec[n++] = IOVEC_MAKE_STRING(syslog_facility);
375 }
376
377 if (identifier) {
378 syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
379 iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier);
380 }
381
382 if (pid) {
383 syslog_pid = strjoina("SYSLOG_PID=", pid);
384 iovec[n++] = IOVEC_MAKE_STRING(syslog_pid);
385 }
386
387 message = strjoina("MESSAGE=", buf);
388 if (message)
389 iovec[n++] = IOVEC_MAKE_STRING(message);
390
391 server_dispatch_message(s, iovec, n, m, context, tv, priority, 0);
392 }
393
394 int server_open_syslog_socket(Server *s) {
395
396 static const union sockaddr_union sa = {
397 .un.sun_family = AF_UNIX,
398 .un.sun_path = "/run/systemd/journal/dev-log",
399 };
400 static const int one = 1;
401 int r;
402
403 assert(s);
404
405 if (s->syslog_fd < 0) {
406 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
407 if (s->syslog_fd < 0)
408 return log_error_errno(errno, "socket() failed: %m");
409
410 (void) unlink(sa.un.sun_path);
411
412 r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
413 if (r < 0)
414 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
415
416 (void) chmod(sa.un.sun_path, 0666);
417 } else
418 fd_nonblock(s->syslog_fd, 1);
419
420 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
421 if (r < 0)
422 return log_error_errno(errno, "SO_PASSCRED failed: %m");
423
424 #if HAVE_SELINUX
425 if (mac_selinux_use()) {
426 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
427 if (r < 0)
428 log_warning_errno(errno, "SO_PASSSEC failed: %m");
429 }
430 #endif
431
432 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
433 if (r < 0)
434 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
435
436 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
437 if (r < 0)
438 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
439
440 r = sd_event_source_set_priority(s->syslog_event_source, SD_EVENT_PRIORITY_NORMAL+5);
441 if (r < 0)
442 return log_error_errno(r, "Failed to adjust syslog event source priority: %m");
443
444 return 0;
445 }
446
447 void server_maybe_warn_forward_syslog_missed(Server *s) {
448 usec_t n;
449
450 assert(s);
451
452 if (s->n_forward_syslog_missed <= 0)
453 return;
454
455 n = now(CLOCK_MONOTONIC);
456 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
457 return;
458
459 server_driver_message(s, 0,
460 "MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR,
461 LOG_MESSAGE("Forwarding to syslog missed %u messages.",
462 s->n_forward_syslog_missed),
463 NULL);
464
465 s->n_forward_syslog_missed = 0;
466 s->last_warn_forward_syslog_missed = n;
467 }