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