chiark / gitweb /
machinectl: fix handling of --verify= argument for dkr downloads
[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[6], header_time[64], header_pid[16];
128         int n = 0;
129         time_t t;
130         struct tm *tm;
131         char *ident_buf = NULL;
132
133         assert(s);
134         assert(priority >= 0);
135         assert(priority <= 999);
136         assert(message);
137
138         if (LOG_PRI(priority) > s->max_level_syslog)
139                 return;
140
141         /* First: priority field */
142         snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
143         char_array_0(header_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                 snprintf(header_pid, sizeof(header_pid), "["PID_FMT"]: ", ucred->pid);
163                 char_array_0(header_pid);
164
165                 if (identifier)
166                         IOVEC_SET_STRING(iovec[n++], identifier);
167
168                 IOVEC_SET_STRING(iovec[n++], header_pid);
169         } else if (identifier) {
170                 IOVEC_SET_STRING(iovec[n++], identifier);
171                 IOVEC_SET_STRING(iovec[n++], ": ");
172         }
173
174         /* Fourth: message */
175         IOVEC_SET_STRING(iovec[n++], message);
176
177         forward_syslog_iovec(s, iovec, n, ucred, tv);
178
179         free(ident_buf);
180 }
181
182 int syslog_fixup_facility(int priority) {
183
184         if ((priority & LOG_FACMASK) == 0)
185                 return (priority & LOG_PRIMASK) | LOG_USER;
186
187         return priority;
188 }
189
190 size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) {
191         const char *p;
192         char *t;
193         size_t l, e;
194
195         assert(buf);
196         assert(identifier);
197         assert(pid);
198
199         p = *buf;
200
201         p += strspn(p, WHITESPACE);
202         l = strcspn(p, WHITESPACE);
203
204         if (l <= 0 ||
205             p[l-1] != ':')
206                 return 0;
207
208         e = l;
209         l--;
210
211         if (p[l-1] == ']') {
212                 size_t k = l-1;
213
214                 for (;;) {
215
216                         if (p[k] == '[') {
217                                 t = strndup(p+k+1, l-k-2);
218                                 if (t)
219                                         *pid = t;
220
221                                 l = k;
222                                 break;
223                         }
224
225                         if (k == 0)
226                                 break;
227
228                         k--;
229                 }
230         }
231
232         t = strndup(p, l);
233         if (t)
234                 *identifier = t;
235
236         e += strspn(p + e, WHITESPACE);
237         *buf = p + e;
238         return e;
239 }
240
241 static void syslog_skip_date(char **buf) {
242         enum {
243                 LETTER,
244                 SPACE,
245                 NUMBER,
246                 SPACE_OR_NUMBER,
247                 COLON
248         } sequence[] = {
249                 LETTER, LETTER, LETTER,
250                 SPACE,
251                 SPACE_OR_NUMBER, NUMBER,
252                 SPACE,
253                 SPACE_OR_NUMBER, NUMBER,
254                 COLON,
255                 SPACE_OR_NUMBER, NUMBER,
256                 COLON,
257                 SPACE_OR_NUMBER, NUMBER,
258                 SPACE
259         };
260
261         char *p;
262         unsigned i;
263
264         assert(buf);
265         assert(*buf);
266
267         p = *buf;
268
269         for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
270
271                 if (!*p)
272                         return;
273
274                 switch (sequence[i]) {
275
276                 case SPACE:
277                         if (*p != ' ')
278                                 return;
279                         break;
280
281                 case SPACE_OR_NUMBER:
282                         if (*p == ' ')
283                                 break;
284
285                         /* fall through */
286
287                 case NUMBER:
288                         if (*p < '0' || *p > '9')
289                                 return;
290
291                         break;
292
293                 case LETTER:
294                         if (!(*p >= 'A' && *p <= 'Z') &&
295                             !(*p >= 'a' && *p <= 'z'))
296                                 return;
297
298                         break;
299
300                 case COLON:
301                         if (*p != ':')
302                                 return;
303                         break;
304
305                 }
306         }
307
308         *buf = p;
309 }
310
311 void server_process_syslog_message(
312         Server *s,
313         const char *buf,
314         const struct ucred *ucred,
315         const struct timeval *tv,
316         const char *label,
317         size_t label_len) {
318
319         char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
320              syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)];
321         const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
322         struct iovec iovec[N_IOVEC_META_FIELDS + 6];
323         unsigned n = 0;
324         int priority = LOG_USER | LOG_INFO;
325         _cleanup_free_ char *identifier = NULL, *pid = NULL;
326         const char *orig;
327
328         assert(s);
329         assert(buf);
330
331         orig = buf;
332         syslog_parse_priority(&buf, &priority, true);
333
334         if (s->forward_to_syslog)
335                 forward_syslog_raw(s, priority, orig, ucred, tv);
336
337         syslog_skip_date((char**) &buf);
338         syslog_parse_identifier(&buf, &identifier, &pid);
339
340         if (s->forward_to_kmsg)
341                 server_forward_kmsg(s, priority, identifier, buf, ucred);
342
343         if (s->forward_to_console)
344                 server_forward_console(s, priority, identifier, buf, ucred);
345
346         if (s->forward_to_wall)
347                 server_forward_wall(s, priority, identifier, buf, ucred);
348
349         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
350
351         sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK);
352         IOVEC_SET_STRING(iovec[n++], syslog_priority);
353
354         if (priority & LOG_FACMASK) {
355                 sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority));
356                 IOVEC_SET_STRING(iovec[n++], syslog_facility);
357         }
358
359         if (identifier) {
360                 syslog_identifier = strappenda("SYSLOG_IDENTIFIER=", identifier);
361                 if (syslog_identifier)
362                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
363         }
364
365         if (pid) {
366                 syslog_pid = strappenda("SYSLOG_PID=", pid);
367                 if (syslog_pid)
368                         IOVEC_SET_STRING(iovec[n++], syslog_pid);
369         }
370
371         message = strappenda("MESSAGE=", buf);
372         if (message)
373                 IOVEC_SET_STRING(iovec[n++], message);
374
375         server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
376 }
377
378 int server_open_syslog_socket(Server *s) {
379         static const int one = 1;
380         int r;
381
382         assert(s);
383
384         if (s->syslog_fd < 0) {
385                 static const union sockaddr_union sa = {
386                         .un.sun_family = AF_UNIX,
387                         .un.sun_path = "/run/systemd/journal/dev-log",
388                 };
389
390                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
391                 if (s->syslog_fd < 0)
392                         return log_error_errno(errno, "socket() failed: %m");
393
394                 unlink(sa.un.sun_path);
395
396                 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
397                 if (r < 0)
398                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
399
400                 chmod(sa.un.sun_path, 0666);
401         } else
402                 fd_nonblock(s->syslog_fd, 1);
403
404         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
405         if (r < 0)
406                 return log_error_errno(errno, "SO_PASSCRED failed: %m");
407
408 #ifdef HAVE_SELINUX
409         if (mac_selinux_use()) {
410                 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
411                 if (r < 0)
412                         log_warning_errno(errno, "SO_PASSSEC failed: %m");
413         }
414 #endif
415
416         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
417         if (r < 0)
418                 return log_error_errno(errno, "SO_TIMESTAMP failed: %m");
419
420         r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s);
421         if (r < 0)
422                 return log_error_errno(r, "Failed to add syslog server fd to event loop: %m");
423
424         return 0;
425 }
426
427 void server_maybe_warn_forward_syslog_missed(Server *s) {
428         usec_t n;
429         assert(s);
430
431         if (s->n_forward_syslog_missed <= 0)
432                 return;
433
434         n = now(CLOCK_MONOTONIC);
435         if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
436                 return;
437
438         server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
439
440         s->n_forward_syslog_missed = 0;
441         s->last_warn_forward_syslog_missed = n;
442 }