+static void forward_syslog(Server *s, const void *buffer, size_t length, struct ucred *ucred, struct timeval *tv) {
+ struct msghdr msghdr;
+ struct iovec iovec;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(sizeof(struct timeval))];
+ } control;
+ union sockaddr_union sa;
+
+ assert(s);
+
+ zero(msghdr);
+
+ zero(iovec);
+ iovec.iov_base = (void*) buffer;
+ iovec.iov_len = length;
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path));
+ msghdr.msg_name = &sa;
+ msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
+
+ zero(control);
+ msghdr.msg_control = &control;
+ msghdr.msg_controllen = sizeof(control);
+
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
+ msghdr.msg_controllen = cmsg->cmsg_len;
+
+ /* Forward the syslog message we received via /dev/log to
+ * /run/systemd/syslog. Unfortunately we currently can't set
+ * the SO_TIMESTAMP auxiliary data, and hence we don't. */
+
+ if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
+ return;
+
+ if (errno == ESRCH) {
+ struct ucred u;
+
+ /* Hmm, presumably the sender process vanished
+ * by now, so let's fix it as good as we
+ * can, and retry */
+
+ u = *ucred;
+ u.pid = getpid();
+ memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
+
+ if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
+ return;
+ }
+
+ log_debug("Failed to forward syslog message: %m");
+}
+
+static int process_event(Server *s, struct epoll_event *ev) {
+ assert(s);
+
+ if (ev->data.fd == s->signal_fd) {
+ struct signalfd_siginfo sfsi;
+ ssize_t n;
+
+ if (ev->events != EPOLLIN) {
+ log_info("Got invalid event from epoll.");
+ return -EIO;
+ }
+
+ n = read(s->signal_fd, &sfsi, sizeof(sfsi));
+ if (n != sizeof(sfsi)) {
+
+ if (n >= 0)
+ return -EIO;
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ }
+
+ if (sfsi.ssi_signo == SIGUSR1) {
+ server_flush_to_var(s);
+ return 0;
+ }
+
+ log_debug("Received SIG%s", signal_to_string(sfsi.ssi_signo));
+ return 0;
+
+ } else if (ev->data.fd == s->native_fd ||
+ ev->data.fd == s->syslog_fd) {
+
+ if (ev->events != EPOLLIN) {
+ log_info("Got invalid event from epoll.");
+ return -EIO;
+ }
+
+ for (;;) {
+ struct msghdr msghdr;
+ struct iovec iovec;
+ struct ucred *ucred = NULL;
+ struct timeval *tv = NULL;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(sizeof(struct timeval))];
+ } control;
+ ssize_t n;
+ int v;
+
+ if (ioctl(ev->data.fd, SIOCINQ, &v) < 0) {
+ log_error("SIOCINQ failed: %m");
+ return -errno;
+ }
+
+ if (v <= 0)
+ return 1;
+
+ if (s->buffer_size < (size_t) v) {
+ void *b;
+ size_t l;
+
+ l = MAX(LINE_MAX + (size_t) v, s->buffer_size * 2);
+ b = realloc(s->buffer, l+1);
+
+ if (!b) {
+ log_error("Couldn't increase buffer.");
+ return -ENOMEM;
+ }
+
+ s->buffer_size = l;
+ s->buffer = b;
+ }
+
+ zero(iovec);
+ iovec.iov_base = s->buffer;
+ iovec.iov_len = s->buffer_size;
+
+ zero(control);
+ zero(msghdr);
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = &control;
+ msghdr.msg_controllen = sizeof(control);
+
+ n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT);
+ if (n < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 1;
+
+ log_error("recvmsg() failed: %m");
+ return -errno;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
+
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
+ else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SO_TIMESTAMP &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
+ tv = (struct timeval*) CMSG_DATA(cmsg);
+ }
+
+ if (ev->data.fd == s->syslog_fd) {
+ char *e;
+
+ e = memchr(s->buffer, '\n', n);
+ if (e)
+ *e = 0;
+ else
+ s->buffer[n] = 0;
+
+ forward_syslog(s, s->buffer, n, ucred, tv);
+ process_syslog_message(s, strstrip(s->buffer), ucred, tv);
+ } else
+ process_native_message(s, s->buffer, n, ucred, tv);
+ }
+
+ return 1;
+
+ } else if (ev->data.fd == s->stdout_fd) {
+
+ if (ev->events != EPOLLIN) {
+ log_info("Got invalid event from epoll.");
+ return -EIO;
+ }
+
+ stdout_stream_new(s);
+ return 1;
+
+ } else {
+ StdoutStream *stream;
+
+ if ((ev->events|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) {
+ log_info("Got invalid event from epoll.");
+ return -EIO;
+ }
+
+ /* If it is none of the well-known fds, it must be an
+ * stdout stream fd. Note that this is a bit ugly here
+ * (since we rely that none of the well-known fds
+ * could be interpreted as pointer), but nonetheless
+ * safe, since the well-known fds would never get an
+ * fd > 4096, i.e. beyond the first memory page */
+
+ stream = ev->data.ptr;
+
+ if (stdout_stream_process(stream) <= 0)
+ stdout_stream_free(stream);
+
+ return 1;
+ }
+
+ log_error("Unknown event.");
+ return 0;
+}
+