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