chiark / gitweb /
systemd-delta: Only print colors when on a tty
[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) {
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                 return;
270
271         *priority = (*priority & LOG_FACMASK) | (a*100 + b*10 + c);
272         *p += k;
273 }
274
275 static void syslog_skip_date(char **buf) {
276         enum {
277                 LETTER,
278                 SPACE,
279                 NUMBER,
280                 SPACE_OR_NUMBER,
281                 COLON
282         } sequence[] = {
283                 LETTER, LETTER, LETTER,
284                 SPACE,
285                 SPACE_OR_NUMBER, NUMBER,
286                 SPACE,
287                 SPACE_OR_NUMBER, NUMBER,
288                 COLON,
289                 SPACE_OR_NUMBER, NUMBER,
290                 COLON,
291                 SPACE_OR_NUMBER, NUMBER,
292                 SPACE
293         };
294
295         char *p;
296         unsigned i;
297
298         assert(buf);
299         assert(*buf);
300
301         p = *buf;
302
303         for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
304
305                 if (!*p)
306                         return;
307
308                 switch (sequence[i]) {
309
310                 case SPACE:
311                         if (*p != ' ')
312                                 return;
313                         break;
314
315                 case SPACE_OR_NUMBER:
316                         if (*p == ' ')
317                                 break;
318
319                         /* fall through */
320
321                 case NUMBER:
322                         if (*p < '0' || *p > '9')
323                                 return;
324
325                         break;
326
327                 case LETTER:
328                         if (!(*p >= 'A' && *p <= 'Z') &&
329                             !(*p >= 'a' && *p <= 'z'))
330                                 return;
331
332                         break;
333
334                 case COLON:
335                         if (*p != ':')
336                                 return;
337                         break;
338
339                 }
340         }
341
342         *buf = p;
343 }
344
345 void server_process_syslog_message(
346         Server *s,
347         const char *buf,
348         struct ucred *ucred,
349         struct timeval *tv,
350         const char *label,
351         size_t label_len) {
352
353         char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
354         struct iovec iovec[N_IOVEC_META_FIELDS + 6];
355         unsigned n = 0;
356         int priority = LOG_USER | LOG_INFO;
357         char *identifier = NULL, *pid = NULL;
358         const char *orig;
359
360         assert(s);
361         assert(buf);
362
363         orig = buf;
364         syslog_parse_priority((char**) &buf, &priority);
365
366         if (s->forward_to_syslog)
367                 forward_syslog_raw(s, priority, orig, ucred, tv);
368
369         syslog_skip_date((char**) &buf);
370         syslog_parse_identifier(&buf, &identifier, &pid);
371
372         if (s->forward_to_kmsg)
373                 server_forward_kmsg(s, priority, identifier, buf, ucred);
374
375         if (s->forward_to_console)
376                 server_forward_console(s, priority, identifier, buf, ucred);
377
378         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
379
380         if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
381                 IOVEC_SET_STRING(iovec[n++], syslog_priority);
382
383         if (priority & LOG_FACMASK)
384                 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
385                         IOVEC_SET_STRING(iovec[n++], syslog_facility);
386
387         if (identifier) {
388                 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
389                 if (syslog_identifier)
390                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
391         }
392
393         if (pid) {
394                 syslog_pid = strappend("SYSLOG_PID=", pid);
395                 if (syslog_pid)
396                         IOVEC_SET_STRING(iovec[n++], syslog_pid);
397         }
398
399         message = strappend("MESSAGE=", buf);
400         if (message)
401                 IOVEC_SET_STRING(iovec[n++], message);
402
403         server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
404
405         free(message);
406         free(identifier);
407         free(pid);
408         free(syslog_priority);
409         free(syslog_facility);
410         free(syslog_identifier);
411         free(syslog_pid);
412 }
413
414 int server_open_syslog_socket(Server *s) {
415         int one, r;
416         struct epoll_event ev;
417
418         assert(s);
419
420         if (s->syslog_fd < 0) {
421                 union sockaddr_union sa = {
422                         .un.sun_family = AF_UNIX,
423                         .un.sun_path = "/dev/log",
424                 };
425
426                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
427                 if (s->syslog_fd < 0) {
428                         log_error("socket() failed: %m");
429                         return -errno;
430                 }
431
432                 unlink(sa.un.sun_path);
433
434                 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
435                 if (r < 0) {
436                         log_error("bind() failed: %m");
437                         return -errno;
438                 }
439
440                 chmod(sa.un.sun_path, 0666);
441         } else
442                 fd_nonblock(s->syslog_fd, 1);
443
444         one = 1;
445         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
446         if (r < 0) {
447                 log_error("SO_PASSCRED failed: %m");
448                 return -errno;
449         }
450
451 #ifdef HAVE_SELINUX
452         one = 1;
453         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
454         if (r < 0)
455                 log_warning("SO_PASSSEC failed: %m");
456 #endif
457
458         one = 1;
459         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
460         if (r < 0) {
461                 log_error("SO_TIMESTAMP failed: %m");
462                 return -errno;
463         }
464
465         zero(ev);
466         ev.events = EPOLLIN;
467         ev.data.fd = s->syslog_fd;
468         if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
469                 log_error("Failed to add syslog server fd to epoll object: %m");
470                 return -errno;
471         }
472
473         return 0;
474 }
475
476 void server_maybe_warn_forward_syslog_missed(Server *s) {
477         usec_t n;
478         assert(s);
479
480         if (s->n_forward_syslog_missed <= 0)
481                 return;
482
483         n = now(CLOCK_MONOTONIC);
484         if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
485                 return;
486
487         server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
488
489         s->n_forward_syslog_missed = 0;
490         s->last_warn_forward_syslog_missed = n;
491 }