LCOV - code coverage report
Current view: top level - journal - journal-send.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 157 262 59.9 %
Date: 2019-08-22 15:41:25 Functions: 9 14 64.3 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <fcntl.h>
       5             : #include <printf.h>
       6             : #include <stddef.h>
       7             : #include <sys/socket.h>
       8             : #include <sys/un.h>
       9             : #include <unistd.h>
      10             : 
      11             : #define SD_JOURNAL_SUPPRESS_LOCATION
      12             : 
      13             : #include "sd-journal.h"
      14             : 
      15             : #include "alloc-util.h"
      16             : #include "errno-util.h"
      17             : #include "fd-util.h"
      18             : #include "io-util.h"
      19             : #include "memfd-util.h"
      20             : #include "socket-util.h"
      21             : #include "stdio-util.h"
      22             : #include "string-util.h"
      23             : #include "tmpfile-util.h"
      24             : 
      25             : #define SNDBUF_SIZE (8*1024*1024)
      26             : 
      27             : #define ALLOCA_CODE_FUNC(f, func)                 \
      28             :         do {                                      \
      29             :                 size_t _fl;                       \
      30             :                 const char *_func = (func);       \
      31             :                 char **_f = &(f);                 \
      32             :                 _fl = strlen(_func) + 1;          \
      33             :                 *_f = newa(char, _fl + 10);       \
      34             :                 memcpy(*_f, "CODE_FUNC=", 10);    \
      35             :                 memcpy(*_f + 10, _func, _fl);     \
      36             :         } while (false)
      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          16 : static int journal_fd(void) {
      44             :         int fd;
      45             :         static int fd_plus_one = 0;
      46             : 
      47          16 : retry:
      48          16 :         if (fd_plus_one > 0)
      49          15 :                 return fd_plus_one - 1;
      50             : 
      51           1 :         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
      52           1 :         if (fd < 0)
      53           0 :                 return -errno;
      54             : 
      55           1 :         fd_inc_sndbuf(fd, SNDBUF_SIZE);
      56             : 
      57           1 :         if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
      58           0 :                 safe_close(fd);
      59           0 :                 goto retry;
      60             :         }
      61             : 
      62           1 :         return fd;
      63             : }
      64             : 
      65           0 : _public_ int sd_journal_print(int priority, const char *format, ...) {
      66             :         int r;
      67             :         va_list ap;
      68             : 
      69           0 :         va_start(ap, format);
      70           0 :         r = sd_journal_printv(priority, format, ap);
      71           0 :         va_end(ap);
      72             : 
      73           0 :         return r;
      74             : }
      75             : 
      76           0 : _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[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
      82             :         struct iovec iov[2];
      83             : 
      84           0 :         assert_return(priority >= 0, -EINVAL);
      85           0 :         assert_return(priority <= 7, -EINVAL);
      86           0 :         assert_return(format, -EINVAL);
      87             : 
      88           0 :         xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
      89             : 
      90           0 :         memcpy(buffer, "MESSAGE=", 8);
      91           0 :         vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
      92             : 
      93             :         /* Strip trailing whitespace, keep prefix whitespace. */
      94           0 :         (void) strstrip(buffer);
      95             : 
      96             :         /* Suppress empty lines */
      97           0 :         if (isempty(buffer+8))
      98           0 :                 return 0;
      99             : 
     100           0 :         iov[0] = IOVEC_MAKE_STRING(buffer);
     101           0 :         iov[1] = IOVEC_MAKE_STRING(p);
     102             : 
     103           0 :         return sd_journal_sendv(iov, 2);
     104             : }
     105             : 
     106           4 : _printf_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) {
     107           4 :         PROTECT_ERRNO;
     108           4 :         int r, n = 0, i = 0, j;
     109           4 :         struct iovec *iov = NULL;
     110             : 
     111           4 :         assert(_iov);
     112             : 
     113           4 :         if (extra > 0) {
     114           4 :                 n = MAX(extra * 2, extra + 4);
     115           4 :                 iov = malloc0(n * sizeof(struct iovec));
     116           4 :                 if (!iov) {
     117           0 :                         r = -ENOMEM;
     118           0 :                         goto fail;
     119             :                 }
     120             : 
     121           4 :                 i = extra;
     122             :         }
     123             : 
     124          23 :         while (format) {
     125             :                 struct iovec *c;
     126             :                 char *buffer;
     127             :                 va_list aq;
     128             : 
     129          19 :                 if (i >= n) {
     130           2 :                         n = MAX(i*2, 4);
     131           2 :                         c = realloc(iov, n * sizeof(struct iovec));
     132           2 :                         if (!c) {
     133           0 :                                 r = -ENOMEM;
     134           0 :                                 goto fail;
     135             :                         }
     136             : 
     137           2 :                         iov = c;
     138             :                 }
     139             : 
     140          19 :                 va_copy(aq, ap);
     141          19 :                 if (vasprintf(&buffer, format, aq) < 0) {
     142           0 :                         va_end(aq);
     143           0 :                         r = -ENOMEM;
     144           0 :                         goto fail;
     145             :                 }
     146          19 :                 va_end(aq);
     147             : 
     148          24 :                 VA_FORMAT_ADVANCE(format, ap);
     149             : 
     150          19 :                 (void) strstrip(buffer); /* strip trailing whitespace, keep prefixing whitespace */
     151             : 
     152          19 :                 iov[i++] = IOVEC_MAKE_STRING(buffer);
     153             : 
     154          19 :                 format = va_arg(ap, char *);
     155             :         }
     156             : 
     157           4 :         *_iov = iov;
     158             : 
     159           4 :         return i;
     160             : 
     161           0 : fail:
     162           0 :         for (j = 0; j < i; j++)
     163           0 :                 free(iov[j].iov_base);
     164             : 
     165           0 :         free(iov);
     166             : 
     167           0 :         return r;
     168             : }
     169             : 
     170           0 : _public_ int sd_journal_send(const char *format, ...) {
     171             :         int r, i, j;
     172             :         va_list ap;
     173           0 :         struct iovec *iov = NULL;
     174             : 
     175           0 :         va_start(ap, format);
     176           0 :         i = fill_iovec_sprintf(format, ap, 0, &iov);
     177           0 :         va_end(ap);
     178             : 
     179           0 :         if (_unlikely_(i < 0)) {
     180           0 :                 r = i;
     181           0 :                 goto finish;
     182             :         }
     183             : 
     184           0 :         r = sd_journal_sendv(iov, i);
     185             : 
     186           0 : finish:
     187           0 :         for (j = 0; j < i; j++)
     188           0 :                 free(iov[j].iov_base);
     189             : 
     190           0 :         free(iov);
     191             : 
     192           0 :         return r;
     193             : }
     194             : 
     195          16 : _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
     196          16 :         PROTECT_ERRNO;
     197             :         int fd, r;
     198          16 :         _cleanup_close_ int buffer_fd = -1;
     199             :         struct iovec *w;
     200             :         uint64_t *l;
     201          16 :         int i, j = 0;
     202             :         static const union sockaddr_union sa = {
     203             :                 .un.sun_family = AF_UNIX,
     204             :                 .un.sun_path = "/run/systemd/journal/socket",
     205             :         };
     206          32 :         struct msghdr mh = {
     207             :                 .msg_name = (struct sockaddr*) &sa.sa,
     208          16 :                 .msg_namelen = SOCKADDR_UN_LEN(sa.un),
     209             :         };
     210             :         ssize_t k;
     211          16 :         bool have_syslog_identifier = false;
     212          16 :         bool seal = true;
     213             : 
     214          16 :         assert_return(iov, -EINVAL);
     215          16 :         assert_return(n > 0, -EINVAL);
     216             : 
     217          16 :         w = newa(struct iovec, n * 5 + 3);
     218          16 :         l = newa(uint64_t, n);
     219             : 
     220          89 :         for (i = 0; i < n; i++) {
     221             :                 char *c, *nl;
     222             : 
     223          73 :                 if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1))
     224           0 :                         return -EINVAL;
     225             : 
     226          73 :                 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
     227          73 :                 if (_unlikely_(!c || c == iov[i].iov_base))
     228           0 :                         return -EINVAL;
     229             : 
     230         146 :                 have_syslog_identifier = have_syslog_identifier ||
     231          73 :                         (c == (char *) iov[i].iov_base + 17 &&
     232           0 :                          startswith(iov[i].iov_base, "SYSLOG_IDENTIFIER"));
     233             : 
     234          73 :                 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
     235          73 :                 if (nl) {
     236           4 :                         if (_unlikely_(nl < c))
     237           0 :                                 return -EINVAL;
     238             : 
     239             :                         /* Already includes a newline? Bummer, then
     240             :                          * let's write the variable name, then a
     241             :                          * newline, then the size (64bit LE), followed
     242             :                          * by the data and a final newline */
     243             : 
     244           4 :                         w[j++] = IOVEC_MAKE(iov[i].iov_base, c - (char*) iov[i].iov_base);
     245           4 :                         w[j++] = IOVEC_MAKE_STRING("\n");
     246             : 
     247           4 :                         l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
     248           4 :                         w[j++] = IOVEC_MAKE(&l[i], sizeof(uint64_t));
     249             : 
     250           4 :                         w[j++] = IOVEC_MAKE(c + 1, iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1);
     251             :                 } else
     252             :                         /* Nothing special? Then just add the line and
     253             :                          * append a newline */
     254          69 :                         w[j++] = iov[i];
     255             : 
     256          73 :                 w[j++] = IOVEC_MAKE_STRING("\n");
     257             :         }
     258             : 
     259          16 :         if (!have_syslog_identifier &&
     260          16 :             string_is_safe(program_invocation_short_name)) {
     261             : 
     262             :                 /* Implicitly add program_invocation_short_name, if it
     263             :                  * is not set explicitly. We only do this for
     264             :                  * program_invocation_short_name, and nothing else
     265             :                  * since everything else is much nicer to retrieve
     266             :                  * from the outside. */
     267             : 
     268          16 :                 w[j++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=");
     269          16 :                 w[j++] = IOVEC_MAKE_STRING(program_invocation_short_name);
     270          16 :                 w[j++] = IOVEC_MAKE_STRING("\n");
     271             :         }
     272             : 
     273          16 :         fd = journal_fd();
     274          16 :         if (_unlikely_(fd < 0))
     275           0 :                 return fd;
     276             : 
     277          16 :         mh.msg_iov = w;
     278          16 :         mh.msg_iovlen = j;
     279             : 
     280          16 :         k = sendmsg(fd, &mh, MSG_NOSIGNAL);
     281          16 :         if (k >= 0)
     282          15 :                 return 0;
     283             : 
     284             :         /* Fail silently if the journal is not available */
     285           1 :         if (errno == ENOENT)
     286           0 :                 return 0;
     287             : 
     288           1 :         if (!IN_SET(errno, EMSGSIZE, ENOBUFS))
     289           0 :                 return -errno;
     290             : 
     291             :         /* Message doesn't fit... Let's dump the data in a memfd or
     292             :          * temporary file and just pass a file descriptor of it to the
     293             :          * other side.
     294             :          *
     295             :          * For the temporary files we use /dev/shm instead of /tmp
     296             :          * here, since we want this to be a tmpfs, and one that is
     297             :          * available from early boot on and where unprivileged users
     298             :          * can create files. */
     299           1 :         buffer_fd = memfd_new(NULL);
     300           1 :         if (buffer_fd < 0) {
     301           0 :                 if (buffer_fd == -ENOSYS) {
     302           0 :                         buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC);
     303           0 :                         if (buffer_fd < 0)
     304           0 :                                 return buffer_fd;
     305             : 
     306           0 :                         seal = false;
     307             :                 } else
     308           0 :                         return buffer_fd;
     309             :         }
     310             : 
     311           1 :         n = writev(buffer_fd, w, j);
     312           1 :         if (n < 0)
     313           0 :                 return -errno;
     314             : 
     315           1 :         if (seal) {
     316           1 :                 r = memfd_set_sealed(buffer_fd);
     317           1 :                 if (r < 0)
     318           0 :                         return r;
     319             :         }
     320             : 
     321           1 :         r = send_one_fd_sa(fd, buffer_fd, mh.msg_name, mh.msg_namelen, 0);
     322           1 :         if (r == -ENOENT)
     323             :                 /* Fail silently if the journal is not available */
     324           0 :                 return 0;
     325           1 :         return r;
     326             : }
     327             : 
     328           2 : static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) {
     329           2 :         PROTECT_ERRNO;
     330             :         size_t n, k;
     331             : 
     332           2 :         k = isempty(message) ? 0 : strlen(message) + 2;
     333           2 :         n = 8 + k + 256 + 1;
     334             : 
     335           0 :         for (;;) {
     336           2 :                 char buffer[n];
     337             :                 char* j;
     338             : 
     339           2 :                 errno = 0;
     340           2 :                 j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k);
     341           2 :                 if (errno == 0) {
     342             :                         char error[STRLEN("ERRNO=") + DECIMAL_STR_MAX(int) + 1];
     343             : 
     344           2 :                         if (j != buffer + 8 + k)
     345           2 :                                 memmove(buffer + 8 + k, j, strlen(j)+1);
     346             : 
     347           2 :                         memcpy(buffer, "MESSAGE=", 8);
     348             : 
     349           2 :                         if (k > 0) {
     350           1 :                                 memcpy(buffer + 8, message, k - 2);
     351           1 :                                 memcpy(buffer + 8 + k - 2, ": ", 2);
     352             :                         }
     353             : 
     354           2 :                         xsprintf(error, "ERRNO=%i", _saved_errno_);
     355             : 
     356             :                         assert_cc(3 == LOG_ERR);
     357           2 :                         iov[skip+0] = IOVEC_MAKE_STRING("PRIORITY=3");
     358           2 :                         iov[skip+1] = IOVEC_MAKE_STRING(buffer);
     359           2 :                         iov[skip+2] = IOVEC_MAKE_STRING(error);
     360             : 
     361           2 :                         return sd_journal_sendv(iov, skip + 3);
     362             :                 }
     363             : 
     364           0 :                 if (errno != ERANGE)
     365           0 :                         return -errno;
     366             : 
     367           0 :                 n *= 2;
     368             :         }
     369             : }
     370             : 
     371           0 : _public_ int sd_journal_perror(const char *message) {
     372             :         struct iovec iovec[3];
     373             : 
     374           0 :         return fill_iovec_perror_and_send(message, 0, iovec);
     375             : }
     376             : 
     377           0 : _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
     378             :         static const union sockaddr_union sa = {
     379             :                 .un.sun_family = AF_UNIX,
     380             :                 .un.sun_path = "/run/systemd/journal/stdout",
     381             :         };
     382           0 :         _cleanup_close_ int fd = -1;
     383             :         char *header;
     384             :         size_t l;
     385             :         int r;
     386             : 
     387           0 :         assert_return(priority >= 0, -EINVAL);
     388           0 :         assert_return(priority <= 7, -EINVAL);
     389             : 
     390           0 :         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
     391           0 :         if (fd < 0)
     392           0 :                 return -errno;
     393             : 
     394           0 :         r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
     395           0 :         if (r < 0)
     396           0 :                 return -errno;
     397             : 
     398           0 :         if (shutdown(fd, SHUT_RD) < 0)
     399           0 :                 return -errno;
     400             : 
     401           0 :         (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
     402             : 
     403           0 :         identifier = strempty(identifier);
     404             : 
     405           0 :         l = strlen(identifier);
     406           0 :         header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
     407             : 
     408           0 :         memcpy(header, identifier, l);
     409           0 :         header[l++] = '\n';
     410           0 :         header[l++] = '\n'; /* unit id */
     411           0 :         header[l++] = '0' + priority;
     412           0 :         header[l++] = '\n';
     413           0 :         header[l++] = '0' + !!level_prefix;
     414           0 :         header[l++] = '\n';
     415           0 :         header[l++] = '0';
     416           0 :         header[l++] = '\n';
     417           0 :         header[l++] = '0';
     418           0 :         header[l++] = '\n';
     419           0 :         header[l++] = '0';
     420           0 :         header[l++] = '\n';
     421             : 
     422           0 :         r = loop_write(fd, header, l, false);
     423           0 :         if (r < 0)
     424           0 :                 return r;
     425             : 
     426           0 :         return TAKE_FD(fd);
     427             : }
     428             : 
     429           2 : _public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
     430             :         int r;
     431             :         va_list ap;
     432             : 
     433           2 :         va_start(ap, format);
     434           2 :         r = sd_journal_printv_with_location(priority, file, line, func, format, ap);
     435           2 :         va_end(ap);
     436             : 
     437           2 :         return r;
     438             : }
     439             : 
     440           2 : _public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
     441             :         char buffer[8 + LINE_MAX], p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
     442             :         struct iovec iov[5];
     443             :         char *f;
     444             : 
     445           2 :         assert_return(priority >= 0, -EINVAL);
     446           2 :         assert_return(priority <= 7, -EINVAL);
     447           2 :         assert_return(format, -EINVAL);
     448             : 
     449           2 :         xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
     450             : 
     451           2 :         memcpy(buffer, "MESSAGE=", 8);
     452           2 :         vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
     453             : 
     454             :         /* Strip trailing whitespace, keep prefixing whitespace */
     455           2 :         (void) strstrip(buffer);
     456             : 
     457             :         /* Suppress empty lines */
     458           2 :         if (isempty(buffer+8))
     459           0 :                 return 0;
     460             : 
     461             :         /* func is initialized from __func__ which is not a macro, but
     462             :          * a static const char[], hence cannot easily be prefixed with
     463             :          * CODE_FUNC=, hence let's do it manually here. */
     464           2 :         ALLOCA_CODE_FUNC(f, func);
     465             : 
     466           2 :         iov[0] = IOVEC_MAKE_STRING(buffer);
     467           2 :         iov[1] = IOVEC_MAKE_STRING(p);
     468           2 :         iov[2] = IOVEC_MAKE_STRING(file);
     469           2 :         iov[3] = IOVEC_MAKE_STRING(line);
     470           2 :         iov[4] = IOVEC_MAKE_STRING(f);
     471             : 
     472           2 :         return sd_journal_sendv(iov, ELEMENTSOF(iov));
     473             : }
     474             : 
     475           4 : _public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) {
     476           8 :         _cleanup_free_ struct iovec *iov = NULL;
     477             :         int r, i, j;
     478             :         va_list ap;
     479             :         char *f;
     480             : 
     481           4 :         va_start(ap, format);
     482           4 :         i = fill_iovec_sprintf(format, ap, 3, &iov);
     483           4 :         va_end(ap);
     484             : 
     485           4 :         if (_unlikely_(i < 0)) {
     486           0 :                 r = i;
     487           0 :                 goto finish;
     488             :         }
     489             : 
     490           4 :         ALLOCA_CODE_FUNC(f, func);
     491             : 
     492           4 :         iov[0] = IOVEC_MAKE_STRING(file);
     493           4 :         iov[1] = IOVEC_MAKE_STRING(line);
     494           4 :         iov[2] = IOVEC_MAKE_STRING(f);
     495             : 
     496           4 :         r = sd_journal_sendv(iov, i);
     497             : 
     498           4 : finish:
     499          23 :         for (j = 3; j < i; j++)
     500          19 :                 free(iov[j].iov_base);
     501             : 
     502           4 :         return r;
     503             : }
     504             : 
     505           4 : _public_ int sd_journal_sendv_with_location(
     506             :                 const char *file, const char *line,
     507             :                 const char *func,
     508             :                 const struct iovec *iov, int n) {
     509             : 
     510             :         struct iovec *niov;
     511             :         char *f;
     512             : 
     513           4 :         assert_return(iov, -EINVAL);
     514           4 :         assert_return(n > 0, -EINVAL);
     515             : 
     516           4 :         niov = newa(struct iovec, n + 3);
     517           4 :         memcpy(niov, iov, sizeof(struct iovec) * n);
     518             : 
     519           4 :         ALLOCA_CODE_FUNC(f, func);
     520             : 
     521           4 :         niov[n++] = IOVEC_MAKE_STRING(file);
     522           4 :         niov[n++] = IOVEC_MAKE_STRING(line);
     523           4 :         niov[n++] = IOVEC_MAKE_STRING(f);
     524             : 
     525           4 :         return sd_journal_sendv(niov, n);
     526             : }
     527             : 
     528           2 : _public_ int sd_journal_perror_with_location(
     529             :                 const char *file, const char *line,
     530             :                 const char *func,
     531             :                 const char *message) {
     532             : 
     533             :         struct iovec iov[6];
     534             :         char *f;
     535             : 
     536           2 :         ALLOCA_CODE_FUNC(f, func);
     537             : 
     538           2 :         iov[0] = IOVEC_MAKE_STRING(file);
     539           2 :         iov[1] = IOVEC_MAKE_STRING(line);
     540           2 :         iov[2] = IOVEC_MAKE_STRING(f);
     541             : 
     542           2 :         return fill_iovec_perror_and_send(message, 3, iov);
     543             : }

Generated by: LCOV version 1.14