]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-syslog.c
journald: splitt of syslog protocol support into its own file
[thirdparty/systemd.git] / src / journal / journald-syslog.c
CommitLineData
35e2e347
LP
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
30static 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
100static 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
113void 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
170int 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
178void 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
228void 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
297int 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}