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