]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-syslog.c
journald: do not strip leading whitespace from messages
[thirdparty/systemd.git] / src / journal / journald-syslog.c
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>
23 #include <stddef.h>
24 #include <sys/epoll.h>
25
26 #include "systemd/sd-messages.h"
27 #include "socket-util.h"
28 #include "selinux-util.h"
29 #include "journald-server.h"
30 #include "journald-syslog.h"
31 #include "journald-kmsg.h"
32 #include "journald-console.h"
33 #include "journald-wall.h"
34 #include "formats-util.h"
35 #include "process-util.h"
36
37 /* Warn once every 30s if we missed syslog message */
38 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
39
40 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
41
42 static const union sockaddr_union sa = {
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,
49 .msg_name = (struct sockaddr*) &sa.sa,
50 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
51 + strlen("/run/systemd/journal/syslog"),
52 };
53 struct cmsghdr *cmsg;
54 union {
55 struct cmsghdr cmsghdr;
56 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
57 } control;
58
59 assert(s);
60 assert(iovec);
61 assert(n_iovec > 0);
62
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... */
85 if (errno == EAGAIN) {
86 s->n_forward_syslog_missed++;
87 return;
88 }
89
90 if (ucred && (errno == ESRCH || errno == EPERM)) {
91 struct ucred u;
92
93 /* Hmm, presumably the sender process vanished
94 * by now, or we don't have CAP_SYS_AMDIN, so
95 * let's fix it as good as we can, and retry */
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
104 if (errno == EAGAIN) {
105 s->n_forward_syslog_missed++;
106 return;
107 }
108 }
109
110 if (errno != ENOENT)
111 log_debug_errno(errno, "Failed to forward syslog message: %m");
112 }
113
114 static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) {
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
127 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
128 struct iovec iovec[5];
129 char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
130 header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
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 */
145 xsprintf(header_priority, "<%i>", priority);
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
164 xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
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
183 int 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
191 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
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] != ':')
207 return 0;
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
237 if (strchr(WHITESPACE, p[e]))
238 e++;
239 *buf = p + e;
240 return e;
241 }
242
243 static 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
313 void server_process_syslog_message(
314 Server *s,
315 const char *buf,
316 const struct ucred *ucred,
317 const struct timeval *tv,
318 const char *label,
319 size_t label_len) {
320
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;
324 struct iovec iovec[N_IOVEC_META_FIELDS + 6];
325 unsigned n = 0;
326 int priority = LOG_USER | LOG_INFO;
327 _cleanup_free_ char *identifier = NULL, *pid = NULL;
328 const char *orig;
329
330 assert(s);
331 assert(buf);
332
333 orig = buf;
334 syslog_parse_priority(&buf, &priority, true);
335
336 if (s->forward_to_syslog)
337 forward_syslog_raw(s, priority, orig, ucred, tv);
338
339 syslog_skip_date((char**) &buf);
340 syslog_parse_identifier(&buf, &identifier, &pid);
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
348 if (s->forward_to_wall)
349 server_forward_wall(s, priority, identifier, buf, ucred);
350
351 IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
352
353 sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
354 IOVEC_SET_STRING(iovec[n++], syslog_priority);
355
356 if (priority & LOG_FACMASK) {
357 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
358 IOVEC_SET_STRING(iovec[n++], syslog_facility);
359 }
360
361 if (identifier) {
362 syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
363 if (syslog_identifier)
364 IOVEC_SET_STRING(iovec[n++], syslog_identifier);
365 }
366
367 if (pid) {
368 syslog_pid = strjoina("SYSLOG_PID=", pid);
369 if (syslog_pid)
370 IOVEC_SET_STRING(iovec[n++], syslog_pid);
371 }
372
373 message = strjoina("MESSAGE=", buf);
374 if (message)
375 IOVEC_SET_STRING(iovec[n++], message);
376
377 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
378 }
379
380 int server_open_syslog_socket(Server *s) {
381 static const int one = 1;
382 int r;
383
384 assert(s);
385
386 if (s->syslog_fd < 0) {
387 static const union sockaddr_union sa = {
388 .un.sun_family = AF_UNIX,
389 .un.sun_path = "/run/systemd/journal/dev-log",
390 };
391
392 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
393 if (s->syslog_fd < 0)
394 return log_error_errno(errno, "socket() failed: %m");
395
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));
399 if (r < 0)
400 return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
401
402 (void) chmod(sa.un.sun_path, 0666);
403 } else
404 fd_nonblock(s->syslog_fd, 1);
405
406 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
407 if (r < 0)
408 return log_error_errno(errno, "SO_PASSCRED failed: %m");
409
410 #ifdef HAVE_SELINUX
411 if (mac_selinux_use()) {
412 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
413 if (r < 0)
414 log_warning_errno(errno, "SO_PASSSEC failed: %m");
415 }
416 #endif
417
418 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
419 if (r < 0)
420 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
421
422 r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
423 if (r < 0)
424 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
425
426 return 0;
427 }
428
429 void 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 }