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