X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fjournal-send.c;h=b5b4fbfa0da9f32f48a575289ded8650804f7dfc;hb=7264278fbbdc1dc6c30fedc902d1337594aa6ff6;hp=e2575a9805f9c572a3b95bee4942c5bf508d3d6a;hpb=7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49e;p=elogind.git diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index e2575a980..b5b4fbfa0 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -23,9 +23,14 @@ #include #include #include +#include +#include #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; +}