chiark / gitweb /
91664762e64a2706ead10ffdbb9343fc329bd286
[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 <sys/epoll.h>
24
25 #include "socket-util.h"
26 #include "journald.h"
27 #include "journald-syslog.h"
28 #include "journald-kmsg.h"
29
30 static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) {
31         struct msghdr msghdr;
32         struct cmsghdr *cmsg;
33         union {
34                 struct cmsghdr cmsghdr;
35                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
36         } control;
37         union sockaddr_union sa;
38
39         assert(s);
40         assert(iovec);
41         assert(n_iovec > 0);
42
43         zero(msghdr);
44         msghdr.msg_iov = (struct iovec*) iovec;
45         msghdr.msg_iovlen = n_iovec;
46
47         zero(sa);
48         sa.un.sun_family = AF_UNIX;
49         strncpy(sa.un.sun_path, "/run/systemd/journal/syslog", sizeof(sa.un.sun_path));
50         msghdr.msg_name = &sa;
51         msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
52
53         if (ucred) {
54                 zero(control);
55                 msghdr.msg_control = &control;
56                 msghdr.msg_controllen = sizeof(control);
57
58                 cmsg = CMSG_FIRSTHDR(&msghdr);
59                 cmsg->cmsg_level = SOL_SOCKET;
60                 cmsg->cmsg_type = SCM_CREDENTIALS;
61                 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
62                 memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
63                 msghdr.msg_controllen = cmsg->cmsg_len;
64         }
65
66         /* Forward the syslog message we received via /dev/log to
67          * /run/systemd/syslog. Unfortunately we currently can't set
68          * the SO_TIMESTAMP auxiliary data, and hence we don't. */
69
70         if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
71                 return;
72
73         /* The socket is full? I guess the syslog implementation is
74          * too slow, and we shouldn't wait for that... */
75         if (errno == EAGAIN)
76                 return;
77
78         if (ucred && errno == ESRCH) {
79                 struct ucred u;
80
81                 /* Hmm, presumably the sender process vanished
82                  * by now, so let's fix it as good as we
83                  * can, and retry */
84
85                 u = *ucred;
86                 u.pid = getpid();
87                 memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
88
89                 if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
90                         return;
91
92                 if (errno == EAGAIN)
93                         return;
94         }
95
96         if (errno != ENOENT)
97                 log_debug("Failed to forward syslog message: %m");
98 }
99
100 static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, struct timeval *tv) {
101         struct iovec iovec;
102
103         assert(s);
104         assert(buffer);
105
106         if (LOG_PRI(priority) > s->max_level_syslog)
107                 return;
108
109         IOVEC_SET_STRING(iovec, buffer);
110         forward_syslog_iovec(s, &iovec, 1, ucred, tv);
111 }
112
113 void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) {
114         struct iovec iovec[5];
115         char header_priority[6], header_time[64], header_pid[16];
116         int n = 0;
117         time_t t;
118         struct tm *tm;
119         char *ident_buf = NULL;
120
121         assert(s);
122         assert(priority >= 0);
123         assert(priority <= 999);
124         assert(message);
125
126         if (LOG_PRI(priority) > s->max_level_syslog)
127                 return;
128
129         /* First: priority field */
130         snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
131         char_array_0(header_priority);
132         IOVEC_SET_STRING(iovec[n++], header_priority);
133
134         /* Second: timestamp */
135         t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
136         tm = localtime(&t);
137         if (!tm)
138                 return;
139         if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
140                 return;
141         IOVEC_SET_STRING(iovec[n++], header_time);
142
143         /* Third: identifier and PID */
144         if (ucred) {
145                 if (!identifier) {
146                         get_process_comm(ucred->pid, &ident_buf);
147                         identifier = ident_buf;
148                 }
149
150                 snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
151                 char_array_0(header_pid);
152
153                 if (identifier)
154                         IOVEC_SET_STRING(iovec[n++], identifier);
155
156                 IOVEC_SET_STRING(iovec[n++], header_pid);
157         } else if (identifier) {
158                 IOVEC_SET_STRING(iovec[n++], identifier);
159                 IOVEC_SET_STRING(iovec[n++], ": ");
160         }
161
162         /* Fourth: message */
163         IOVEC_SET_STRING(iovec[n++], message);
164
165         forward_syslog_iovec(s, iovec, n, ucred, tv);
166
167         free(ident_buf);
168 }
169
170 int syslog_fixup_facility(int priority) {
171
172         if ((priority & LOG_FACMASK) == 0)
173                 return (priority & LOG_PRIMASK) | LOG_USER;
174
175         return priority;
176 }
177
178 void syslog_read_identifier(const char **buf, char **identifier, char **pid) {
179         const char *p;
180         char *t;
181         size_t l, e;
182
183         assert(buf);
184         assert(identifier);
185         assert(pid);
186
187         p = *buf;
188
189         p += strspn(p, WHITESPACE);
190         l = strcspn(p, WHITESPACE);
191
192         if (l <= 0 ||
193             p[l-1] != ':')
194                 return;
195
196         e = l;
197         l--;
198
199         if (p[l-1] == ']') {
200                 size_t k = l-1;
201
202                 for (;;) {
203
204                         if (p[k] == '[') {
205                                 t = strndup(p+k+1, l-k-2);
206                                 if (t)
207                                         *pid = t;
208
209                                 l = k;
210                                 break;
211                         }
212
213                         if (k == 0)
214                                 break;
215
216                         k--;
217                 }
218         }
219
220         t = strndup(p, l);
221         if (t)
222                 *identifier = t;
223
224         *buf = p + e;
225         *buf += strspn(*buf, WHITESPACE);
226 }
227
228 void server_process_syslog_message(
229         Server *s,
230         const char *buf,
231         struct ucred *ucred,
232         struct timeval *tv,
233         const char *label,
234         size_t label_len) {
235
236         char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
237         struct iovec iovec[N_IOVEC_META_FIELDS + 6];
238         unsigned n = 0;
239         int priority = LOG_USER | LOG_INFO;
240         char *identifier = NULL, *pid = NULL;
241         const char *orig;
242
243         assert(s);
244         assert(buf);
245
246         orig = buf;
247         syslog_parse_priority((char**) &buf, &priority);
248
249         if (s->forward_to_syslog)
250                 forward_syslog_raw(s, priority, orig, ucred, tv);
251
252         syslog_skip_date((char**) &buf);
253         syslog_read_identifier(&buf, &identifier, &pid);
254
255         if (s->forward_to_kmsg)
256                 server_forward_kmsg(s, priority, identifier, buf, ucred);
257
258         if (s->forward_to_console)
259                 server_forward_console(s, priority, identifier, buf, ucred);
260
261         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
262
263         if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
264                 IOVEC_SET_STRING(iovec[n++], syslog_priority);
265
266         if (priority & LOG_FACMASK)
267                 if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
268                         IOVEC_SET_STRING(iovec[n++], syslog_facility);
269
270         if (identifier) {
271                 syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
272                 if (syslog_identifier)
273                         IOVEC_SET_STRING(iovec[n++], syslog_identifier);
274         }
275
276         if (pid) {
277                 syslog_pid = strappend("SYSLOG_PID=", pid);
278                 if (syslog_pid)
279                         IOVEC_SET_STRING(iovec[n++], syslog_pid);
280         }
281
282         message = strappend("MESSAGE=", buf);
283         if (message)
284                 IOVEC_SET_STRING(iovec[n++], message);
285
286         server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority);
287
288         free(message);
289         free(identifier);
290         free(pid);
291         free(syslog_priority);
292         free(syslog_facility);
293         free(syslog_identifier);
294         free(syslog_pid);
295 }
296
297 int server_open_syslog_socket(Server *s) {
298         union sockaddr_union sa;
299         int one, r;
300         struct epoll_event ev;
301
302         assert(s);
303
304         if (s->syslog_fd < 0) {
305
306                 s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
307                 if (s->syslog_fd < 0) {
308                         log_error("socket() failed: %m");
309                         return -errno;
310                 }
311
312                 zero(sa);
313                 sa.un.sun_family = AF_UNIX;
314                 strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
315
316                 unlink(sa.un.sun_path);
317
318                 r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
319                 if (r < 0) {
320                         log_error("bind() failed: %m");
321                         return -errno;
322                 }
323
324                 chmod(sa.un.sun_path, 0666);
325         } else
326                 fd_nonblock(s->syslog_fd, 1);
327
328         one = 1;
329         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
330         if (r < 0) {
331                 log_error("SO_PASSCRED failed: %m");
332                 return -errno;
333         }
334
335 #ifdef HAVE_SELINUX
336         one = 1;
337         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
338         if (r < 0)
339                 log_warning("SO_PASSSEC failed: %m");
340 #endif
341
342         one = 1;
343         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
344         if (r < 0) {
345                 log_error("SO_TIMESTAMP failed: %m");
346                 return -errno;
347         }
348
349         zero(ev);
350         ev.events = EPOLLIN;
351         ev.data.fd = s->syslog_fd;
352         if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) {
353                 log_error("Failed to add syslog server fd to epoll object: %m");
354                 return -errno;
355         }
356
357         return 0;
358 }