1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
24 #include <sys/epoll.h>
26 #include "socket-util.h"
28 #include "journald-native.h"
29 #include "journald-kmsg.h"
30 #include "journald-console.h"
31 #include "journald-syslog.h"
33 #define ENTRY_SIZE_MAX (1024*1024*64)
34 #define DATA_SIZE_MAX (1024*1024*64)
36 static bool valid_user_field(const char *p, size_t l) {
39 /* We kinda enforce POSIX syntax recommendations for
40 environment variables here, but make a couple of additional
43 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
45 /* No empty field names */
49 /* Don't allow names longer than 64 chars */
53 /* Variables starting with an underscore are protected */
57 /* Don't allow digits as first character */
58 if (p[0] >= '0' && p[0] <= '9')
61 /* Only allow A-Z0-9 and '_' */
62 for (a = p; a < p + l; a++)
63 if (!((*a >= 'A' && *a <= 'Z') ||
64 (*a >= '0' && *a <= '9') ||
71 void server_process_native_message(
73 const void *buffer, size_t buffer_size,
76 const char *label, size_t label_len) {
78 struct iovec *iovec = NULL;
79 unsigned n = 0, m = 0, j, tn = (unsigned) -1;
82 int priority = LOG_INFO;
83 char *identifier = NULL, *message = NULL;
86 assert(buffer || buffer_size == 0);
89 remaining = buffer_size;
91 while (remaining > 0) {
94 e = memchr(p, '\n', remaining);
97 /* Trailing noise, let's ignore it, and flush what we collected */
98 log_debug("Received message with trailing noise, ignoring.");
103 /* Entry separator */
104 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
113 if (*p == '.' || *p == '#') {
114 /* Ignore control commands for now, and
116 remaining -= (e - p) + 1;
121 /* A property follows */
123 if (n+N_IOVEC_META_FIELDS >= m) {
127 u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
128 c = realloc(iovec, u * sizeof(struct iovec));
138 q = memchr(p, '=', e - p);
140 if (valid_user_field(p, q - p)) {
145 /* If the field name starts with an
146 * underscore, skip the variable,
147 * since that indidates a trusted
149 iovec[n].iov_base = (char*) p;
150 iovec[n].iov_len = l;
153 /* We need to determine the priority
154 * of this entry for the rate limiting
157 memcmp(p, "PRIORITY=", 9) == 0 &&
158 p[9] >= '0' && p[9] <= '9')
159 priority = (priority & LOG_FACMASK) | (p[9] - '0');
162 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
163 p[16] >= '0' && p[16] <= '9')
164 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
167 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
168 p[16] >= '0' && p[16] <= '9' &&
169 p[17] >= '0' && p[17] <= '9')
170 priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
173 memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
176 t = strndup(p + 18, l - 18);
182 memcmp(p, "MESSAGE=", 8) == 0) {
185 t = strndup(p + 8, l - 8);
193 remaining -= (e - p) + 1;
201 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
202 log_debug("Failed to parse message, ignoring.");
206 memcpy(&l_le, e + 1, sizeof(uint64_t));
209 if (l > DATA_SIZE_MAX) {
210 log_debug("Received binary data block too large, ignoring.");
214 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
215 e[1+sizeof(uint64_t)+l] != '\n') {
216 log_debug("Failed to parse message, ignoring.");
220 k = malloc((e - p) + 1 + l);
228 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
230 if (valid_user_field(p, e - p)) {
231 iovec[n].iov_base = k;
232 iovec[n].iov_len = (e - p) + 1 + l;
237 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
238 p = e + 1 + sizeof(uint64_t) + l + 1;
246 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
249 if (s->forward_to_syslog)
250 server_forward_syslog(s, priority, identifier, message, ucred, tv);
252 if (s->forward_to_kmsg)
253 server_forward_kmsg(s, priority, identifier, message, ucred);
255 if (s->forward_to_console)
256 server_forward_console(s, priority, identifier, message, ucred);
259 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
262 for (j = 0; j < n; j++) {
266 if (iovec[j].iov_base < buffer ||
267 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
268 free(iovec[j].iov_base);
276 void server_process_native_file(
281 const char *label, size_t label_len) {
290 /* Data is in the passed file, since it didn't fit in a
291 * datagram. We can't map the file here, since clients might
292 * then truncate it and trigger a SIGBUS for us. So let's
293 * stupidly read it */
295 if (fstat(fd, &st) < 0) {
296 log_error("Failed to stat passed file, ignoring: %m");
300 if (!S_ISREG(st.st_mode)) {
301 log_error("File passed is not regular. Ignoring.");
308 if (st.st_size > ENTRY_SIZE_MAX) {
309 log_error("File passed too large. Ignoring.");
313 p = malloc(st.st_size);
319 n = pread(fd, p, st.st_size, 0);
321 log_error("Failed to read file, ignoring: %s", strerror(-n));
323 server_process_native_message(s, p, n, ucred, tv, label, label_len);
328 int server_open_native_socket(Server*s) {
329 union sockaddr_union sa;
331 struct epoll_event ev;
335 if (s->native_fd < 0) {
337 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
338 if (s->native_fd < 0) {
339 log_error("socket() failed: %m");
344 sa.un.sun_family = AF_UNIX;
345 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
347 unlink(sa.un.sun_path);
349 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
351 log_error("bind() failed: %m");
355 chmod(sa.un.sun_path, 0666);
357 fd_nonblock(s->native_fd, 1);
360 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
362 log_error("SO_PASSCRED failed: %m");
368 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
370 log_warning("SO_PASSSEC failed: %m");
374 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
376 log_error("SO_TIMESTAMP failed: %m");
382 ev.data.fd = s->native_fd;
383 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
384 log_error("Failed to add native server fd to epoll object: %m");