chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[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, 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 }