chiark / gitweb /
readahead: fix calculation of percentage
[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 + 1 + 2 + 2 + 2 + 2 + 2);
379
380         memcpy(header, identifier, l);
381         header[l++] = '\n';
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 }