chiark / gitweb /
sd-journal: properly parse cursor strings
[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.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         struct msghdr msghdr;
38         struct cmsghdr *cmsg;
39         union {
40                 struct cmsghdr cmsghdr;
41                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
42         } control;
43         union sockaddr_union sa;
44
45         assert(s);
46         assert(iovec);
47         assert(n_iovec > 0);
48
49         zero(msghdr);
50         msghdr.msg_iov = (struct iovec*) iovec;
51         msghdr.msg_iovlen = n_iovec;
52
53         zero(sa);
54         sa.un.sun_family = AF_UNIX;
55         strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path));
56         msghdr.msg_name = &sa;
57         msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
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 void 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;
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         *buf = p + e;
235         *buf += strspn(*buf, WHITESPACE);
236 }
237
238 void syslog_parse_priority(char **p, int *priority) {
239         int a = 0, b = 0, c = 0;
240         int k;
241
242         assert(p);
243         assert(*p);
244         assert(priority);
245
246         if ((*p)[0] != '<')
247                 return;
248
249         if (!strchr(*p, '>'))
250                 return;
251
252         if ((*p)[2] == '>') {
253                 c = undecchar((*p)[1]);
254                 k = 3;
255         } else if ((*p)[3] == '>') {
256                 b = undecchar((*p)[1]);
257                 c = undecchar((*p)[2]);
258                 k = 4;
259         } else if ((*p)[4] == '>') {
260                 a = undecchar((*p)[1]);
261                 b = undecchar((*p)[2]);
262                 c = undecchar((*p)[3]);
263                 k = 5;
264         } else
265                 return;
266
267         if (a < 0 || b < 0 || c < 0)
268                 return;
269
270         *priority = a*100+b*10+c;
271         *p += k;
272 }
273
274 static void syslog_skip_date(char **buf) {
275         enum {
276                 LETTER,
277                 SPACE,
278                 NUMBER,
279                 SPACE_OR_NUMBER,
280                 COLON
281         } sequence[] = {
282                 LETTER, LETTER, LETTER,
283                 SPACE,
284                 SPACE_OR_NUMBER, NUMBER,
285                 SPACE,
286                 SPACE_OR_NUMBER, NUMBER,
287                 COLON,
288                 SPACE_OR_NUMBER, NUMBER,
289                 COLON,
290                 SPACE_OR_NUMBER, NUMBER,
291                 SPACE
292         };
293
294         char *p;
295         unsigned i;
296
297         assert(buf);
298         assert(*buf);
299
300         p = *buf;
301
302         for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
303
304                 if (!*p)
305                         return;
306
307                 switch (sequence[i]) {
308
309                 case SPACE:
310                         if (*p != ' ')
311                                 return;
312                         break;
313
314                 case SPACE_OR_NUMBER:
315                         if (*p == ' ')
316                                 break;
317
318                         /* fall through */
319
320                 case NUMBER:
321                         if (*p < '0' || *p > '9')
322                                 return;
323
324                         break;
325
326                 case LETTER:
327                         if (!(*p >= 'A' && *p <= 'Z') &&
328                             !(*p >= 'a' && *p <= 'z'))
329                                 return;
330
331                         break;
332
333                 case COLON:
334                         if (*p != ':')
335                                 return;
336                         break;
337
338                 }
339         }
340
341         *buf = p;
342 }
343
344 void server_process_syslog_message(
345         Server *s,
346         const char *buf,
347         struct ucred *ucred,
348         struct timeval *tv,
349         const char *label,
350         size_t label_len) {
351
352         char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
353         struct iovec iovec[N_IOVEC_META_FIELDS + 6];
354         unsigned n = 0;
355         int priority = LOG_USER | LOG_INFO;
356         char *identifier = NULL, *pid = NULL;
357         const char *orig;
358
359         assert(s);
360         assert(buf);
361
362         orig = buf;
363         syslog_parse_priority((char**) &buf, &priority);
364
365         if (s->forward_to_syslog)
366                 forward_syslog_raw(s, priority, orig, ucred, tv);
367
368         syslog_skip_date((char**) &buf);
369         syslog_parse_identifier(&buf, &identifier, &pid);
370
371         if (s->forward_to_kmsg)
372                 server_forward_kmsg(s, priority, identifier, buf, ucred);
373
374         if (s->forward_to_console)
375                 server_forward_console(s, priority, identifier, buf, ucred);
376
377         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
378
379         if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
380                 IOVEC_SET_STRING(iovec[n++], syslog_priority);
381
382         if (priority & LOG_FACMASK)
383                 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
384                         IOVEC_SET_STRING(iovec[n++], syslog_facility);
385
386         if (identifier) {
387                 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
388                 if (syslog_identifier)
389                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
390         }
391
392         if (pid) {
393                 syslog_pid = strappend("SYSLOG_PID=", pid);
394                 if (syslog_pid)
395                         IOVEC_SET_STRING(iovec[n++], syslog_pid);
396         }
397
398         message = strappend("MESSAGE=", buf);
399         if (message)
400                 IOVEC_SET_STRING(iovec[n++], message);
401
402         server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority);
403
404         free(message);
405         free(identifier);
406         free(pid);
407         free(syslog_priority);
408         free(syslog_facility);
409         free(syslog_identifier);
410         free(syslog_pid);
411 }
412
413 int server_open_syslog_socket(Server *s) {
414         union sockaddr_union sa;
415         int one, r;
416         struct epoll_event ev;
417
418         assert(s);
419
420         if (s->syslog_fd < 0) {
421
422                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
423                 if (s->syslog_fd < 0) {
424                         log_error("socket() failed: %m");
425                         return -errno;
426                 }
427
428                 zero(sa);
429                 sa.un.sun_family = AF_UNIX;
430                 strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
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 }