#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
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;
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;
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;
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);
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;
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;
+}