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