+ dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL);
+
+ if (s->tee_console) {
+ int console;
+
+ console = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (console >= 0) {
+ n = 0;
+ if (s->tag) {
+ IOVEC_SET_STRING(iovec[n++], s->tag);
+ IOVEC_SET_STRING(iovec[n++], ": ");
+ }
+
+ iovec[n].iov_base = (void*) p;
+ iovec[n].iov_len = l;
+ n++;
+
+ IOVEC_SET_STRING(iovec[n++], (char*) "\n");
+
+ writev(console, iovec, n);
+ }
+ }
+
+ free(message);
+ free(syslog_priority);
+
+ return 0;
+}
+
+static int stdout_stream_line(StdoutStream *s, const char *p, size_t l) {
+ assert(s);
+ assert(p);
+
+ while (l > 0 && strchr(WHITESPACE, *p)) {
+ l--;
+ p++;
+ }
+
+ while (l > 0 && strchr(WHITESPACE, *(p+l-1)))
+ l--;
+
+ switch (s->state) {
+
+ case STDOUT_STREAM_TAG:
+
+ if (l > 0) {
+ s->tag = strndup(p, l);
+ if (!s->tag) {
+ log_error("Out of memory");
+ return -EINVAL;
+ }
+ }
+
+ s->state = STDOUT_STREAM_PRIORITY;
+ return 0;
+
+ case STDOUT_STREAM_PRIORITY:
+ if (l != 1 || *p < '0' || *p > '7') {
+ log_warning("Failed to parse log priority line.");
+ return -EINVAL;
+ }
+
+ s->priority = *p - '0';
+ s->state = STDOUT_STREAM_PRIORITY_PREFIX;
+ return 0;
+
+ case STDOUT_STREAM_PRIORITY_PREFIX:
+ if (l != 1 || *p < '0' || *p > '1') {
+ log_warning("Failed to parse priority prefix line.");
+ return -EINVAL;
+ }
+
+ s->priority_prefix = *p - '0';
+ s->state = STDOUT_STREAM_TEE_CONSOLE;
+ return 0;
+
+ case STDOUT_STREAM_TEE_CONSOLE:
+ if (l != 1 || *p < '0' || *p > '1') {
+ log_warning("Failed to parse tee to console line.");
+ return -EINVAL;
+ }
+
+ s->tee_console = *p - '0';
+ s->state = STDOUT_STREAM_RUNNING;
+ return 0;
+
+ case STDOUT_STREAM_RUNNING:
+ return stdout_stream_log(s, p, l);
+ }
+
+ assert_not_reached("Unknown stream state");
+}
+
+static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
+ char *p;
+ size_t remaining;
+ int r;
+
+ assert(s);
+
+ p = s->buffer;
+ remaining = s->length;
+ for (;;) {
+ char *end;
+ size_t skip;
+
+ end = memchr(p, '\n', remaining);
+ if (!end) {
+ if (remaining >= LINE_MAX) {
+ end = p + LINE_MAX;
+ skip = LINE_MAX;
+ } else
+ break;
+ } else
+ skip = end - p + 1;
+
+ r = stdout_stream_line(s, p, end - p);
+ if (r < 0)
+ return r;
+
+ remaining -= skip;
+ p += skip;
+ }
+
+ if (force_flush && remaining > 0) {
+ r = stdout_stream_line(s, p, remaining);
+ if (r < 0)
+ return r;
+
+ p += remaining;
+ remaining = 0;
+ }
+
+ if (p > s->buffer) {
+ memmove(s->buffer, p, remaining);
+ s->length = remaining;
+ }
+
+ return 0;
+}
+
+static int stdout_stream_process(StdoutStream *s) {
+ ssize_t l;
+ int r;
+
+ assert(s);
+
+ l = read(s->fd, s->buffer+s->length, sizeof(s->buffer)-1-s->length);
+ if (l < 0) {
+
+ if (errno == EAGAIN)
+ return 0;
+
+ log_warning("Failed to read from stream: %m");
+ return -errno;
+ }
+
+ if (l == 0) {
+ r = stdout_stream_scan(s, true);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ s->length += l;
+ r = stdout_stream_scan(s, false);
+ if (r < 0)
+ return r;
+
+ return 1;
+
+}
+
+static void stdout_stream_free(StdoutStream *s) {
+ assert(s);
+
+ if (s->server) {
+ assert(s->server->n_stdout_streams > 0);
+ s->server->n_stdout_streams --;
+ LIST_REMOVE(StdoutStream, stdout_stream, s->server->stdout_streams, s);
+ }
+
+ if (s->fd >= 0) {
+ if (s->server)
+ epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL);
+
+ close_nointr_nofail(s->fd);
+ }
+
+ free(s->tag);
+ free(s);
+}
+
+static int stdout_stream_new(Server *s) {
+ StdoutStream *stream;
+ int fd, r;
+ socklen_t len;
+ struct epoll_event ev;
+
+ assert(s);
+
+ fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ if (fd < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ log_error("Failed to accept stdout connection: %m");
+ return -errno;
+ }
+
+ if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) {
+ log_warning("Too many stdout streams, refusing connection.");
+ close_nointr_nofail(fd);
+ return 0;
+ }
+
+ stream = new0(StdoutStream, 1);
+ if (!stream) {
+ log_error("Out of memory.");
+ close_nointr_nofail(fd);
+ return -ENOMEM;
+ }
+
+ stream->fd = fd;
+
+ len = sizeof(stream->ucred);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &stream->ucred, &len) < 0) {
+ log_error("Failed to determine peer credentials: %m");
+ r = -errno;
+ goto fail;
+ }
+
+ if (shutdown(fd, SHUT_WR) < 0) {
+ log_error("Failed to shutdown writing side of socket: %m");
+ r = -errno;
+ goto fail;
+ }
+
+ zero(ev);
+ ev.data.ptr = stream;
+ ev.events = EPOLLIN;
+ if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ log_error("Failed to add stream to event loop: %m");
+ r = -errno;
+ goto fail;
+ }
+
+ stream->server = s;
+ LIST_PREPEND(StdoutStream, stdout_stream, s->stdout_streams, stream);
+ s->n_stdout_streams ++;
+
+ return 0;
+
+fail:
+ stdout_stream_free(stream);
+ return r;
+}
+
+static int process_event(Server *s, struct epoll_event *ev) {
+ assert(s);
+