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/>.
23 #include <sys/epoll.h>
25 #include "socket-util.h"
27 #include "journald-native.h"
28 #include "journald-kmsg.h"
29 #include "journald-console.h"
30 #include "journald-syslog.h"
32 #define ENTRY_SIZE_MAX (1024*1024*32)
34 static bool valid_user_field(const char *p, size_t l) {
37 /* We kinda enforce POSIX syntax recommendations for
38 environment variables here, but make a couple of additional
41 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
43 /* No empty field names */
47 /* Don't allow names longer than 64 chars */
51 /* Variables starting with an underscore are protected */
55 /* Don't allow digits as first character */
56 if (p[0] >= '0' && p[0] <= '9')
59 /* Only allow A-Z0-9 and '_' */
60 for (a = p; a < p + l; a++)
61 if (!((*a >= 'A' && *a <= 'Z') ||
62 (*a >= '0' && *a <= '9') ||
69 void server_process_native_message(
71 const void *buffer, size_t buffer_size,
74 const char *label, size_t label_len) {
76 struct iovec *iovec = NULL;
77 unsigned n = 0, m = 0, j, tn = (unsigned) -1;
80 int priority = LOG_INFO;
81 char *identifier = NULL, *message = NULL;
84 assert(buffer || buffer_size == 0);
87 remaining = buffer_size;
89 while (remaining > 0) {
92 e = memchr(p, '\n', remaining);
95 /* Trailing noise, let's ignore it, and flush what we collected */
96 log_debug("Received message with trailing noise, ignoring.");
101 /* Entry separator */
102 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
111 if (*p == '.' || *p == '#') {
112 /* Ignore control commands for now, and
114 remaining -= (e - p) + 1;
119 /* A property follows */
121 if (n+N_IOVEC_META_FIELDS >= m) {
125 u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
126 c = realloc(iovec, u * sizeof(struct iovec));
136 q = memchr(p, '=', e - p);
138 if (valid_user_field(p, q - p)) {
143 /* If the field name starts with an
144 * underscore, skip the variable,
145 * since that indidates a trusted
147 iovec[n].iov_base = (char*) p;
148 iovec[n].iov_len = l;
151 /* We need to determine the priority
152 * of this entry for the rate limiting
155 memcmp(p, "PRIORITY=", 9) == 0 &&
156 p[9] >= '0' && p[9] <= '9')
157 priority = (priority & LOG_FACMASK) | (p[9] - '0');
160 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
161 p[16] >= '0' && p[16] <= '9')
162 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
165 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
166 p[16] >= '0' && p[16] <= '9' &&
167 p[17] >= '0' && p[17] <= '9')
168 priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
171 memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
174 t = strndup(p + 18, l - 18);
180 memcmp(p, "MESSAGE=", 8) == 0) {
183 t = strndup(p + 8, l - 8);
191 remaining -= (e - p) + 1;
199 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
200 log_debug("Failed to parse message, ignoring.");
204 memcpy(&l_le, e + 1, sizeof(uint64_t));
207 if (remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
208 e[1+sizeof(uint64_t)+l] != '\n') {
209 log_debug("Failed to parse message, ignoring.");
213 k = malloc((e - p) + 1 + l);
221 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
223 if (valid_user_field(p, e - p)) {
224 iovec[n].iov_base = k;
225 iovec[n].iov_len = (e - p) + 1 + l;
230 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
231 p = e + 1 + sizeof(uint64_t) + l + 1;
239 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
242 if (s->forward_to_syslog)
243 server_forward_syslog(s, priority, identifier, message, ucred, tv);
245 if (s->forward_to_kmsg)
246 server_forward_kmsg(s, priority, identifier, message, ucred);
248 if (s->forward_to_console)
249 server_forward_console(s, priority, identifier, message, ucred);
252 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
255 for (j = 0; j < n; j++) {
259 if (iovec[j].iov_base < buffer ||
260 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
261 free(iovec[j].iov_base);
269 void server_process_native_file(
274 const char *label, size_t label_len) {
283 /* Data is in the passed file, since it didn't fit in a
284 * datagram. We can't map the file here, since clients might
285 * then truncate it and trigger a SIGBUS for us. So let's
286 * stupidly read it */
288 if (fstat(fd, &st) < 0) {
289 log_error("Failed to stat passed file, ignoring: %m");
293 if (!S_ISREG(st.st_mode)) {
294 log_error("File passed is not regular. Ignoring.");
301 if (st.st_size > ENTRY_SIZE_MAX) {
302 log_error("File passed too large. Ignoring.");
306 p = malloc(st.st_size);
312 n = pread(fd, p, st.st_size, 0);
314 log_error("Failed to read file, ignoring: %s", strerror(-n));
316 server_process_native_message(s, p, n, ucred, tv, label, label_len);
321 int server_open_native_socket(Server*s) {
322 union sockaddr_union sa;
324 struct epoll_event ev;
328 if (s->native_fd < 0) {
330 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
331 if (s->native_fd < 0) {
332 log_error("socket() failed: %m");
337 sa.un.sun_family = AF_UNIX;
338 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
340 unlink(sa.un.sun_path);
342 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
344 log_error("bind() failed: %m");
348 chmod(sa.un.sun_path, 0666);
350 fd_nonblock(s->native_fd, 1);
353 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
355 log_error("SO_PASSCRED failed: %m");
361 r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
363 log_warning("SO_PASSSEC failed: %m");
367 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
369 log_error("SO_TIMESTAMP failed: %m");
375 ev.data.fd = s->native_fd;
376 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
377 log_error("Failed to add native server fd to epoll object: %m");