chiark / gitweb /
journald: properly update message size after stripping the identifier
[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 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 = 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);
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         union sockaddr_union sa;
416         int one, r;
417         struct epoll_event ev;
418
419         assert(s);
420
421         if (s->syslog_fd < 0) {
422
423                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
424                 if (s->syslog_fd < 0) {
425                         log_error("socket() failed: %m");
426                         return -errno;
427                 }
428
429                 zero(sa);
430                 sa.un.sun_family = AF_UNIX;
431                 strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
432
433                 unlink(sa.un.sun_path);
434
435                 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
436                 if (r < 0) {
437                         log_error("bind() failed: %m");
438                         return -errno;
439                 }
440
441                 chmod(sa.un.sun_path, 0666);
442         } else
443                 fd_nonblock(s->syslog_fd, 1);
444
445         one = 1;
446         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
447         if (r < 0) {
448                 log_error("SO_PASSCRED failed: %m");
449                 return -errno;
450         }
451
452 #ifdef HAVE_SELINUX
453         one = 1;
454         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
455         if (r < 0)
456                 log_warning("SO_PASSSEC failed: %m");
457 #endif
458
459         one = 1;
460         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
461         if (r < 0) {
462                 log_error("SO_TIMESTAMP failed: %m");
463                 return -errno;
464         }
465
466         zero(ev);
467         ev.events = EPOLLIN;
468         ev.data.fd = s->syslog_fd;
469         if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
470                 log_error("Failed to add syslog server fd to epoll object: %m");
471                 return -errno;
472         }
473
474         return 0;
475 }
476
477 void server_maybe_warn_forward_syslog_missed(Server *s) {
478         usec_t n;
479         assert(s);
480
481         if (s->n_forward_syslog_missed <= 0)
482                 return;
483
484         n = now(CLOCK_MONOTONIC);
485         if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
486                 return;
487
488         server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
489
490         s->n_forward_syslog_missed = 0;
491         s->last_warn_forward_syslog_missed = n;
492 }