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