chiark / gitweb /
journal: PAGE_SIZE is not known on ppc and other archs
[elogind.git] / src / journal / journal-send.c
index e2575a9805f9c572a3b95bee4942c5bf508d3d6a..b5b4fbfa0da9f32f48a575289ded8650804f7dfc 100644 (file)
 #include <sys/un.h>
 #include <errno.h>
 #include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include "sd-journal.h"
 #include "util.h"
+#include "socket-util.h"
+
+#define SNDBUF_SIZE (8*1024*1024)
 
 /* We open a single fd, and we'll share it with the current process,
  * all its threads, and all its subprocesses. This means we need to
@@ -44,6 +49,8 @@ retry:
         if (fd < 0)
                 return -errno;
 
+        fd_inc_sndbuf(fd, SNDBUF_SIZE);
+
         if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
                 close_nointr_nofail(fd);
                 goto retry;
@@ -52,33 +59,42 @@ retry:
         return fd;
 }
 
-int sd_journal_print(const char *format, ...) {
+_public_ int sd_journal_print(int priority, const char *format, ...) {
         int r;
         va_list ap;
 
         va_start(ap, format);
-        r = sd_journal_printv(format, ap);
+        r = sd_journal_printv(priority, format, ap);
         va_end(ap);
 
         return r;
 }
 
-int sd_journal_printv(const char *format, va_list ap) {
-        char buffer[8 + LINE_MAX];
-        struct iovec iov;
+_public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
+        char buffer[8 + LINE_MAX], p[11];
+        struct iovec iov[2];
+
+        if (priority < 0 || priority > 7)
+                return -EINVAL;
+
+        if (!format)
+                return -EINVAL;
+
+        snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK);
+        char_array_0(p);
 
         memcpy(buffer, "MESSAGE=", 8);
         vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
-
         char_array_0(buffer);
 
         zero(iov);
-        IOVEC_SET_STRING(iov, buffer);
+        IOVEC_SET_STRING(iov[0], buffer);
+        IOVEC_SET_STRING(iov[1], p);
 
-        return sd_journal_sendv(&iov, 1);
+        return sd_journal_sendv(iov, 2);
 }
 
-int sd_journal_send(const char *format, ...) {
+_public_ int sd_journal_send(const char *format, ...) {
         int r, n = 0, i = 0, j;
         va_list ap;
         struct iovec *iov = NULL;
@@ -121,13 +137,23 @@ fail:
         return r;
 }
 
-int sd_journal_sendv(const struct iovec *iov, int n) {
-        int fd;
+_public_ int sd_journal_sendv(const struct iovec *iov, int n) {
+        int fd, buffer_fd;
         struct iovec *w;
         uint64_t *l;
         int i, j = 0;
         struct msghdr mh;
         struct sockaddr_un sa;
+        ssize_t k;
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(int))];
+        } control;
+        struct cmsghdr *cmsg;
+        /* We use /dev/shm instead of /tmp here, since we want this to
+         * be a tmpfs, and one that is available from early boot on
+         * and where unprivileged users can create files. */
+        char path[] = "/dev/shm/journal.XXXXXX";
 
         if (!iov || n <= 0)
                 return -EINVAL;
@@ -138,8 +164,12 @@ int sd_journal_sendv(const struct iovec *iov, int n) {
         for (i = 0; i < n; i++) {
                 char *c, *nl;
 
+                if (!iov[i].iov_base ||
+                    iov[i].iov_len <= 1)
+                        return -EINVAL;
+
                 c = memchr(iov[i].iov_base, '=', iov[i].iov_len);
-                if (!c)
+                if (!c || c == iov[i].iov_base)
                         return -EINVAL;
 
                 nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len);
@@ -181,7 +211,7 @@ int sd_journal_sendv(const struct iovec *iov, int n) {
 
         zero(sa);
         sa.sun_family = AF_UNIX;
-        strncpy(sa.sun_path,"/run/systemd/journal", sizeof(sa.sun_path));
+        strncpy(sa.sun_path, "/run/systemd/journal/socket", sizeof(sa.sun_path));
 
         zero(mh);
         mh.msg_name = &sa;
@@ -189,8 +219,116 @@ int sd_journal_sendv(const struct iovec *iov, int n) {
         mh.msg_iov = w;
         mh.msg_iovlen = j;
 
-        if (sendmsg(fd, &mh, MSG_NOSIGNAL) < 0)
+        k = sendmsg(fd, &mh, MSG_NOSIGNAL);
+        if (k >= 0)
+                return 0;
+
+        if (errno != EMSGSIZE && errno != ENOBUFS)
+                return -errno;
+
+        /* Message doesn't fit... Let's dump the data in a temporary
+         * file and just pass a file descriptor of it to the other
+         * side */
+
+        buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR);
+        if (buffer_fd < 0)
+                return -errno;
+
+        if (unlink(path) < 0) {
+                close_nointr_nofail(buffer_fd);
+                return -errno;
+        }
+
+        n = writev(buffer_fd, w, j);
+        if (n < 0)  {
+                close_nointr_nofail(buffer_fd);
+                return -errno;
+        }
+
+        mh.msg_iov = NULL;
+        mh.msg_iovlen = 0;
+
+        zero(control);
+        mh.msg_control = &control;
+        mh.msg_controllen = sizeof(control);
+
+        cmsg = CMSG_FIRSTHDR(&mh);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+        memcpy(CMSG_DATA(cmsg), &buffer_fd, sizeof(int));
+
+        mh.msg_controllen = cmsg->cmsg_len;
+
+        k = sendmsg(fd, &mh, MSG_NOSIGNAL);
+        close_nointr_nofail(buffer_fd);
+
+        if (k < 0)
                 return -errno;
 
         return 0;
 }
+
+_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
+        union sockaddr_union sa;
+        int fd;
+        char *header;
+        size_t l;
+        ssize_t r;
+
+        if (priority < 0 || priority > 7)
+                return -EINVAL;
+
+        fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return -errno;
+
+        zero(sa);
+        sa.un.sun_family = AF_UNIX;
+        strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
+
+        r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
+        if (r < 0) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        if (shutdown(fd, SHUT_RD) < 0) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        fd_inc_sndbuf(fd, SNDBUF_SIZE);
+
+        if (!identifier)
+                identifier = "";
+
+        l = strlen(identifier);
+        header = alloca(l + 1 + 2 + 2 + 2 + 2 + 2);
+
+        memcpy(header, identifier, l);
+        header[l++] = '\n';
+        header[l++] = '0' + priority;
+        header[l++] = '\n';
+        header[l++] = '0' + !!level_prefix;
+        header[l++] = '\n';
+        header[l++] = '0';
+        header[l++] = '\n';
+        header[l++] = '0';
+        header[l++] = '\n';
+        header[l++] = '0';
+        header[l++] = '\n';
+
+        r = loop_write(fd, header, l, false);
+        if (r < 0) {
+                close_nointr_nofail(fd);
+                return (int) r;
+        }
+
+        if ((size_t) r != l) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        return fd;
+}