[systemd-commits] 7 commits - .gitignore Makefile.am configure.ac src/.gitignore src/ask-password-api.c src/job.c src/journal src/log.c src/systemctl.c src/systemd src/util.c src/util.h sysctl.d/.gitignore sysctl.d/Makefile sysctl.d/coredump.conf.in

Lennart Poettering lennart at kemper.freedesktop.org
Fri Jan 13 16:54:50 PST 2012


 .gitignore                 |    2 
 Makefile.am                |   35 ++++++++
 configure.ac               |    8 +
 src/.gitignore             |    1 
 src/ask-password-api.c     |    4 
 src/job.c                  |    8 -
 src/journal/cat.c          |  181 +++++++++++++++++++++++++++++++++++++++++++++
 src/journal/coredump.c     |  173 +++++++++++++++++++++++++++++++++++++++++++
 src/journal/journal-send.c |   57 +++++++++++++-
 src/journal/journald.c     |  112 +++++++++++++++++++++++----
 src/log.c                  |    2 
 src/systemctl.c            |   32 +++----
 src/systemd/sd-messages.h  |    2 
 src/util.c                 |   24 +++--
 src/util.h                 |    4 
 sysctl.d/.gitignore        |    1 
 sysctl.d/Makefile          |    1 
 sysctl.d/coredump.conf.in  |   10 ++
 18 files changed, 603 insertions(+), 54 deletions(-)

New commits:
commit 4c7de07481080d19c7c22a8437184c515ebaeafb
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 14 01:54:02 2012 +0100

    journal: handle empty syslog identifier properly

diff --git a/src/journal/journald.c b/src/journal/journald.c
index 39263bf..f63a12f 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -1405,10 +1405,14 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
         switch (s->state) {
 
         case STDOUT_STREAM_IDENTIFIER:
-                s->identifier = strdup(p);
-                if (!s->identifier) {
-                        log_error("Out of memory");
-                        return -ENOMEM;
+                if (isempty(p))
+                        s->identifier = NULL;
+                else  {
+                        s->identifier = strdup(p);
+                        if (!s->identifier) {
+                                log_error("Out of memory");
+                                return -ENOMEM;
+                        }
                 }
 
                 s->state = STDOUT_STREAM_PRIORITY;

commit 5c3759bf8a3d418fa877e6a278f3150f404745b2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 14 01:53:47 2012 +0100

    journal: fix bad memory access

diff --git a/src/journal/journald.c b/src/journal/journald.c
index 53dbf8e..39263bf 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -489,7 +489,7 @@ static void dispatch_message_real(Server *s,
                         exe = strappend("_EXE=", t);
                         free(t);
 
-                        if (comm)
+                        if (exe)
                                 IOVEC_SET_STRING(iovec[n++], exe);
                 }
 

commit 0dad12c190b7493955cd60d2a1625199b1709f69
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 14 01:53:20 2012 +0100

    journal: if the data to be sent is larger than the maximum datagram size resort to passing a temporary fd over native protocol

diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index d51aec9..03bd170 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -23,6 +23,8 @@
 #include <sys/un.h>
 #include <errno.h>
 #include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include "sd-journal.h"
 #include "util.h"
@@ -132,12 +134,19 @@ fail:
 }
 
 _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
-        int fd;
+        int fd, buffer_fd;
         struct iovec *w;
         uint64_t *l;
         int i, j = 0;
         struct msghdr mh;
         struct sockaddr_un sa;
+        char path[] = "/tmp/journal.XXXXXX";
+        ssize_t k;
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(int))];
+        } control;
+        struct cmsghdr *cmsg;
 
         if (!iov || n <= 0)
                 return -EINVAL;
@@ -203,7 +212,51 @@ _public_ 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)
+                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;
diff --git a/src/journal/journald.c b/src/journal/journald.c
index f924c93..53dbf8e 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -70,6 +70,8 @@
 
 #define N_IOVEC_META_FIELDS 17
 
+#define ENTRY_SIZE_MAX (1024*1024*32)
+
 typedef enum StdoutStreamState {
         STDOUT_STREAM_IDENTIFIER,
         STDOUT_STREAM_PRIORITY,
@@ -1291,6 +1293,52 @@ finish:
         free(message);
 }
 
+static void process_native_file(Server *s, int fd, struct ucred *ucred, struct timeval *tv) {
+        struct stat st;
+        void *p;
+        ssize_t n;
+
+        assert(s);
+        assert(fd >= 0);
+
+        /* Data is in the passed file, since it didn't fit in a
+         * datagram. We can't map the file here, since clients might
+         * then truncate it and trigger a SIGBUS for us. So let's
+         * stupidly read it */
+
+        if (fstat(fd, &st) < 0) {
+                log_error("Failed to stat passed file, ignoring: %m");
+                return;
+        }
+
+        if (!S_ISREG(st.st_mode)) {
+                log_error("File passed is not regular. Ignoring.");
+                return;
+        }
+
+        if (st.st_size <= 0)
+                return;
+
+        if (st.st_size > ENTRY_SIZE_MAX) {
+                log_error("File passed too large. Ignoring.");
+                return;
+        }
+
+        p = malloc(st.st_size);
+        if (!p) {
+                log_error("Out of memory");
+                return;
+        }
+
+        n = pread(fd, p, st.st_size, 0);
+        if (n < 0)
+                log_error("Failed to read file, ignoring: %s", strerror(-n));
+        else if (n > 0)
+                process_native_message(s, p, n, ucred, tv);
+
+        free(p);
+}
+
 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;
@@ -1300,6 +1348,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
         assert(s);
         assert(p);
 
+        if (isempty(p))
+                return 0;
+
         priority = s->priority;
 
         if (s->level_prefix)
@@ -1649,6 +1700,9 @@ static void proc_kmsg_line(Server *s, const char *p) {
         assert(s);
         assert(p);
 
+        if (isempty(p))
+                return;
+
         parse_syslog_priority((char **) &p, &priority);
 
         if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN)
@@ -1697,7 +1751,6 @@ static void proc_kmsg_line(Server *s, const char *p) {
         if (message)
                 IOVEC_SET_STRING(iovec[n++], message);
 
-
         dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority);
 
         free(message);
@@ -2027,19 +2080,19 @@ static int process_event(Server *s, struct epoll_event *ev) {
                         union {
                                 struct cmsghdr cmsghdr;
                                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
-                                            CMSG_SPACE(sizeof(struct timeval))];
+                                            CMSG_SPACE(sizeof(struct timeval)) +
+                                            CMSG_SPACE(sizeof(int))];
                         } control;
                         ssize_t n;
                         int v;
+                        int *fds = NULL;
+                        unsigned n_fds = 0;
 
                         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;
@@ -2067,7 +2120,7 @@ static int process_event(Server *s, struct epoll_event *ev) {
                         msghdr.msg_control = &control;
                         msghdr.msg_controllen = sizeof(control);
 
-                        n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT);
+                        n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
                         if (n < 0) {
 
                                 if (errno == EINTR || errno == EAGAIN)
@@ -2087,20 +2140,37 @@ static int process_event(Server *s, struct epoll_event *ev) {
                                          cmsg->cmsg_type == SO_TIMESTAMP &&
                                          cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
                                         tv = (struct timeval*) CMSG_DATA(cmsg);
+                                else if (cmsg->cmsg_level == SOL_SOCKET &&
+                                         cmsg->cmsg_type == SCM_RIGHTS) {
+                                        fds = (int*) CMSG_DATA(cmsg);
+                                        n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+                                }
                         }
 
                         if (ev->data.fd == s->syslog_fd) {
                                 char *e;
 
-                                e = memchr(s->buffer, '\n', n);
-                                if (e)
-                                        *e = 0;
-                                else
-                                        s->buffer[n] = 0;
+                                if (n > 0 && n_fds == 0) {
+                                        e = memchr(s->buffer, '\n', n);
+                                        if (e)
+                                                *e = 0;
+                                        else
+                                                s->buffer[n] = 0;
+
+                                        process_syslog_message(s, strstrip(s->buffer), ucred, tv);
+                                } else if (n_fds > 0)
+                                        log_warning("Got file descriptors via syslog socket. Ignoring.");
+
+                        } else {
+                                if (n > 0 && n_fds == 0)
+                                        process_native_message(s, s->buffer, n, ucred, tv);
+                                else if (n == 0 && n_fds == 1)
+                                        process_native_file(s, fds[0], ucred, tv);
+                                else if (n_fds > 0)
+                                        log_warning("Got too many file descriptors via native socket. Ignoring.");
+                        }
 
-                                process_syslog_message(s, strstrip(s->buffer), ucred, tv);
-                        } else
-                                process_native_message(s, s->buffer, n, ucred, tv);
+                        close_many(fds, n_fds);
                 }
 
                 return 1;

commit f5e04665ebf7124f3ea17dcf258793ed73a95fe1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 14 00:37:35 2012 +0100

    journal: hook up coredumping with journal

diff --git a/.gitignore b/.gitignore
index c260210..011aece 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/systemd-coredump
 /systemd-cat
 /systemd-rc-local-generator
 /libsystemd-id128.pc
diff --git a/Makefile.am b/Makefile.am
index d94de56..607ae7e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -52,6 +52,7 @@ bashcompletiondir=$(sysconfdir)/bash_completion.d
 pkgsysconfdir=$(sysconfdir)/systemd
 userunitdir=$(prefix)/lib/systemd/user
 tmpfilesdir=$(prefix)/lib/tmpfiles.d
+sysctldir=$(prefix)/lib/sysctl.d
 usergeneratordir=$(pkglibexecdir)/user-generators
 pkgincludedir=$(includedir)/systemd
 
@@ -1397,6 +1398,27 @@ EXTRA_DIST += \
 CLEANFILES += \
 	src/journal/journald-gperf.c
 
+if ENABLE_COREDUMP
+
+systemd_coredump_SOURCES = \
+        src/journal/coredump.c
+
+systemd_coredump_LDADD = \
+	libsystemd-basic.la \
+	libsystemd-journal.la \
+	libsystemd-login.la
+
+rootlibexec_PROGRAMS += \
+        systemd-coredump
+
+sysctl_DATA = \
+        sysctl.d/coredump.conf
+
+EXTRA_DIST += \
+        sysctl.d/coredump.conf.in
+
+endif
+
 # ------------------------------------------------------------------------------
 if ENABLE_BINFMT
 systemd_binfmt_SOURCES = \
@@ -2074,6 +2096,9 @@ units/%: units/%.in Makefile
 man/%: man/%.in Makefile
 	$(SED_PROCESS)
 
+sysctl.d/%: sysctl.d/%.in Makefile
+	$(SED_PROCESS)
+
 %.pc: %.pc.in Makefile
 	$(SED_PROCESS)
 
diff --git a/configure.ac b/configure.ac
index 80d0156..194caa5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -372,6 +372,13 @@ if test "x$enable_localed" != "xno"; then
 fi
 AM_CONDITIONAL(ENABLE_LOCALED, [test "$have_localed" = "yes"])
 
+have_coredump=no
+AC_ARG_ENABLE(coredump, AS_HELP_STRING([--disable-coredump], [disable coredump hook]))
+if test "x$enable_coredump" != "xno"; then
+	have_coredump=yes
+fi
+AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"])
+
 have_gtk=no
 AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk], [disable GTK tools]))
 if test "x$enable_gtk" != "xno"; then
@@ -646,6 +653,7 @@ AC_MSG_RESULT([
         hostnamed:               ${have_hostnamed}
         timedated:               ${have_timedated}
         localed:                 ${have_localed}
+        coredump:                ${have_coredump}
         plymouth:                ${have_plymouth}
         prefix:                  ${prefix}
         rootprefix:              ${with_rootprefix}
diff --git a/src/.gitignore b/src/.gitignore
index ff2737b..c54c6f6 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,3 +1,4 @@
+/*.pc
 load-fragment-gperf-nulstr.c
 load-fragment-gperf.c
 load-fragment-gperf.gperf
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
new file mode 100644
index 0000000..f160270
--- /dev/null
+++ b/src/journal/coredump.c
@@ -0,0 +1,173 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <systemd/sd-journal.h>
+#include <systemd/sd-login.h>
+
+#include "log.h"
+#include "util.h"
+
+#define COREDUMP_MAX (64*1024)
+
+enum {
+        ARG_PID = 1,
+        ARG_UID,
+        ARG_GID,
+        ARG_SIGNAL,
+        ARG_TIMESTAMP,
+        ARG_COMM,
+        _ARG_MAX
+};
+
+int main(int argc, char* argv[]) {
+        int r, j = 0;
+        char *p = NULL;
+        ssize_t n;
+        pid_t pid;
+        struct iovec iovec[14];
+        char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
+                *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
+                *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t;
+
+        log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
+        log_parse_environment();
+        log_open();
+
+        if (argc != _ARG_MAX) {
+                log_error("Invalid number of arguments passed from kernel.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        r = parse_pid(argv[ARG_PID], &pid);
+        if (r < 0) {
+                log_error("Failed to parse PID.");
+                r = -EINVAL;
+                goto finish;
+        }
+
+        p = malloc(9 + COREDUMP_MAX);
+        if (!p) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        memcpy(p, "COREDUMP=", 9);
+
+        n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false);
+        if (n < 0) {
+                log_error("Failed to read core dump data: %s", strerror(-n));
+                r = (int) n;
+                goto finish;
+        }
+
+        zero(iovec);
+        iovec[j].iov_base = p;
+        iovec[j].iov_len = 9 + n;
+        j++;
+
+        core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
+        if (core_pid)
+                IOVEC_SET_STRING(iovec[j++], core_pid);
+
+        core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
+        if (core_uid)
+                IOVEC_SET_STRING(iovec[j++], core_uid);
+
+        core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
+        if (core_gid)
+                IOVEC_SET_STRING(iovec[j++], core_gid);
+
+        core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
+        if (core_signal)
+                IOVEC_SET_STRING(iovec[j++], core_signal);
+
+        core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
+        if (core_comm)
+                IOVEC_SET_STRING(iovec[j++], core_comm);
+
+        if (sd_pid_get_session(pid, &t) >= 0) {
+                core_session = strappend("COREDUMP_SESSION=", t);
+                free(t);
+
+                if (core_session)
+                        IOVEC_SET_STRING(iovec[j++], core_session);
+        }
+
+        if (sd_pid_get_unit(pid, &t) >= 0) {
+                core_unit = strappend("COREDUMP_UNIT=", t);
+                free(t);
+
+                if (core_unit)
+                        IOVEC_SET_STRING(iovec[j++], core_unit);
+        }
+
+        if (get_process_exe(pid, &t) >= 0) {
+                core_exe = strappend("COREDUMP_EXE=", t);
+                free(t);
+
+                if (core_exe)
+                        IOVEC_SET_STRING(iovec[j++], core_exe);
+        }
+
+        if (get_process_cmdline(pid, LINE_MAX, false, &t) >= 0) {
+                core_cmdline = strappend("COREDUMP_CMDLINE=", t);
+                free(t);
+
+                if (core_cmdline)
+                        IOVEC_SET_STRING(iovec[j++], core_cmdline);
+        }
+
+        core_timestamp = join("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
+        if (core_timestamp)
+                IOVEC_SET_STRING(iovec[j++], core_timestamp);
+
+        IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
+        IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
+
+        core_message = join("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
+        if (core_message)
+                IOVEC_SET_STRING(iovec[j++], core_message);
+
+        r = sd_journal_sendv(iovec, j);
+        if (r < 0)
+                log_error("Failed to send coredump: %s", strerror(-r));
+
+finish:
+        free(p);
+        free(core_pid);
+        free(core_uid);
+        free(core_gid);
+        free(core_signal);
+        free(core_timestamp);
+        free(core_comm);
+        free(core_exe);
+        free(core_cmdline);
+        free(core_unit);
+        free(core_session);
+        free(core_message);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 5fd1aa7..c5ac3ab 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -32,6 +32,8 @@ extern "C" {
 #define SD_MESSAGE_JOURNAL_STOP    SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
 #define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
 
+#define SD_MESSAGE_COREDUMP        SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/sysctl.d/.gitignore b/sysctl.d/.gitignore
new file mode 100644
index 0000000..7563539
--- /dev/null
+++ b/sysctl.d/.gitignore
@@ -0,0 +1 @@
+/coredump.conf
diff --git a/sysctl.d/Makefile b/sysctl.d/Makefile
new file mode 120000
index 0000000..bd10475
--- /dev/null
+++ b/sysctl.d/Makefile
@@ -0,0 +1 @@
+../src/Makefile
\ No newline at end of file
diff --git a/sysctl.d/coredump.conf.in b/sysctl.d/coredump.conf.in
new file mode 100644
index 0000000..ab19b1e
--- /dev/null
+++ b/sysctl.d/coredump.conf.in
@@ -0,0 +1,10 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+
+# See sysctl.d(5) for details
+
+kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %p %u %g %s %t %e

commit 755a02c6800246e7e293897d0594fe7e7531ba59
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 13 23:17:54 2012 +0100

    journal: add new system-cat tool as kind of a more powerfull BSD logger

diff --git a/.gitignore b/.gitignore
index 8daf8f0..c260210 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/systemd-cat
 /systemd-rc-local-generator
 /libsystemd-id128.pc
 systemd-journalctl
diff --git a/Makefile.am b/Makefile.am
index 42da4fb..d94de56 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1237,6 +1237,13 @@ systemd_journald_LDADD += \
 	$(XZ_LIBS)
 endif
 
+systemd_cat_SOURCES = \
+        src/journal/cat.c
+
+systemd_cat_LDADD = \
+	libsystemd-basic.la \
+	libsystemd-journal.la
+
 systemd_journalctl_SOURCES = \
 	src/journal/journalctl.c \
 	src/pager.c \
@@ -1345,6 +1352,9 @@ rootlibexec_PROGRAMS += \
 rootbin_PROGRAMS += \
 	systemd-journalctl
 
+bin_PROGRAMS += \
+        systemd-cat
+
 dist_systemunit_DATA += \
 	units/systemd-journald.socket
 
diff --git a/src/journal/cat.c b/src/journal/cat.c
new file mode 100644
index 0000000..6745f1c
--- /dev/null
+++ b/src/journal/cat.c
@@ -0,0 +1,181 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <getopt.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+
+#include <systemd/sd-journal.h>
+
+#include "util.h"
+#include "build.h"
+
+static char *arg_identifier = NULL;
+static char arg_priority = LOG_INFO;
+static bool arg_level_prefix = true;
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+               "Execute process with stdout/stderr connected to the journal.\n\n"
+               "  -h --help               Show this help\n"
+               "     --version            Show package version\n"
+               "  -t --identifier=STRING  Set syslog identifier\n"
+               "  -p --priority=PRIORITY  Set priority value (0..7)\n"
+               "     --level-prefix=BOOL  Control whether level prefix shall be parsed\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_LEVEL_PREFIX
+        };
+
+        static const struct option options[] = {
+                { "help",         no_argument,       NULL, 'h'              },
+                { "version" ,     no_argument,       NULL, ARG_VERSION      },
+                { "identifier",   required_argument, NULL, 't'              },
+                { "priority",     required_argument, NULL, 'p'              },
+                { "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX },
+                { NULL,           0,                 NULL, 0                }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(DISTRIBUTION);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
+                case 't':
+                        free(arg_identifier);
+                        if (isempty(optarg))
+                                arg_identifier = NULL;
+                        else {
+                                arg_identifier = strdup(optarg);
+                                if (!arg_identifier) {
+                                        log_error("Out of memory.");
+                                        return -ENOMEM;
+                                }
+                        }
+                        break;
+
+                case 'p':
+                        arg_priority = log_level_from_string(optarg);
+                        if (arg_priority < 0) {
+                                log_error("Failed to parse priority value.");
+                                return arg_priority;
+                        }
+                        break;
+
+                case ARG_LEVEL_PREFIX: {
+                        int k;
+
+                        k = parse_boolean(optarg);
+                        if (k < 0) {
+                                log_error("Failed to parse level prefix value.");
+                                return k;
+                        }
+                        arg_level_prefix = k;
+                        break;
+                }
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r, fd = -1, saved_stderr = -1;
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                goto finish;
+
+        fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix);
+        if (fd < 0) {
+                log_error("Failed to create stream fd: %s", strerror(fd));
+                r = fd;
+                goto finish;
+        }
+
+        saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3);
+
+        if (dup3(fd, STDOUT_FILENO, 0) < 0 ||
+            dup3(fd, STDERR_FILENO, 0) < 0) {
+                log_error("Failed to duplicate fd: %s", strerror(fd));
+                r = -errno;
+                goto finish;
+        }
+
+        if (fd >= 3)
+                close_nointr_nofail(fd);
+
+        fd = -1;
+
+        if (argc <= optind)
+                execl("/bin/cat", "/bin/cat", NULL);
+        else
+                execvp(argv[optind], argv + optind);
+
+        /* Let's try to restore a working stderr, so we can print the error message */
+        if (saved_stderr >= 0)
+                dup3(saved_stderr, STDERR_FILENO, 0);
+
+        log_error("Failed to execute process: %m");
+        r = -errno;
+
+finish:
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        if (saved_stderr >= 0)
+                close_nointr_nofail(saved_stderr);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}

commit 3043935f02da2e680b37cf282587106ad05440ee
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 13 21:56:28 2012 +0100

    util: split out tty_is_vc_resolve() from default_term_for_tty()

diff --git a/src/util.c b/src/util.c
index a6cdfd5..7450565 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4402,31 +4402,37 @@ int vtnr_from_tty(const char *tty) {
         return i;
 }
 
-const char *default_term_for_tty(const char *tty) {
+bool tty_is_vc_resolve(const char *tty) {
         char *active = NULL;
-        const char *term;
+        bool b;
 
         assert(tty);
 
         if (startswith(tty, "/dev/"))
                 tty += 5;
 
-        /* Resolve where /dev/console is pointing when determining
-         * TERM */
+        /* Resolve where /dev/console is pointing to */
         if (streq(tty, "console"))
                 if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
                         /* If multiple log outputs are configured the
                          * last one is what /dev/console points to */
-                        if ((tty = strrchr(active, ' ')))
+                        tty = strrchr(active, ' ');
+                        if (tty)
                                 tty++;
                         else
                                 tty = active;
                 }
 
-        term = tty_is_vc(tty) ? "TERM=linux" : "TERM=vt100";
+        b = tty_is_vc(tty);
         free(active);
 
-        return term;
+        return b;
+}
+
+const char *default_term_for_tty(const char *tty) {
+        assert(tty);
+
+        return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100";
 }
 
 bool dirent_is_file(const struct dirent *de) {
diff --git a/src/util.h b/src/util.h
index a52ac64..e6ffad6 100644
--- a/src/util.h
+++ b/src/util.h
@@ -411,6 +411,7 @@ char *fstab_node_to_udev_node(const char *p);
 void filter_environ(const char *prefix);
 
 bool tty_is_vc(const char *tty);
+bool tty_is_vc_resolve(const char *tty);
 int vtnr_from_tty(const char *tty);
 const char *default_term_for_tty(const char *tty);
 

commit c1072ea0dade39a4188de5e511adfffd4ba8e42c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 13 21:56:09 2012 +0100

    util: rework ANSI escape code macros

diff --git a/src/ask-password-api.c b/src/ask-password-api.c
index f57105c..ce2f3cb 100644
--- a/src/ask-password-api.c
+++ b/src/ask-password-api.c
@@ -89,10 +89,10 @@ int ask_password_tty(
                         goto finish;
                 }
 
-                loop_write(ttyfd, "\x1B[1m", 4, false);
+                loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false);
                 loop_write(ttyfd, message, strlen(message), false);
                 loop_write(ttyfd, " ", 1, false);
-                loop_write(ttyfd, "\x1B[0m", 4, false);
+                loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false);
 
                 new_termios = old_termios;
                 new_termios.c_lflag &= ~(ICANON|ECHO);
diff --git a/src/job.c b/src/job.c
index 1520d81..85f2dee 100644
--- a/src/job.c
+++ b/src/job.c
@@ -488,16 +488,16 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
                         break;
 
                 case JOB_FAILED:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
                         unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->meta.id);
                         break;
 
                 case JOB_DEPENDENCY:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
                         break;
 
                 case JOB_TIMEOUT:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
                         break;
 
                 default:
@@ -509,7 +509,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
                 switch (result) {
 
                 case JOB_TIMEOUT:
-                        unit_status_printf(u, ANSI_HIGHLIGHT_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
+                        unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
                         break;
 
                 case JOB_DONE:
diff --git a/src/log.c b/src/log.c
index 6caa5fa..79c63a1 100644
--- a/src/log.c
+++ b/src/log.c
@@ -344,7 +344,7 @@ static int write_to_console(
         }
 
         if (highlight)
-                IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_ON);
+                IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
         IOVEC_SET_STRING(iovec[n++], buffer);
         if (highlight)
                 IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
diff --git a/src/systemctl.c b/src/systemctl.c
index b72fcbf..d7d7d67 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -166,12 +166,12 @@ static void agent_open_if_enabled(void) {
         agent_open();
 }
 
-static const char *ansi_highlight(bool b) {
+static const char *ansi_highlight_red(bool b) {
 
         if (!on_tty())
                 return "";
 
-        return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
+        return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF;
 }
 
 static const char *ansi_highlight_green(bool b) {
@@ -383,14 +383,14 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
                 n_shown++;
 
                 if (streq(u->load_state, "error")) {
-                        on_loaded = ansi_highlight(true);
-                        off_loaded = ansi_highlight(false);
+                        on_loaded = ansi_highlight_red(true);
+                        off_loaded = ansi_highlight_red(false);
                 } else
                         on_loaded = off_loaded = "";
 
                 if (streq(u->active_state, "failed")) {
-                        on_active = ansi_highlight(true);
-                        off_active = ansi_highlight(false);
+                        on_active = ansi_highlight_red(true);
+                        off_active = ansi_highlight_red(false);
                 } else
                         on_active = off_active = "";
 
@@ -593,8 +593,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                 if (u->state == UNIT_FILE_MASKED ||
                     u->state == UNIT_FILE_MASKED_RUNTIME ||
                     u->state == UNIT_FILE_DISABLED) {
-                        on  = ansi_highlight(true);
-                        off = ansi_highlight(false);
+                        on  = ansi_highlight_red(true);
+                        off = ansi_highlight_red(false);
                 } else if (u->state == UNIT_FILE_ENABLED) {
                         on  = ansi_highlight_green(true);
                         off = ansi_highlight_green(false);
@@ -2069,8 +2069,8 @@ static void print_status_info(UnitStatusInfo *i) {
                 printf("\t  Follow: unit currently follows state of %s\n", i->following);
 
         if (streq_ptr(i->load_state, "error")) {
-                on = ansi_highlight(true);
-                off = ansi_highlight(false);
+                on = ansi_highlight_red(true);
+                off = ansi_highlight_red(false);
         } else
                 on = off = "";
 
@@ -2086,8 +2086,8 @@ static void print_status_info(UnitStatusInfo *i) {
         ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
 
         if (streq_ptr(i->active_state, "failed")) {
-                on = ansi_highlight(true);
-                off = ansi_highlight(false);
+                on = ansi_highlight_red(true);
+                off = ansi_highlight_red(false);
         } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
                 on = ansi_highlight_green(true);
                 off = ansi_highlight_green(false);
@@ -2163,8 +2163,8 @@ static void print_status_info(UnitStatusInfo *i) {
                         good = is_clean_exit(p->code, p->status);
 
                 if (!good) {
-                        on = ansi_highlight(true);
-                        off = ansi_highlight(false);
+                        on = ansi_highlight_red(true);
+                        off = ansi_highlight_red(false);
                 } else
                         on = off = "";
 
@@ -2274,8 +2274,8 @@ static void print_status_info(UnitStatusInfo *i) {
 
         if (i->need_daemon_reload)
                 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
-                       ansi_highlight(true),
-                       ansi_highlight(false),
+                       ansi_highlight_red(true),
+                       ansi_highlight_red(false),
                        arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
 }
 
diff --git a/src/util.c b/src/util.c
index 3179502..a6cdfd5 100644
--- a/src/util.c
+++ b/src/util.c
@@ -2467,14 +2467,14 @@ int ask(char *ret, const char *replies, const char *text, ...) {
                 bool need_nl = true;
 
                 if (on_tty)
-                        fputs("\x1B[1m", stdout);
+                        fputs(ANSI_HIGHLIGHT_ON, stdout);
 
                 va_start(ap, text);
                 vprintf(text, ap);
                 va_end(ap);
 
                 if (on_tty)
-                        fputs("\x1B[0m", stdout);
+                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
 
                 fflush(stdout);
 
diff --git a/src/util.h b/src/util.h
index 8de608f..a52ac64 100644
--- a/src/util.h
+++ b/src/util.h
@@ -68,7 +68,8 @@ typedef struct dual_timestamp {
 #define FORMAT_TIMESTAMP_PRETTY_MAX 256
 #define FORMAT_TIMESPAN_MAX 64
 
-#define ANSI_HIGHLIGHT_ON "\x1B[1;31m"
+#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;32m"
 #define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
 #define ANSI_HIGHLIGHT_OFF "\x1B[0m"
 



More information about the systemd-commits mailing list