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