chiark / gitweb /
rules: simplify mmc RPMB handling
[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 #include "journald-wall.h"
34
35 /* Warn once every 30s if we missed syslog message */
36 #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC)
37
38 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) {
39
40         static const union sockaddr_union sa = {
41                 .un.sun_family = AF_UNIX,
42                 .un.sun_path = "/run/systemd/journal/syslog",
43         };
44         struct msghdr msghdr = {
45                 .msg_iov = (struct iovec *) iovec,
46                 .msg_iovlen = n_iovec,
47                 .msg_name = (struct sockaddr*) &sa.sa,
48                 .msg_namelen = offsetof(union sockaddr_union, un.sun_path)
49                                + strlen("/run/systemd/journal/syslog"),
50         };
51         struct cmsghdr *cmsg;
52         union {
53                 struct cmsghdr cmsghdr;
54                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
55         } control;
56
57         assert(s);
58         assert(iovec);
59         assert(n_iovec > 0);
60
61         if (ucred) {
62                 zero(control);
63                 msghdr.msg_control = &control;
64                 msghdr.msg_controllen = sizeof(control);
65
66                 cmsg = CMSG_FIRSTHDR(&msghdr);
67                 cmsg->cmsg_level = SOL_SOCKET;
68                 cmsg->cmsg_type = SCM_CREDENTIALS;
69                 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
70                 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
71                 msghdr.msg_controllen = cmsg->cmsg_len;
72         }
73
74         /* Forward the syslog message we received via /dev/log to
75          * /run/systemd/syslog. Unfortunately we currently can't set
76          * the SO_TIMESTAMP auxiliary data, and hence we don't. */
77
78         if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
79                 return;
80
81         /* The socket is full? I guess the syslog implementation is
82          * too slow, and we shouldn't wait for that... */
83         if (errno == EAGAIN) {
84                 s->n_forward_syslog_missed++;
85                 return;
86         }
87
88         if (ucred && (errno == ESRCH || errno == EPERM)) {
89                 struct ucred u;
90
91                 /* Hmm, presumably the sender process vanished
92                  * by now, or we don't have CAP_SYS_AMDIN, so
93                  * let's fix it as good as we can, and retry */
94
95                 u = *ucred;
96                 u.pid = getpid();
97                 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
98
99                 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
100                         return;
101
102                 if (errno == EAGAIN) {
103                         s->n_forward_syslog_missed++;
104                         return;
105                 }
106         }
107
108         if (errno != ENOENT)
109                 log_debug_errno(errno, "Failed to forward syslog message: %m");
110 }
111
112 static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) {
113         struct iovec iovec;
114
115         assert(s);
116         assert(buffer);
117
118         if (LOG_PRI(priority) > s->max_level_syslog)
119                 return;
120
121         IOVEC_SET_STRING(iovec, buffer);
122         forward_syslog_iovec(s, &iovec, 1, ucred, tv);
123 }
124
125 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) {
126         struct iovec iovec[5];
127         char header_priority[DECIMAL_STR_MAX(priority) + 3], header_time[64],
128              header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
129         int n = 0;
130         time_t t;
131         struct tm *tm;
132         char *ident_buf = NULL;
133
134         assert(s);
135         assert(priority >= 0);
136         assert(priority <= 999);
137         assert(message);
138
139         if (LOG_PRI(priority) > s->max_level_syslog)
140                 return;
141
142         /* First: priority field */
143         xsprintf(header_priority, "<%i>", priority);
144         IOVEC_SET_STRING(iovec[n++], header_priority);
145
146         /* Second: timestamp */
147         t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
148         tm = localtime(&t);
149         if (!tm)
150                 return;
151         if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
152                 return;
153         IOVEC_SET_STRING(iovec[n++], header_time);
154
155         /* Third: identifier and PID */
156         if (ucred) {
157                 if (!identifier) {
158                         get_process_comm(ucred->pid, &ident_buf);
159                         identifier = ident_buf;
160                 }
161
162                 xsprintf(header_pid, "["PID_FMT"]: ", ucred->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 static void syslog_skip_date(char **buf) {
241         enum {
242                 LETTER,
243                 SPACE,
244                 NUMBER,
245                 SPACE_OR_NUMBER,
246                 COLON
247         } sequence[] = {
248                 LETTER, LETTER, LETTER,
249                 SPACE,
250                 SPACE_OR_NUMBER, NUMBER,
251                 SPACE,
252                 SPACE_OR_NUMBER, NUMBER,
253                 COLON,
254                 SPACE_OR_NUMBER, NUMBER,
255                 COLON,
256                 SPACE_OR_NUMBER, NUMBER,
257                 SPACE
258         };
259
260         char *p;
261         unsigned i;
262
263         assert(buf);
264         assert(*buf);
265
266         p = *buf;
267
268         for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
269
270                 if (!*p)
271                         return;
272
273                 switch (sequence[i]) {
274
275                 case SPACE:
276                         if (*p != ' ')
277                                 return;
278                         break;
279
280                 case SPACE_OR_NUMBER:
281                         if (*p == ' ')
282                                 break;
283
284                         /* fall through */
285
286                 case NUMBER:
287                         if (*p < '0' || *p > '9')
288                                 return;
289
290                         break;
291
292                 case LETTER:
293                         if (!(*p >= 'A' && *p <= 'Z') &&
294                             !(*p >= 'a' && *p <= 'z'))
295                                 return;
296
297                         break;
298
299                 case COLON:
300                         if (*p != ':')
301                                 return;
302                         break;
303
304                 }
305         }
306
307         *buf = p;
308 }
309
310 void server_process_syslog_message(
311         Server *s,
312         const char *buf,
313         const struct ucred *ucred,
314         const struct timeval *tv,
315         const char *label,
316         size_t label_len) {
317
318         char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
319              syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
320         const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
321         struct iovec iovec[N_IOVEC_META_FIELDS + 6];
322         unsigned n = 0;
323         int priority = LOG_USER | LOG_INFO;
324         _cleanup_free_ char *identifier = NULL, *pid = NULL;
325         const char *orig;
326
327         assert(s);
328         assert(buf);
329
330         orig = buf;
331         syslog_parse_priority(&buf, &priority, true);
332
333         if (s->forward_to_syslog)
334                 forward_syslog_raw(s, priority, orig, ucred, tv);
335
336         syslog_skip_date((char**) &buf);
337         syslog_parse_identifier(&buf, &identifier, &pid);
338
339         if (s->forward_to_kmsg)
340                 server_forward_kmsg(s, priority, identifier, buf, ucred);
341
342         if (s->forward_to_console)
343                 server_forward_console(s, priority, identifier, buf, ucred);
344
345         if (s->forward_to_wall)
346                 server_forward_wall(s, priority, identifier, buf, ucred);
347
348         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
349
350         sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
351         IOVEC_SET_STRING(iovec[n++], syslog_priority);
352
353         if (priority & LOG_FACMASK) {
354                 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
355                 IOVEC_SET_STRING(iovec[n++], syslog_facility);
356         }
357
358         if (identifier) {
359                 syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier);
360                 if (syslog_identifier)
361                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
362         }
363
364         if (pid) {
365                 syslog_pid = strjoina("SYSLOG_PID=", pid);
366                 if (syslog_pid)
367                         IOVEC_SET_STRING(iovec[n++], syslog_pid);
368         }
369
370         message = strjoina("MESSAGE=", buf);
371         if (message)
372                 IOVEC_SET_STRING(iovec[n++], message);
373
374         server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
375 }
376
377 int server_open_syslog_socket(Server *s) {
378         static const int one = 1;
379         int r;
380
381         assert(s);
382
383         if (s->syslog_fd < 0) {
384                 static const union sockaddr_union sa = {
385                         .un.sun_family = AF_UNIX,
386                         .un.sun_path = "/run/systemd/journal/dev-log",
387                 };
388
389                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
390                 if (s->syslog_fd < 0)
391                         return log_error_errno(errno, "socket() failed: %m");
392
393                 unlink(sa.un.sun_path);
394
395                 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
396                 if (r < 0)
397                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
398
399                 chmod(sa.un.sun_path, 0666);
400         } else
401                 fd_nonblock(s->syslog_fd, 1);
402
403         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
404         if (r < 0)
405                 return log_error_errno(errno, "SO_PASSCRED failed: %m");
406
407 #ifdef HAVE_SELINUX
408         if (mac_selinux_use()) {
409                 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
410                 if (r < 0)
411                         log_warning_errno(errno, "SO_PASSSEC failed: %m");
412         }
413 #endif
414
415         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
416         if (r < 0)
417                 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
418
419         r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
420         if (r < 0)
421                 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
422
423         return 0;
424 }
425
426 void server_maybe_warn_forward_syslog_missed(Server *s) {
427         usec_t n;
428         assert(s);
429
430         if (s->n_forward_syslog_missed <= 0)
431                 return;
432
433         n = now(CLOCK_MONOTONIC);
434         if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
435                 return;
436
437         server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
438
439         s->n_forward_syslog_missed = 0;
440         s->last_warn_forward_syslog_missed = n;
441 }