+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, "/run/systemd/journal/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);
+
+ if (ucred) {
+ 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 (ucred && 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 void forward_syslog_raw(Server *s, const char *buffer, struct ucred *ucred, struct timeval *tv) {
+ struct iovec iovec;
+
+ assert(s);
+ assert(buffer);
+
+ IOVEC_SET_STRING(iovec, buffer);
+ forward_syslog_iovec(s, &iovec, 1, ucred, tv);
+}
+
+static void forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) {
+ struct iovec iovec[5];
+ char header_priority[6], header_time[64], header_pid[16];
+ int n = 0;
+ time_t t;
+ struct tm *tm;
+ char *ident_buf = NULL;
+
+ assert(s);
+ assert(priority >= 0);
+ assert(priority <= 999);
+ assert(message);
+
+ /* First: priority field */
+ snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
+ char_array_0(header_priority);
+ IOVEC_SET_STRING(iovec[n++], header_priority);
+
+ /* Second: timestamp */
+ t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC));
+ tm = localtime(&t);
+ if (!tm)
+ return;
+ if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
+ return;
+ IOVEC_SET_STRING(iovec[n++], header_time);
+
+ /* Third: identifier and PID */
+ if (ucred) {
+ if (!identifier) {
+ get_process_comm(ucred->pid, &ident_buf);
+ identifier = ident_buf;
+ }
+
+ snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
+ char_array_0(header_pid);
+
+ if (identifier)
+ IOVEC_SET_STRING(iovec[n++], identifier);
+
+ IOVEC_SET_STRING(iovec[n++], header_pid);
+ } else if (identifier) {
+ IOVEC_SET_STRING(iovec[n++], identifier);
+ IOVEC_SET_STRING(iovec[n++], ": ");
+ }
+
+ /* Fourth: message */
+ IOVEC_SET_STRING(iovec[n++], message);
+
+ forward_syslog_iovec(s, iovec, n, ucred, tv);
+
+ free(ident_buf);
+}
+
+static int fixup_priority(int priority) {
+
+ if ((priority & LOG_FACMASK) == 0)
+ return (priority & LOG_PRIMASK) | LOG_USER;
+
+ return priority;
+}
+
+static void forward_kmsg(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred) {
+ struct iovec iovec[5];
+ char header_priority[6], header_pid[16];
+ int n = 0;
+ char *ident_buf = NULL;
+ int fd;
+
+ assert(s);
+ assert(priority >= 0);
+ assert(priority <= 999);
+ assert(message);
+
+ /* Never allow messages with kernel facility to be written to
+ * kmsg, regardless where the data comes from. */
+ priority = fixup_priority(priority);
+
+ /* First: priority field */
+ snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
+ char_array_0(header_priority);
+ IOVEC_SET_STRING(iovec[n++], header_priority);
+
+ /* Second: identifier and PID */
+ if (ucred) {
+ if (!identifier) {
+ get_process_comm(ucred->pid, &ident_buf);
+ identifier = ident_buf;
+ }
+
+ snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
+ char_array_0(header_pid);
+
+ if (identifier)
+ IOVEC_SET_STRING(iovec[n++], identifier);
+
+ IOVEC_SET_STRING(iovec[n++], header_pid);
+ } else if (identifier) {
+ IOVEC_SET_STRING(iovec[n++], identifier);
+ IOVEC_SET_STRING(iovec[n++], ": ");
+ }
+
+ /* Fourth: message */
+ IOVEC_SET_STRING(iovec[n++], message);
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0) {
+ log_debug("Failed to open /dev/kmsg for logging: %s", strerror(errno));
+ goto finish;
+ }
+
+ if (writev(fd, iovec, n) < 0)
+ log_debug("Failed to write to /dev/kmsg for logging: %s", strerror(errno));
+
+ close_nointr_nofail(fd);
+
+finish:
+ free(ident_buf);
+}
+
+static void forward_console(Server *s, const char *identifier, const char *message, struct ucred *ucred) {
+ struct iovec iovec[4];
+ char header_pid[16];
+ int n = 0, fd;
+ char *ident_buf = NULL;
+
+ assert(s);
+ assert(message);
+
+ /* First: identifier and PID */
+ if (ucred) {
+ if (!identifier) {
+ get_process_comm(ucred->pid, &ident_buf);
+ identifier = ident_buf;
+ }
+
+ snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
+ char_array_0(header_pid);
+
+ if (identifier)
+ IOVEC_SET_STRING(iovec[n++], identifier);
+
+ IOVEC_SET_STRING(iovec[n++], header_pid);
+ } else if (identifier) {
+ IOVEC_SET_STRING(iovec[n++], identifier);
+ IOVEC_SET_STRING(iovec[n++], ": ");
+ }
+
+ /* Third: message */
+ IOVEC_SET_STRING(iovec[n++], message);
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0) {
+ log_debug("Failed to open /dev/console for logging: %s", strerror(errno));
+ goto finish;
+ }
+
+ if (writev(fd, iovec, n) < 0)
+ log_debug("Failed to write to /dev/console for logging: %s", strerror(errno));
+
+ close_nointr_nofail(fd);
+
+finish:
+ free(ident_buf);
+}
+
+static void read_identifier(const char **buf, char **identifier, char **pid) {
+ const char *p;
+ char *t;
+ size_t l, e;
+
+ assert(buf);
+ assert(identifier);
+ assert(pid);
+
+ p = *buf;
+
+ p += strspn(p, WHITESPACE);
+ l = strcspn(p, WHITESPACE);
+
+ if (l <= 0 ||
+ p[l-1] != ':')
+ return;
+
+ e = l;
+ l--;
+
+ if (p[l-1] == ']') {
+ size_t k = l-1;
+
+ for (;;) {
+
+ if (p[k] == '[') {
+ t = strndup(p+k+1, l-k-2);
+ if (t)
+ *pid = t;
+
+ l = k;
+ break;
+ }
+
+ if (k == 0)
+ break;
+
+ k--;
+ }
+ }
+
+ t = strndup(p, l);
+ if (t)
+ *identifier = t;
+
+ *buf = p + e;
+ *buf += strspn(*buf, WHITESPACE);
+}
+
+static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) {
+ char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
+ struct iovec iovec[N_IOVEC_META_FIELDS + 6];
+ unsigned n = 0;
+ int priority = LOG_USER | LOG_INFO;
+ char *identifier = NULL, *pid = NULL;
+
+ assert(s);
+ assert(buf);
+
+ if (s->forward_to_syslog)
+ forward_syslog_raw(s, buf, ucred, tv);
+
+ parse_syslog_priority((char**) &buf, &priority);
+ skip_syslog_date((char**) &buf);
+ read_identifier(&buf, &identifier, &pid);
+
+ if (s->forward_to_kmsg)
+ forward_kmsg(s, priority, identifier, buf, ucred);
+
+ if (s->forward_to_console)
+ forward_console(s, identifier, buf, ucred);
+
+ IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog");
+
+ if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
+ IOVEC_SET_STRING(iovec[n++], syslog_priority);
+
+ if (priority & LOG_FACMASK)
+ if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
+ IOVEC_SET_STRING(iovec[n++], syslog_facility);
+
+ if (identifier) {
+ syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
+ if (syslog_identifier)
+ IOVEC_SET_STRING(iovec[n++], syslog_identifier);
+ }
+
+ if (pid) {
+ syslog_pid = strappend("SYSLOG_PID=", pid);
+ if (syslog_pid)
+ IOVEC_SET_STRING(iovec[n++], syslog_pid);
+ }
+
+ message = strappend("MESSAGE=", buf);
+ if (message)
+ IOVEC_SET_STRING(iovec[n++], message);
+
+ dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, priority);
+
+ free(message);
+ free(identifier);
+ free(pid);
+ free(syslog_priority);
+ free(syslog_facility);
+ free(syslog_identifier);
+}
+
+static bool valid_user_field(const char *p, size_t l) {
+ const char *a;
+
+ /* We kinda enforce POSIX syntax recommendations for
+ environment variables here, but make a couple of additional
+ requirements.
+
+ http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
+
+ /* No empty field names */
+ if (l <= 0)
+ return false;
+
+ /* Don't allow names longer than 64 chars */
+ if (l > 64)
+ return false;
+
+ /* Variables starting with an underscore are protected */
+ if (p[0] == '_')
+ return false;
+
+ /* Don't allow digits as first character */
+ if (p[0] >= '0' && p[0] <= '9')
+ return false;
+
+ /* Only allow A-Z0-9 and '_' */
+ for (a = p; a < p + l; a++)
+ if (!((*a >= 'A' && *a <= 'Z') ||
+ (*a >= '0' && *a <= '9') ||
+ *a == '_'))
+ return false;
+
+ return true;
+}
+
+static void process_native_message(Server *s, const void *buffer, size_t buffer_size, struct ucred *ucred, struct timeval *tv) {
+ struct iovec *iovec = NULL;
+ unsigned n = 0, m = 0, j, tn = (unsigned) -1;
+ const char *p;
+ size_t remaining;
+ int priority = LOG_INFO;
+ char *identifier = NULL, *message = NULL;
+
+ assert(s);
+ assert(buffer || n == 0);
+
+ p = buffer;
+ remaining = buffer_size;
+
+ while (remaining > 0) {
+ const char *e, *q;
+
+ e = memchr(p, '\n', remaining);
+
+ if (!e) {
+ /* Trailing noise, let's ignore it, and flush what we collected */
+ log_debug("Received message with trailing noise, ignoring.");
+ break;
+ }
+
+ if (e == p) {
+ /* Entry separator */
+ dispatch_message(s, iovec, n, m, ucred, tv, priority);
+ n = 0;
+ priority = LOG_INFO;
+
+ p++;
+ remaining--;
+ continue;
+ }
+
+ if (*p == '.' || *p == '#') {
+ /* Ignore control commands for now, and
+ * comments too. */
+ remaining -= (e - p) + 1;
+ p = e + 1;
+ continue;
+ }
+
+ /* A property follows */
+
+ if (n+N_IOVEC_META_FIELDS >= m) {
+ struct iovec *c;
+ unsigned u;
+
+ u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
+ c = realloc(iovec, u * sizeof(struct iovec));
+ if (!c) {
+ log_error("Out of memory");
+ break;
+ }
+
+ iovec = c;
+ m = u;
+ }
+
+ q = memchr(p, '=', e - p);
+ if (q) {
+ if (valid_user_field(p, q - p)) {
+ size_t l;
+
+ l = e - p;
+
+ /* If the field name starts with an
+ * underscore, skip the variable,
+ * since that indidates a trusted
+ * field */
+ iovec[n].iov_base = (char*) p;
+ iovec[n].iov_len = l;
+ n++;
+
+ /* We need to determine the priority
+ * of this entry for the rate limiting
+ * logic */
+ if (l == 10 &&
+ memcmp(p, "PRIORITY=", 9) == 0 &&
+ p[9] >= '0' && p[9] <= '9')
+ priority = (priority & LOG_FACMASK) | (p[9] - '0');
+
+ else if (l == 17 &&
+ memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
+ p[16] >= '0' && p[16] <= '9')
+ priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
+
+ else if (l == 18 &&
+ memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
+ p[16] >= '0' && p[16] <= '9' &&
+ p[17] >= '0' && p[17] <= '9')
+ priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
+
+ else if (l >= 12 &&
+ memcmp(p, "SYSLOG_IDENTIFIER=", 11) == 0) {
+ char *t;
+
+ t = strndup(p + 11, l - 11);
+ if (t) {
+ free(identifier);
+ identifier = t;
+ }
+ } else if (l >= 8 &&
+ memcmp(p, "MESSAGE=", 8) == 0) {
+ char *t;
+
+ t = strndup(p + 8, l - 8);
+ if (t) {
+ free(message);
+ message = t;
+ }
+ }
+ }
+
+ remaining -= (e - p) + 1;
+ p = e + 1;
+ continue;
+ } else {
+ uint64_t l;
+ char *k;
+
+ if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
+ log_debug("Failed to parse message, ignoring.");
+ break;
+ }
+
+ memcpy(&l, e + 1, sizeof(uint64_t));
+ l = le64toh(l);
+
+ if (remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
+ e[1+sizeof(uint64_t)+l] != '\n') {
+ log_debug("Failed to parse message, ignoring.");
+ break;
+ }
+
+ k = malloc((e - p) + 1 + l);
+ if (!k) {
+ log_error("Out of memory");
+ break;
+ }
+
+ memcpy(k, p, e - p);
+ k[e - p] = '=';
+ memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
+
+ if (valid_user_field(p, e - p)) {
+ iovec[n].iov_base = k;
+ iovec[n].iov_len = (e - p) + 1 + l;
+ n++;
+ } else
+ free(k);
+
+ remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
+ p = e + 1 + sizeof(uint64_t) + l + 1;
+ }
+ }
+
+ if (n <= 0)
+ goto finish;
+
+ tn = n++;
+ IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
+
+ if (message) {
+ if (s->forward_to_syslog)
+ forward_syslog(s, priority, identifier, message, ucred, tv);
+
+ if (s->forward_to_kmsg)
+ forward_kmsg(s, priority, identifier, message, ucred);
+
+ if (s->forward_to_console)
+ forward_console(s, identifier, message, ucred);
+ }
+
+ dispatch_message(s, iovec, n, m, ucred, tv, priority);
+
+finish:
+ for (j = 0; j < n; j++) {
+ if (j == tn)
+ continue;
+
+ if (iovec[j].iov_base < buffer ||
+ (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
+ free(iovec[j].iov_base);
+ }
+
+ free(identifier);
+ free(message);
+}
+
+static int stdout_stream_log(StdoutStream *s, const char *p) {
+ struct iovec iovec[N_IOVEC_META_FIELDS + 5];
+ char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL;
+ unsigned n = 0;
+ int priority;
+
+ assert(s);
+ assert(p);
+
+ priority = s->priority;
+
+ if (s->level_prefix)
+ parse_syslog_priority((char**) &p, &priority);
+
+ if (s->forward_to_syslog || s->server->forward_to_syslog)
+ forward_syslog(s->server, fixup_priority(priority), s->identifier, p, &s->ucred, NULL);
+
+ if (s->forward_to_kmsg || s->server->forward_to_kmsg)
+ forward_kmsg(s->server, priority, s->identifier, p, &s->ucred);
+
+ if (s->forward_to_console || s->server->forward_to_console)
+ forward_console(s->server, s->identifier, p, &s->ucred);
+
+ IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=stdout");
+
+ if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
+ IOVEC_SET_STRING(iovec[n++], syslog_priority);
+
+ if (priority & LOG_FACMASK)
+ if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
+ IOVEC_SET_STRING(iovec[n++], syslog_facility);
+
+ if (s->identifier) {
+ syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier);
+ if (syslog_identifier)
+ IOVEC_SET_STRING(iovec[n++], syslog_identifier);
+ }
+
+ message = strappend("MESSAGE=", p);
+ if (message)
+ IOVEC_SET_STRING(iovec[n++], message);
+
+ dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, priority);
+
+ free(message);
+ free(syslog_priority);
+ free(syslog_facility);
+ free(syslog_identifier);
+
+ return 0;
+}
+
+static int stdout_stream_line(StdoutStream *s, char *p) {
+ int r;
+
+ assert(s);
+ assert(p);
+
+ p = strstrip(p);
+
+ switch (s->state) {
+
+ case STDOUT_STREAM_IDENTIFIER:
+ s->identifier = strdup(p);
+ if (!s->identifier) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ s->state = STDOUT_STREAM_PRIORITY;
+ return 0;
+
+ case STDOUT_STREAM_PRIORITY:
+ r = safe_atoi(p, &s->priority);
+ if (r < 0 || s->priority <= 0 || s->priority >= 999) {
+ log_warning("Failed to parse log priority line.");
+ return -EINVAL;
+ }
+
+ s->state = STDOUT_STREAM_LEVEL_PREFIX;
+ return 0;
+
+ case STDOUT_STREAM_LEVEL_PREFIX:
+ r = parse_boolean(p);
+ if (r < 0) {
+ log_warning("Failed to parse level prefix line.");
+ return -EINVAL;
+ }
+
+ s->level_prefix = !!r;
+ s->state = STDOUT_STREAM_FORWARD_TO_SYSLOG;
+ return 0;
+
+ case STDOUT_STREAM_FORWARD_TO_SYSLOG:
+ r = parse_boolean(p);
+ if (r < 0) {
+ log_warning("Failed to parse forward to syslog line.");
+ return -EINVAL;
+ }
+
+ s->forward_to_syslog = !!r;
+ s->state = STDOUT_STREAM_FORWARD_TO_KMSG;
+ return 0;
+
+ case STDOUT_STREAM_FORWARD_TO_KMSG:
+ r = parse_boolean(p);
+ if (r < 0) {
+ log_warning("Failed to parse copy to kmsg line.");
+ return -EINVAL;
+ }
+
+ s->forward_to_kmsg = !!r;
+ s->state = STDOUT_STREAM_FORWARD_TO_CONSOLE;
+ return 0;
+
+ case STDOUT_STREAM_FORWARD_TO_CONSOLE:
+ r = parse_boolean(p);
+ if (r < 0) {
+ log_warning("Failed to parse copy to console line.");
+ return -EINVAL;
+ }
+
+ s->forward_to_console = !!r;
+ s->state = STDOUT_STREAM_RUNNING;
+ return 0;
+
+ case STDOUT_STREAM_RUNNING:
+ return stdout_stream_log(s, p);
+ }
+
+ 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)
+ skip = end - p + 1;
+ else if (remaining >= sizeof(s->buffer) - 1) {
+ end = p + sizeof(s->buffer) - 1;
+ skip = remaining;
+ } else
+ break;
+
+ *end = 0;
+
+ r = stdout_stream_line(s, p);
+ if (r < 0)
+ return r;
+
+ remaining -= skip;
+ p += skip;
+ }
+
+ if (force_flush && remaining > 0) {
+ p[remaining] = 0;
+ r = stdout_stream_line(s, p);
+ 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->identifier);
+ 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 parse_kernel_timestamp(char **_p, usec_t *t) {
+ usec_t r;
+ int k, i;
+ char *p;
+
+ assert(_p);
+ assert(*_p);
+ assert(t);
+
+ p = *_p;
+
+ if (strlen(p) < 14 || p[0] != '[' || p[13] != ']' || p[6] != '.')
+ return 0;
+
+ r = 0;
+
+ for (i = 1; i <= 5; i++) {
+ r *= 10;
+
+ if (p[i] == ' ')
+ continue;
+
+ k = undecchar(p[i]);
+ if (k < 0)
+ return 0;
+
+ r += k;
+ }
+
+ for (i = 7; i <= 12; i++) {
+ r *= 10;
+
+ k = undecchar(p[i]);
+ if (k < 0)
+ return 0;
+
+ r += k;
+ }
+
+ *t = r;
+ *_p += 14;
+ *_p += strspn(*_p, WHITESPACE);
+
+ return 1;
+}
+
+static void proc_kmsg_line(Server *s, const char *p) {
+ struct iovec iovec[N_IOVEC_META_FIELDS + 7];
+ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL;
+ int priority = LOG_KERN | LOG_INFO;
+ unsigned n = 0;
+ usec_t usec;
+ char *identifier = NULL, *pid = NULL;
+
+ assert(s);
+ assert(p);
+
+ parse_syslog_priority((char **) &p, &priority);
+
+ if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN)
+ return;
+
+ if (parse_kernel_timestamp((char **) &p, &usec) > 0) {
+ if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu",
+ (unsigned long long) usec) >= 0)
+ IOVEC_SET_STRING(iovec[n++], source_time);
+ }
+
+ IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel");
+
+ if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
+ IOVEC_SET_STRING(iovec[n++], syslog_priority);
+
+ if ((priority & LOG_FACMASK) == LOG_KERN) {
+
+ if (s->forward_to_syslog)
+ forward_syslog(s, priority, "kernel", p, NULL, NULL);
+
+ IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel");
+ } else {
+ read_identifier(&p, &identifier, &pid);
+
+ if (s->forward_to_syslog)
+ forward_syslog(s, priority, identifier, p, NULL, NULL);
+
+ if (identifier) {
+ syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
+ if (syslog_identifier)
+ IOVEC_SET_STRING(iovec[n++], syslog_identifier);
+ }
+
+ if (pid) {
+ syslog_pid = strappend("SYSLOG_PID=", pid);
+ if (syslog_pid)
+ IOVEC_SET_STRING(iovec[n++], syslog_pid);
+ }
+
+ if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
+ IOVEC_SET_STRING(iovec[n++], syslog_facility);
+ }
+
+ message = strappend("MESSAGE=", p);
+ if (message)
+ IOVEC_SET_STRING(iovec[n++], message);
+
+
+ dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority);
+
+ free(message);
+ free(syslog_priority);
+ free(syslog_identifier);
+ free(syslog_pid);
+ free(syslog_facility);
+ free(source_time);
+ free(identifier);
+ free(pid);
+}
+
+static void proc_kmsg_scan(Server *s) {
+ char *p;
+ size_t remaining;
+
+ assert(s);
+
+ p = s->proc_kmsg_buffer;
+ remaining = s->proc_kmsg_length;
+ for (;;) {
+ char *end;
+ size_t skip;
+
+ end = memchr(p, '\n', remaining);
+ if (end)
+ skip = end - p + 1;
+ else if (remaining >= sizeof(s->proc_kmsg_buffer) - 1) {
+ end = p + sizeof(s->proc_kmsg_buffer) - 1;
+ skip = remaining;
+ } else
+ break;
+
+ *end = 0;
+
+ proc_kmsg_line(s, p);
+
+ remaining -= skip;
+ p += skip;
+ }
+
+ if (p > s->proc_kmsg_buffer) {
+ memmove(s->proc_kmsg_buffer, p, remaining);
+ s->proc_kmsg_length = remaining;
+ }
+}