chiark / gitweb /
journald: add missing includes
[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*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 }