chiark / gitweb /
journald: split console transport stuff into its own file
[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 <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 }