]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journal-send.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / journal / journal-send.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 <sys/socket.h>
23 #include <sys/un.h>
24 #include <errno.h>
25 #include <stddef.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28
29 #define SD_JOURNAL_SUPPRESS_LOCATION
30
31 #include "sd-journal.h"
32 #include "util.h"
33 #include "socket-util.h"
34
35 #define SNDBUF_SIZE (8*1024*1024)
36
37 /* We open a single fd, and we'll share it with the current process,
38 * all its threads, and all its subprocesses. This means we need to
39 * initialize it atomically, and need to operate on it atomically
40 * never assuming we are the only user */
41
42 static int journal_fd(void) {
43 int fd;
44 static int fd_plus_one = 0;
45
46 retry:
47 if (fd_plus_one > 0)
48 return fd_plus_one - 1;
49
50 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
51 if (fd < 0)
52 return -errno;
53
54 fd_inc_sndbuf(fd, SNDBUF_SIZE);
55
56 if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
57 close_nointr_nofail(fd);
58 goto retry;
59 }
60
61 return fd;
62 }
63
64 _public_ int sd_journal_print(int priority, const char *format, ...) {
65 int r;
66 va_list ap;
67
68 va_start(ap, format);
69 r = sd_journal_printv(priority, format, ap);
70 va_end(ap);
71
72 return r;
73 }
74
75 _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
76 char buffer[8 + LINE_MAX], p[11];
77 struct iovec iov[2];
78
79 if (priority < 0 || priority > 7)
80 return -EINVAL;
81
82 if (!format)
83 return -EINVAL;
84
85 snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK);
86 char_array_0(p);
87
88 memcpy(buffer, "MESSAGE=", 8);
89 vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
90 char_array_0(buffer);
91
92 zero(iov);
93 IOVEC_SET_STRING(iov[0], buffer);
94 IOVEC_SET_STRING(iov[1], p);
95
96 return sd_journal_sendv(iov, 2);
97 }
98
99 static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
100 int r, n = 0, i, j;
101 struct iovec *iov = NULL;
102 int saved_errno;
103
104 assert(_iov);
105 saved_errno = errno;
106
107 if (extra > 0) {
108 n = MAX(extra * 2, extra + 4);
109 iov = malloc0(n * sizeof(struct iovec));
110 if (!iov) {
111 r = -ENOMEM;
112 goto fail;
113 }
114
115 i = extra;
116 } else
117 i = 0;
118
119 while (format) {
120 struct iovec *c;
121 char *buffer;
122
123 if (i >= n) {
124 n = MAX(i*2, 4);
125 c = realloc(iov, n * sizeof(struct iovec));
126 if (!c) {
127 r = -ENOMEM;
128 goto fail;
129 }
130
131 iov = c;
132 }
133
134 if (vasprintf(&buffer, format, ap) < 0) {
135 r = -ENOMEM;
136 goto fail;
137 }
138
139 IOVEC_SET_STRING(iov[i++], buffer);
140
141 format = va_arg(ap, char *);
142 }
143
144 *_iov = iov;
145
146 errno = saved_errno;
147 return i;
148
149 fail:
150 for (j = 0; j < i; j++)
151 free(iov[j].iov_base);
152
153 free(iov);
154
155 errno = saved_errno;
156 return r;
157 }
158
159 _public_ int sd_journal_send(const char *format, ...) {
160 int r, i, j;
161 va_list ap;
162 struct iovec *iov = NULL;
163
164 va_start(ap, format);
165 i = fill_iovec_sprintf(format, ap, 0, &iov);
166 va_end(ap);
167
168 if (_unlikely_(i < 0)) {
169 r = i;
170 goto finish;
171 }
172
173 r = sd_journal_sendv(iov, i);
174
175 finish:
176 for (j = 0; j < i; j++)
177 free(iov[j].iov_base);
178
179 free(iov);
180
181 return r;
182 }
183
184 _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
185 int fd, buffer_fd;
186 struct iovec *w;
187 uint64_t *l;
188 int r, i, j = 0;
189 struct msghdr mh;
190 struct sockaddr_un sa;
191 ssize_t k;
192 int saved_errno;
193 union {
194 struct cmsghdr cmsghdr;
195 uint8_t buf[CMSG_SPACE(sizeof(int))];
196 } control;
197 struct cmsghdr *cmsg;
198 /* We use /dev/shm instead of /tmp here, since we want this to
199 * be a tmpfs, and one that is available from early boot on
200 * and where unprivileged users can create files. */
201 char path[] = "/dev/shm/journal.XXXXXX";
202
203 if (_unlikely_(!iov))
204 return -EINVAL;
205
206 if (_unlikely_(n <= 0))
207 return -EINVAL;
208
209 saved_errno = errno;
210
211 w = alloca(sizeof(struct iovec) * n * 5);
212 l = alloca(sizeof(uint64_t) * n);
213
214 for (i = 0; i < n; i++) {
215 char *c, *nl;
216
217 if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) {
218 r = -EINVAL;
219 goto finish;
220 }
221
222 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
223 if (_unlikely_(!c || c == iov[i].iov_base)) {
224 r = -EINVAL;
225 goto finish;
226 }
227
228 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
229 if (nl) {
230 if (_unlikely_(nl < c)) {
231 r = -EINVAL;
232 goto finish;
233 }
234
235 /* Already includes a newline? Bummer, then
236 * let's write the variable name, then a
237 * newline, then the size (64bit LE), followed
238 * by the data and a final newline */
239
240 w[j].iov_base = iov[i].iov_base;
241 w[j].iov_len = c - (char*) iov[i].iov_base;
242 j++;
243
244 IOVEC_SET_STRING(w[j++], "\n");
245
246 l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
247 w[j].iov_base = &l[i];
248 w[j].iov_len = sizeof(uint64_t);
249 j++;
250
251 w[j].iov_base = c + 1;
252 w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1;
253 j++;
254
255 } else
256 /* Nothing special? Then just add the line and
257 * append a newline */
258 w[j++] = iov[i];
259
260 IOVEC_SET_STRING(w[j++], "\n");
261 }
262
263 fd = journal_fd();
264 if (_unlikely_(fd < 0)) {
265 r = fd;
266 goto finish;
267 }
268
269 zero(sa);
270 sa.sun_family = AF_UNIX;
271 strncpy(sa.sun_path, "/run/systemd/journal/socket", sizeof(sa.sun_path));
272
273 zero(mh);
274 mh.msg_name = &sa;
275 mh.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path);
276 mh.msg_iov = w;
277 mh.msg_iovlen = j;
278
279 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
280 if (k >= 0) {
281 r = 0;
282 goto finish;
283 }
284
285 if (errno != EMSGSIZE && errno != ENOBUFS) {
286 r = -errno;
287 goto finish;
288 }
289
290 /* Message doesn't fit... Let's dump the data in a temporary
291 * file and just pass a file descriptor of it to the other
292 * side */
293
294 buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR);
295 if (buffer_fd < 0) {
296 r = -errno;
297 goto finish;
298 }
299
300 if (unlink(path) < 0) {
301 close_nointr_nofail(buffer_fd);
302 r = -errno;
303 goto finish;
304 }
305
306 n = writev(buffer_fd, w, j);
307 if (n < 0) {
308 close_nointr_nofail(buffer_fd);
309 r = -errno;
310 goto finish;
311 }
312
313 mh.msg_iov = NULL;
314 mh.msg_iovlen = 0;
315
316 zero(control);
317 mh.msg_control = &control;
318 mh.msg_controllen = sizeof(control);
319
320 cmsg = CMSG_FIRSTHDR(&mh);
321 cmsg->cmsg_level = SOL_SOCKET;
322 cmsg->cmsg_type = SCM_RIGHTS;
323 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
324 memcpy(CMSG_DATA(cmsg), &buffer_fd, sizeof(int));
325
326 mh.msg_controllen = cmsg->cmsg_len;
327
328 k = sendmsg(fd, &mh, MSG_NOSIGNAL);
329 close_nointr_nofail(buffer_fd);
330
331 if (k < 0) {
332 r = -errno;
333 goto finish;
334 }
335
336 r = 0;
337
338 finish:
339 errno = saved_errno;
340
341 return r;
342 }
343
344 _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
345 union sockaddr_union sa;
346 int fd;
347 char *header;
348 size_t l;
349 ssize_t r;
350
351 if (priority < 0 || priority > 7)
352 return -EINVAL;
353
354 fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
355 if (fd < 0)
356 return -errno;
357
358 zero(sa);
359 sa.un.sun_family = AF_UNIX;
360 strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
361
362 r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
363 if (r < 0) {
364 close_nointr_nofail(fd);
365 return -errno;
366 }
367
368 if (shutdown(fd, SHUT_RD) < 0) {
369 close_nointr_nofail(fd);
370 return -errno;
371 }
372
373 fd_inc_sndbuf(fd, SNDBUF_SIZE);
374
375 if (!identifier)
376 identifier = "";
377
378 l = strlen(identifier);
379 header = alloca(l + 1 + 2 + 2 + 2 + 2 + 2);
380
381 memcpy(header, identifier, l);
382 header[l++] = '\n';
383 header[l++] = '0' + priority;
384 header[l++] = '\n';
385 header[l++] = '0' + !!level_prefix;
386 header[l++] = '\n';
387 header[l++] = '0';
388 header[l++] = '\n';
389 header[l++] = '0';
390 header[l++] = '\n';
391 header[l++] = '0';
392 header[l++] = '\n';
393
394 r = loop_write(fd, header, l, false);
395 if (r < 0) {
396 close_nointr_nofail(fd);
397 return (int) r;
398 }
399
400 if ((size_t) r != l) {
401 close_nointr_nofail(fd);
402 return -errno;
403 }
404
405 return fd;
406 }
407
408 _public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
409 int r;
410 va_list ap;
411
412 va_start(ap, format);
413 r = sd_journal_printv_with_location(priority, file, line, func, format, ap);
414 va_end(ap);
415
416 return r;
417 }
418
419 _public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
420 char buffer[8 + LINE_MAX], p[11];
421 struct iovec iov[5];
422 char *f;
423 size_t fl;
424
425 if (priority < 0 || priority > 7)
426 return -EINVAL;
427
428 if (_unlikely_(!format))
429 return -EINVAL;
430
431 snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK);
432 char_array_0(p);
433
434 memcpy(buffer, "MESSAGE=", 8);
435 vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
436 char_array_0(buffer);
437
438 /* func is initialized from __func__ which is not a macro, but
439 * a static const char[], hence cannot easily be prefixed with
440 * CODE_FUNC=, hence let's do it manually here. */
441 fl = strlen(func);
442 f = alloca(fl + 10);
443 memcpy(f, "CODE_FUNC=", 10);
444 memcpy(f + 10, func, fl + 1);
445
446 zero(iov);
447 IOVEC_SET_STRING(iov[0], buffer);
448 IOVEC_SET_STRING(iov[1], p);
449 IOVEC_SET_STRING(iov[2], file);
450 IOVEC_SET_STRING(iov[3], line);
451 IOVEC_SET_STRING(iov[4], f);
452
453 return sd_journal_sendv(iov, ELEMENTSOF(iov));
454 }
455
456 _public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
457 int r, i, j;
458 va_list ap;
459 struct iovec *iov = NULL;
460 char *f;
461 size_t fl;
462
463 va_start(ap, format);
464 i = fill_iovec_sprintf(format, ap, 3, &iov);
465 va_end(ap);
466
467 if (_unlikely_(i < 0)) {
468 r = i;
469 goto finish;
470 }
471
472 fl = strlen(func);
473 f = alloca(fl + 10);
474 memcpy(f, "CODE_FUNC=", 10);
475 memcpy(f + 10, func, fl + 1);
476
477 IOVEC_SET_STRING(iov[0], file);
478 IOVEC_SET_STRING(iov[1], line);
479 IOVEC_SET_STRING(iov[2], f);
480
481 r = sd_journal_sendv(iov, i);
482
483 finish:
484 for (j = 3; j < i; j++)
485 free(iov[j].iov_base);
486
487 free(iov);
488
489 return r;
490 }
491
492 _public_ int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n) {
493 struct iovec *niov;
494 char *f;
495 size_t fl;
496
497 if (_unlikely_(!iov))
498 return -EINVAL;
499
500 if (_unlikely_(n <= 0))
501 return -EINVAL;
502
503 niov = alloca(sizeof(struct iovec) * (n + 3));
504 memcpy(niov, iov, sizeof(struct iovec) * n);
505
506 fl = strlen(func);
507 f = alloca(fl + 10);
508 memcpy(f, "CODE_FUNC=", 10);
509 memcpy(f + 10, func, fl + 1);
510
511 IOVEC_SET_STRING(niov[n++], file);
512 IOVEC_SET_STRING(niov[n++], line);
513 IOVEC_SET_STRING(niov[n++], f);
514
515 return sd_journal_sendv(niov, n);
516 }