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*32)
35 static bool valid_user_field(const char *p, size_t l) {
38 /* We kinda enforce POSIX syntax recommendations for
39 environment variables here, but make a couple of additional
42 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
44 /* No empty field names */
48 /* Don't allow names longer than 64 chars */
52 /* Variables starting with an underscore are protected */
56 /* Don't allow digits as first character */
57 if (p[0] >= '0' && p[0] <= '9')
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') ||
70 void server_process_native_message(
72 const void *buffer, size_t buffer_size,
75 const char *label, size_t label_len) {
77 struct iovec *iovec = NULL;
78 unsigned n = 0, m = 0, j, tn = (unsigned) -1;
81 int priority = LOG_INFO;
82 char *identifier = NULL, *message = NULL;
85 assert(buffer || buffer_size == 0);
88 remaining = buffer_size;
90 while (remaining > 0) {
93 e = memchr(p, '\n', remaining);
96 /* Trailing noise, let's ignore it, and flush what we collected */
97 log_debug("Received message with trailing noise, ignoring.");
102 /* Entry separator */
103 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
112 if (*p == '.' || *p == '#') {
113 /* Ignore control commands for now, and
115 remaining -= (e - p) + 1;
120 /* A property follows */
122 if (n+N_IOVEC_META_FIELDS >= m) {
126 u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
127 c = realloc(iovec, u * sizeof(struct iovec));
137 q = memchr(p, '=', e - p);
139 if (valid_user_field(p, q - p)) {
144 /* If the field name starts with an
145 * underscore, skip the variable,
146 * since that indidates a trusted
148 iovec[n].iov_base = (char*) p;
149 iovec[n].iov_len = l;
152 /* We need to determine the priority
153 * of this entry for the rate limiting
156 memcmp(p, "PRIORITY=", 9) == 0 &&
157 p[9] >= '0' && p[9] <= '9')
158 priority = (priority & LOG_FACMASK) | (p[9] - '0');
161 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
162 p[16] >= '0' && p[16] <= '9')
163 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
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);
172 memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
175 t = strndup(p + 18, l - 18);
181 memcmp(p, "MESSAGE=", 8) == 0) {
184 t = strndup(p + 8, l - 8);
192 remaining -= (e - p) + 1;
200 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
201 log_debug("Failed to parse message, ignoring.");
205 memcpy(&l_le, e + 1, sizeof(uint64_t));
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.");
214 k = malloc((e - p) + 1 + l);
222 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
224 if (valid_user_field(p, e - p)) {
225 iovec[n].iov_base = k;
226 iovec[n].iov_len = (e - p) + 1 + l;
231 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
232 p = e + 1 + sizeof(uint64_t) + l + 1;
240 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
243 if (s->forward_to_syslog)
244 server_forward_syslog(s, priority, identifier, message, ucred, tv);
246 if (s->forward_to_kmsg)
247 server_forward_kmsg(s, priority, identifier, message, ucred);
249 if (s->forward_to_console)
250 server_forward_console(s, priority, identifier, message, ucred);
253 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
256 for (j = 0; j < n; j++) {
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);
270 void server_process_native_file(
275 const char *label, size_t label_len) {
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 */
289 if (fstat(fd, &st) < 0) {
290 log_error("Failed to stat passed file, ignoring: %m");
294 if (!S_ISREG(st.st_mode)) {
295 log_error("File passed is not regular. Ignoring.");
302 if (st.st_size > ENTRY_SIZE_MAX) {
303 log_error("File passed too large. Ignoring.");
307 p = malloc(st.st_size);
313 n = pread(fd, p, st.st_size, 0);
315 log_error("Failed to read file, ignoring: %s", strerror(-n));
317 server_process_native_message(s, p, n, ucred, tv, label, label_len);
322 int server_open_native_socket(Server*s) {
323 union sockaddr_union sa;
325 struct epoll_event ev;
329 if (s->native_fd < 0) {
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");
338 sa.un.sun_family = AF_UNIX;
339 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
341 unlink(sa.un.sun_path);
343 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
345 log_error("bind() failed: %m");
349 chmod(sa.un.sun_path, 0666);
351 fd_nonblock(s->native_fd, 1);
354 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
356 log_error("SO_PASSCRED failed: %m");
362 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
364 log_warning("SO_PASSSEC failed: %m");
368 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
370 log_error("SO_TIMESTAMP failed: %m");
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");