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