]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
journald: add missing includes
[thirdparty/systemd.git] / src / journal / journald-native.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 "socket-util.h"
27 #include "journald.h"
28 #include "journald-native.h"
29 #include "journald-kmsg.h"
30 #include "journald-console.h"
31 #include "journald-syslog.h"
32
33 #define ENTRY_SIZE_MAX (1024*1024*32)
34
35 static bool valid_user_field(const char *p, size_t l) {
36 const char *a;
37
38 /* We kinda enforce POSIX syntax recommendations for
39 environment variables here, but make a couple of additional
40 requirements.
41
42 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
43
44 /* No empty field names */
45 if (l <= 0)
46 return false;
47
48 /* Don't allow names longer than 64 chars */
49 if (l > 64)
50 return false;
51
52 /* Variables starting with an underscore are protected */
53 if (p[0] == '_')
54 return false;
55
56 /* Don't allow digits as first character */
57 if (p[0] >= '0' && p[0] <= '9')
58 return false;
59
60 /* Only allow A-Z0-9 and '_' */
61 for (a = p; a < p + l; a++)
62 if (!((*a >= 'A' && *a <= 'Z') ||
63 (*a >= '0' && *a <= '9') ||
64 *a == '_'))
65 return false;
66
67 return true;
68 }
69
70 void server_process_native_message(
71 Server *s,
72 const void *buffer, size_t buffer_size,
73 struct ucred *ucred,
74 struct timeval *tv,
75 const char *label, size_t label_len) {
76
77 struct iovec *iovec = NULL;
78 unsigned n = 0, m = 0, j, tn = (unsigned) -1;
79 const char *p;
80 size_t remaining;
81 int priority = LOG_INFO;
82 char *identifier = NULL, *message = NULL;
83
84 assert(s);
85 assert(buffer || buffer_size == 0);
86
87 p = buffer;
88 remaining = buffer_size;
89
90 while (remaining > 0) {
91 const char *e, *q;
92
93 e = memchr(p, '\n', remaining);
94
95 if (!e) {
96 /* Trailing noise, let's ignore it, and flush what we collected */
97 log_debug("Received message with trailing noise, ignoring.");
98 break;
99 }
100
101 if (e == p) {
102 /* Entry separator */
103 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
104 n = 0;
105 priority = LOG_INFO;
106
107 p++;
108 remaining--;
109 continue;
110 }
111
112 if (*p == '.' || *p == '#') {
113 /* Ignore control commands for now, and
114 * comments too. */
115 remaining -= (e - p) + 1;
116 p = e + 1;
117 continue;
118 }
119
120 /* A property follows */
121
122 if (n+N_IOVEC_META_FIELDS >= m) {
123 struct iovec *c;
124 unsigned u;
125
126 u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
127 c = realloc(iovec, u * sizeof(struct iovec));
128 if (!c) {
129 log_oom();
130 break;
131 }
132
133 iovec = c;
134 m = u;
135 }
136
137 q = memchr(p, '=', e - p);
138 if (q) {
139 if (valid_user_field(p, q - p)) {
140 size_t l;
141
142 l = e - p;
143
144 /* If the field name starts with an
145 * underscore, skip the variable,
146 * since that indidates a trusted
147 * field */
148 iovec[n].iov_base = (char*) p;
149 iovec[n].iov_len = l;
150 n++;
151
152 /* We need to determine the priority
153 * of this entry for the rate limiting
154 * logic */
155 if (l == 10 &&
156 memcmp(p, "PRIORITY=", 9) == 0 &&
157 p[9] >= '0' && p[9] <= '9')
158 priority = (priority & LOG_FACMASK) | (p[9] - '0');
159
160 else if (l == 17 &&
161 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
162 p[16] >= '0' && p[16] <= '9')
163 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
164
165 else if (l == 18 &&
166 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
167 p[16] >= '0' && p[16] <= '9' &&
168 p[17] >= '0' && p[17] <= '9')
169 priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
170
171 else if (l >= 19 &&
172 memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
173 char *t;
174
175 t = strndup(p + 18, l - 18);
176 if (t) {
177 free(identifier);
178 identifier = t;
179 }
180 } else if (l >= 8 &&
181 memcmp(p, "MESSAGE=", 8) == 0) {
182 char *t;
183
184 t = strndup(p + 8, l - 8);
185 if (t) {
186 free(message);
187 message = t;
188 }
189 }
190 }
191
192 remaining -= (e - p) + 1;
193 p = e + 1;
194 continue;
195 } else {
196 le64_t l_le;
197 uint64_t l;
198 char *k;
199
200 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
201 log_debug("Failed to parse message, ignoring.");
202 break;
203 }
204
205 memcpy(&l_le, e + 1, sizeof(uint64_t));
206 l = le64toh(l_le);
207
208 if (remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
209 e[1+sizeof(uint64_t)+l] != '\n') {
210 log_debug("Failed to parse message, ignoring.");
211 break;
212 }
213
214 k = malloc((e - p) + 1 + l);
215 if (!k) {
216 log_oom();
217 break;
218 }
219
220 memcpy(k, p, e - p);
221 k[e - p] = '=';
222 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
223
224 if (valid_user_field(p, e - p)) {
225 iovec[n].iov_base = k;
226 iovec[n].iov_len = (e - p) + 1 + l;
227 n++;
228 } else
229 free(k);
230
231 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
232 p = e + 1 + sizeof(uint64_t) + l + 1;
233 }
234 }
235
236 if (n <= 0)
237 goto finish;
238
239 tn = n++;
240 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
241
242 if (message) {
243 if (s->forward_to_syslog)
244 server_forward_syslog(s, priority, identifier, message, ucred, tv);
245
246 if (s->forward_to_kmsg)
247 server_forward_kmsg(s, priority, identifier, message, ucred);
248
249 if (s->forward_to_console)
250 server_forward_console(s, priority, identifier, message, ucred);
251 }
252
253 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
254
255 finish:
256 for (j = 0; j < n; j++) {
257 if (j == tn)
258 continue;
259
260 if (iovec[j].iov_base < buffer ||
261 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
262 free(iovec[j].iov_base);
263 }
264
265 free(iovec);
266 free(identifier);
267 free(message);
268 }
269
270 void server_process_native_file(
271 Server *s,
272 int fd,
273 struct ucred *ucred,
274 struct timeval *tv,
275 const char *label, size_t label_len) {
276
277 struct stat st;
278 void *p;
279 ssize_t n;
280
281 assert(s);
282 assert(fd >= 0);
283
284 /* Data is in the passed file, since it didn't fit in a
285 * datagram. We can't map the file here, since clients might
286 * then truncate it and trigger a SIGBUS for us. So let's
287 * stupidly read it */
288
289 if (fstat(fd, &st) < 0) {
290 log_error("Failed to stat passed file, ignoring: %m");
291 return;
292 }
293
294 if (!S_ISREG(st.st_mode)) {
295 log_error("File passed is not regular. Ignoring.");
296 return;
297 }
298
299 if (st.st_size <= 0)
300 return;
301
302 if (st.st_size > ENTRY_SIZE_MAX) {
303 log_error("File passed too large. Ignoring.");
304 return;
305 }
306
307 p = malloc(st.st_size);
308 if (!p) {
309 log_oom();
310 return;
311 }
312
313 n = pread(fd, p, st.st_size, 0);
314 if (n < 0)
315 log_error("Failed to read file, ignoring: %s", strerror(-n));
316 else if (n > 0)
317 server_process_native_message(s, p, n, ucred, tv, label, label_len);
318
319 free(p);
320 }
321
322 int server_open_native_socket(Server*s) {
323 union sockaddr_union sa;
324 int one, r;
325 struct epoll_event ev;
326
327 assert(s);
328
329 if (s->native_fd < 0) {
330
331 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
332 if (s->native_fd < 0) {
333 log_error("socket() failed: %m");
334 return -errno;
335 }
336
337 zero(sa);
338 sa.un.sun_family = AF_UNIX;
339 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
340
341 unlink(sa.un.sun_path);
342
343 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
344 if (r < 0) {
345 log_error("bind() failed: %m");
346 return -errno;
347 }
348
349 chmod(sa.un.sun_path, 0666);
350 } else
351 fd_nonblock(s->native_fd, 1);
352
353 one = 1;
354 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
355 if (r < 0) {
356 log_error("SO_PASSCRED failed: %m");
357 return -errno;
358 }
359
360 #ifdef HAVE_SELINUX
361 one = 1;
362 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
363 if (r < 0)
364 log_warning("SO_PASSSEC failed: %m");
365 #endif
366
367 one = 1;
368 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
369 if (r < 0) {
370 log_error("SO_TIMESTAMP failed: %m");
371 return -errno;
372 }
373
374 zero(ev);
375 ev.events = EPOLLIN;
376 ev.data.fd = s->native_fd;
377 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
378 log_error("Failed to add native server fd to epoll object: %m");
379 return -errno;
380 }
381
382 return 0;
383 }