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