chiark / gitweb /
9db9198a49a082829e25e74ade9e1b6ecf579c0e
[elogind.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 }