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