]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-syslog.c
core: use an AF_UNIX/SOCK_DGRAM socket for cgroup agent notification
[thirdparty/systemd.git] / src / journal / journald-syslog.c
CommitLineData
35e2e347
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2011 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
4871690d 20#include <stddef.h>
35e2e347 21#include <sys/epoll.h>
07630cea 22#include <unistd.h>
35e2e347 23
07630cea
LP
24#include "sd-messages.h"
25
b5efdb8a 26#include "alloc-util.h"
3ffd4af2 27#include "fd-util.h"
07630cea 28#include "formats-util.h"
afc5dbf3 29#include "io-util.h"
3b7124a8 30#include "journald-console.h"
07630cea
LP
31#include "journald-kmsg.h"
32#include "journald-server.h"
3ffd4af2 33#include "journald-syslog.h"
40b71e89 34#include "journald-wall.h"
0b452006 35#include "process-util.h"
07630cea
LP
36#include "selinux-util.h"
37#include "socket-util.h"
15a5e950 38#include "stdio-util.h"
07630cea 39#include "string-util.h"
7ccbd1ae 40#include "syslog-util.h"
35e2e347 41
178cc770
LP
42/* Warn once every 30s if we missed syslog message */
43#define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
44
3b3154df 45static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
b92bea5d 46
46b13157 47 static const union sockaddr_union sa = {
b92bea5d
ZJS
48 .un.sun_family = AF_UNIX,
49 .un.sun_path = "/run/systemd/journal/syslog",
50 };
51 struct msghdr msghdr = {
52 .msg_iov = (struct iovec *) iovec,
53 .msg_iovlen = n_iovec,
46b13157 54 .msg_name = (struct sockaddr*) &sa.sa,
b92bea5d 55 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
f8294e41 56 + strlen("/run/systemd/journal/syslog"),
b92bea5d 57 };
35e2e347
LP
58 struct cmsghdr *cmsg;
59 union {
60 struct cmsghdr cmsghdr;
61 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
62 } control;
35e2e347
LP
63
64 assert(s);
65 assert(iovec);
66 assert(n_iovec > 0);
67
35e2e347
LP
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... */
178cc770
LP
90 if (errno == EAGAIN) {
91 s->n_forward_syslog_missed++;
35e2e347 92 return;
178cc770 93 }
35e2e347 94
ccf23ad5 95 if (ucred && (errno == ESRCH || errno == EPERM)) {
35e2e347
LP
96 struct ucred u;
97
98 /* Hmm, presumably the sender process vanished
ccf23ad5
CS
99 * by now, or we don't have CAP_SYS_AMDIN, so
100 * let's fix it as good as we can, and retry */
35e2e347
LP
101
102 u = *ucred;
103 u.pid = getpid();
104 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
105
106 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
107 return;
108
178cc770
LP
109 if (errno == EAGAIN) {
110 s->n_forward_syslog_missed++;
35e2e347 111 return;
178cc770 112 }
35e2e347
LP
113 }
114
115 if (errno != ENOENT)
56f64d95 116 log_debug_errno(errno, "Failed to forward syslog message: %m");
35e2e347
LP
117}
118
3b3154df 119static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) {
35e2e347
LP
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_SET_STRING(iovec, buffer);
129 forward_syslog_iovec(s, &iovec, 1, ucred, tv);
130}
131
3b3154df 132void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
35e2e347 133 struct iovec iovec[5];
3b97fcbd 134 char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
5ffa8c81 135 header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
35e2e347
LP
136 int n = 0;
137 time_t t;
138 struct tm *tm;
139 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 */
5ffa8c81 150 xsprintf(header_priority, "<%i>", priority);
35e2e347
LP
151 IOVEC_SET_STRING(iovec[n++], 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_SET_STRING(iovec[n++], 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
5ffa8c81 169 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
35e2e347
LP
170
171 if (identifier)
172 IOVEC_SET_STRING(iovec[n++], identifier);
173
174 IOVEC_SET_STRING(iovec[n++], header_pid);
175 } else if (identifier) {
176 IOVEC_SET_STRING(iovec[n++], identifier);
177 IOVEC_SET_STRING(iovec[n++], ": ");
178 }
179
180 /* Fourth: message */
181 IOVEC_SET_STRING(iovec[n++], message);
182
183 forward_syslog_iovec(s, iovec, n, ucred, tv);
184
185 free(ident_buf);
186}
187
188int syslog_fixup_facility(int priority) {
189
190 if ((priority & LOG_FACMASK) == 0)
191 return (priority & LOG_PRIMASK) | LOG_USER;
192
193 return priority;
194}
195
e88baee8 196size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
35e2e347
LP
197 const char *p;
198 char *t;
199 size_t l, e;
200
201 assert(buf);
202 assert(identifier);
203 assert(pid);
204
205 p = *buf;
206
207 p += strspn(p, WHITESPACE);
208 l = strcspn(p, WHITESPACE);
209
210 if (l <= 0 ||
211 p[l-1] != ':')
e88baee8 212 return 0;
35e2e347
LP
213
214 e = l;
215 l--;
216
217 if (p[l-1] == ']') {
218 size_t k = l-1;
219
220 for (;;) {
221
222 if (p[k] == '[') {
223 t = strndup(p+k+1, l-k-2);
224 if (t)
225 *pid = t;
226
227 l = k;
228 break;
229 }
230
231 if (k == 0)
232 break;
233
234 k--;
235 }
236 }
237
238 t = strndup(p, l);
239 if (t)
240 *identifier = t;
241
ec5ff444
FB
242 if (strchr(WHITESPACE, p[e]))
243 e++;
35e2e347 244 *buf = p + e;
e88baee8 245 return e;
35e2e347
LP
246}
247
5809560d
LP
248static void syslog_skip_date(char **buf) {
249 enum {
250 LETTER,
251 SPACE,
252 NUMBER,
253 SPACE_OR_NUMBER,
254 COLON
255 } sequence[] = {
256 LETTER, LETTER, LETTER,
257 SPACE,
258 SPACE_OR_NUMBER, NUMBER,
259 SPACE,
260 SPACE_OR_NUMBER, NUMBER,
261 COLON,
262 SPACE_OR_NUMBER, NUMBER,
263 COLON,
264 SPACE_OR_NUMBER, NUMBER,
265 SPACE
266 };
267
268 char *p;
269 unsigned i;
270
271 assert(buf);
272 assert(*buf);
273
274 p = *buf;
275
276 for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
277
278 if (!*p)
279 return;
280
281 switch (sequence[i]) {
282
283 case SPACE:
284 if (*p != ' ')
285 return;
286 break;
287
288 case SPACE_OR_NUMBER:
289 if (*p == ' ')
290 break;
291
292 /* fall through */
293
294 case NUMBER:
295 if (*p < '0' || *p > '9')
296 return;
297
298 break;
299
300 case LETTER:
301 if (!(*p >= 'A' && *p <= 'Z') &&
302 !(*p >= 'a' && *p <= 'z'))
303 return;
304
305 break;
306
307 case COLON:
308 if (*p != ':')
309 return;
310 break;
311
312 }
313 }
314
315 *buf = p;
316}
317
35e2e347
LP
318void server_process_syslog_message(
319 Server *s,
320 const char *buf,
3b3154df
LP
321 const struct ucred *ucred,
322 const struct timeval *tv,
35e2e347
LP
323 const char *label,
324 size_t label_len) {
325
8457f8d6 326 char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
13f5402c 327 syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
8457f8d6 328 const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
35e2e347
LP
329 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
330 unsigned n = 0;
331 int priority = LOG_USER | LOG_INFO;
8457f8d6 332 _cleanup_free_ char *identifier = NULL, *pid = NULL;
35e2e347
LP
333 const char *orig;
334
335 assert(s);
336 assert(buf);
337
338 orig = buf;
e3bfb7be 339 syslog_parse_priority(&buf, &priority, true);
35e2e347
LP
340
341 if (s->forward_to_syslog)
342 forward_syslog_raw(s, priority, orig, ucred, tv);
343
344 syslog_skip_date((char**) &buf);
5809560d 345 syslog_parse_identifier(&buf, &identifier, &pid);
35e2e347
LP
346
347 if (s->forward_to_kmsg)
348 server_forward_kmsg(s, priority, identifier, buf, ucred);
349
350 if (s->forward_to_console)
351 server_forward_console(s, priority, identifier, buf, ucred);
352
40b71e89
ST
353 if (s->forward_to_wall)
354 server_forward_wall(s, priority, identifier, buf, ucred);
355
35e2e347
LP
356 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
357
d054f0a4 358 xsprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
8457f8d6 359 IOVEC_SET_STRING(iovec[n++], syslog_priority);
35e2e347 360
8457f8d6 361 if (priority & LOG_FACMASK) {
d054f0a4 362 xsprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
8457f8d6
LP
363 IOVEC_SET_STRING(iovec[n++], syslog_facility);
364 }
35e2e347
LP
365
366 if (identifier) {
63c372cb 367 syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
35e2e347
LP
368 if (syslog_identifier)
369 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
370 }
371
372 if (pid) {
63c372cb 373 syslog_pid = strjoina("SYSLOG_PID=", pid);
35e2e347
LP
374 if (syslog_pid)
375 IOVEC_SET_STRING(iovec[n++], syslog_pid);
376 }
377
63c372cb 378 message = strjoina("MESSAGE=", buf);
35e2e347
LP
379 if (message)
380 IOVEC_SET_STRING(iovec[n++], message);
381
968f3196 382 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
35e2e347
LP
383}
384
385int server_open_syslog_socket(Server *s) {
3b3154df
LP
386 static const int one = 1;
387 int r;
35e2e347
LP
388
389 assert(s);
390
391 if (s->syslog_fd < 0) {
46b13157 392 static const union sockaddr_union sa = {
b92bea5d 393 .un.sun_family = AF_UNIX,
03ee5c38 394 .un.sun_path = "/run/systemd/journal/dev-log",
b92bea5d 395 };
35e2e347
LP
396
397 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
4a62c710
MS
398 if (s->syslog_fd < 0)
399 return log_error_errno(errno, "socket() failed: %m");
35e2e347 400
35e2e347
LP
401 unlink(sa.un.sun_path);
402
403 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
4a62c710
MS
404 if (r < 0)
405 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
35e2e347 406
4a61c3e5 407 (void) chmod(sa.un.sun_path, 0666);
35e2e347
LP
408 } else
409 fd_nonblock(s->syslog_fd, 1);
410
35e2e347 411 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
4a62c710
MS
412 if (r < 0)
413 return log_error_errno(errno, "SO_PASSCRED failed: %m");
35e2e347
LP
414
415#ifdef HAVE_SELINUX
6355e756 416 if (mac_selinux_have()) {
d682b3a7
LP
417 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
418 if (r < 0)
56f64d95 419 log_warning_errno(errno, "SO_PASSSEC failed: %m");
d682b3a7 420 }
35e2e347
LP
421#endif
422
35e2e347 423 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
4a62c710
MS
424 if (r < 0)
425 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
35e2e347 426
8531ae70 427 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
23bbb0de
MS
428 if (r < 0)
429 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
35e2e347 430
48cef295
VC
431 r = sd_event_source_set_priority(s->syslog_event_source, SD_EVENT_PRIORITY_NORMAL+5);
432 if (r < 0)
433 return log_error_errno(r, "Failed to adjust syslog event source priority: %m");
434
35e2e347
LP
435 return 0;
436}
178cc770
LP
437
438void server_maybe_warn_forward_syslog_missed(Server *s) {
439 usec_t n;
440 assert(s);
441
442 if (s->n_forward_syslog_missed <= 0)
443 return;
444
445 n = now(CLOCK_MONOTONIC);
446 if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
447 return;
448
8a03c9ef
ZJS
449 server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED,
450 LOG_MESSAGE("Forwarding to syslog missed %u messages.",
451 s->n_forward_syslog_missed),
452 NULL);
178cc770
LP
453
454 s->n_forward_syslog_missed = 0;
455 s->last_warn_forward_syslog_missed = n;
456}