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