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