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