chiark / gitweb /
sd-dhcp-client: Add reference counting for DHCP
[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, struct ucred *ucred, struct timeval *tv) {
39
40         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 = &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) {
89                 struct ucred u;
90
91                 /* Hmm, presumably the sender process vanished
92                  * by now, so let's fix it as good as we
93                  * 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("Failed to forward syslog message: %m");
110 }
111
112 static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, 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, struct ucred *ucred, 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), "[%lu]: ", (unsigned long) 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 void syslog_parse_priority(const char **p, int *priority, bool with_facility) {
242         int a = 0, b = 0, c = 0;
243         int k;
244
245         assert(p);
246         assert(*p);
247         assert(priority);
248
249         if ((*p)[0] != '<')
250                 return;
251
252         if (!strchr(*p, '>'))
253                 return;
254
255         if ((*p)[2] == '>') {
256                 c = undecchar((*p)[1]);
257                 k = 3;
258         } else if ((*p)[3] == '>') {
259                 b = undecchar((*p)[1]);
260                 c = undecchar((*p)[2]);
261                 k = 4;
262         } else if ((*p)[4] == '>') {
263                 a = undecchar((*p)[1]);
264                 b = undecchar((*p)[2]);
265                 c = undecchar((*p)[3]);
266                 k = 5;
267         } else
268                 return;
269
270         if (a < 0 || b < 0 || c < 0 ||
271             (!with_facility && (a || b || c > 7)))
272                 return;
273
274         if (with_facility)
275                 *priority = a*100 + b*10 + c;
276         else
277                 *priority = (*priority & LOG_FACMASK) | c;
278         *p += k;
279 }
280
281 static void syslog_skip_date(char **buf) {
282         enum {
283                 LETTER,
284                 SPACE,
285                 NUMBER,
286                 SPACE_OR_NUMBER,
287                 COLON
288         } sequence[] = {
289                 LETTER, LETTER, LETTER,
290                 SPACE,
291                 SPACE_OR_NUMBER, NUMBER,
292                 SPACE,
293                 SPACE_OR_NUMBER, NUMBER,
294                 COLON,
295                 SPACE_OR_NUMBER, NUMBER,
296                 COLON,
297                 SPACE_OR_NUMBER, NUMBER,
298                 SPACE
299         };
300
301         char *p;
302         unsigned i;
303
304         assert(buf);
305         assert(*buf);
306
307         p = *buf;
308
309         for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
310
311                 if (!*p)
312                         return;
313
314                 switch (sequence[i]) {
315
316                 case SPACE:
317                         if (*p != ' ')
318                                 return;
319                         break;
320
321                 case SPACE_OR_NUMBER:
322                         if (*p == ' ')
323                                 break;
324
325                         /* fall through */
326
327                 case NUMBER:
328                         if (*p < '0' || *p > '9')
329                                 return;
330
331                         break;
332
333                 case LETTER:
334                         if (!(*p >= 'A' && *p <= 'Z') &&
335                             !(*p >= 'a' && *p <= 'z'))
336                                 return;
337
338                         break;
339
340                 case COLON:
341                         if (*p != ':')
342                                 return;
343                         break;
344
345                 }
346         }
347
348         *buf = p;
349 }
350
351 void server_process_syslog_message(
352         Server *s,
353         const char *buf,
354         struct ucred *ucred,
355         struct timeval *tv,
356         const char *label,
357         size_t label_len) {
358
359         char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
360         struct iovec iovec[N_IOVEC_META_FIELDS + 6];
361         unsigned n = 0;
362         int priority = LOG_USER | LOG_INFO;
363         char *identifier = NULL, *pid = NULL;
364         const char *orig;
365
366         assert(s);
367         assert(buf);
368
369         orig = buf;
370         syslog_parse_priority(&buf, &priority, true);
371
372         if (s->forward_to_syslog)
373                 forward_syslog_raw(s, priority, orig, ucred, tv);
374
375         syslog_skip_date((char**) &buf);
376         syslog_parse_identifier(&buf, &identifier, &pid);
377
378         if (s->forward_to_kmsg)
379                 server_forward_kmsg(s, priority, identifier, buf, ucred);
380
381         if (s->forward_to_console)
382                 server_forward_console(s, priority, identifier, buf, ucred);
383
384         if (s->forward_to_wall)
385                 server_forward_wall(s, priority, identifier, buf, ucred);
386
387         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
388
389         if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
390                 IOVEC_SET_STRING(iovec[n++], syslog_priority);
391
392         if (priority & LOG_FACMASK)
393                 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
394                         IOVEC_SET_STRING(iovec[n++], syslog_facility);
395
396         if (identifier) {
397                 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
398                 if (syslog_identifier)
399                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
400         }
401
402         if (pid) {
403                 syslog_pid = strappend("SYSLOG_PID=", pid);
404                 if (syslog_pid)
405                         IOVEC_SET_STRING(iovec[n++], syslog_pid);
406         }
407
408         message = strappend("MESSAGE=", buf);
409         if (message)
410                 IOVEC_SET_STRING(iovec[n++], message);
411
412         server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
413
414         free(message);
415         free(identifier);
416         free(pid);
417         free(syslog_priority);
418         free(syslog_facility);
419         free(syslog_identifier);
420         free(syslog_pid);
421 }
422
423 int server_open_syslog_socket(Server *s) {
424         int one, r;
425
426         assert(s);
427
428         if (s->syslog_fd < 0) {
429                 union sockaddr_union sa = {
430                         .un.sun_family = AF_UNIX,
431                         .un.sun_path = "/dev/log",
432                 };
433
434                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
435                 if (s->syslog_fd < 0) {
436                         log_error("socket() failed: %m");
437                         return -errno;
438                 }
439
440                 unlink(sa.un.sun_path);
441
442                 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
443                 if (r < 0) {
444                         log_error("bind() failed: %m");
445                         return -errno;
446                 }
447
448                 chmod(sa.un.sun_path, 0666);
449         } else
450                 fd_nonblock(s->syslog_fd, 1);
451
452         one = 1;
453         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
454         if (r < 0) {
455                 log_error("SO_PASSCRED failed: %m");
456                 return -errno;
457         }
458
459 #ifdef HAVE_SELINUX
460         if (use_selinux()) {
461                 one = 1;
462                 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
463                 if (r < 0)
464                         log_warning("SO_PASSSEC failed: %m");
465         }
466 #endif
467
468         one = 1;
469         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
470         if (r < 0) {
471                 log_error("SO_TIMESTAMP failed: %m");
472                 return -errno;
473         }
474
475         r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, process_datagram, s);
476         if (r < 0) {
477                 log_error("Failed to add syslog server fd to event loop: %s", strerror(-r));
478                 return r;
479         }
480
481         return 0;
482 }
483
484 void server_maybe_warn_forward_syslog_missed(Server *s) {
485         usec_t n;
486         assert(s);
487
488         if (s->n_forward_syslog_missed <= 0)
489                 return;
490
491         n = now(CLOCK_MONOTONIC);
492         if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n)
493                 return;
494
495         server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, "Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed);
496
497         s->n_forward_syslog_missed = 0;
498         s->last_warn_forward_syslog_missed = n;
499 }