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