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