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