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