[systemd-commits] 4 commits - configure.ac .gitignore Makefile.am man/journalctl.xml README src/journal src/shared src/systemctl units/.gitignore units/systemd-journal-gatewayd.service.in units/systemd-journal-gatewayd.socket

Lennart Poettering lennart at kemper.freedesktop.org
Thu Sep 27 15:55:49 PDT 2012


 .gitignore                                |    2 
 Makefile.am                               |   37 +
 README                                    |    3 
 configure.ac                              |   13 
 man/journalctl.xml                        |    9 
 src/journal/journal-gatewayd.c            |  623 ++++++++++++++++++++++++++++++
 src/journal/journalctl.c                  |   26 -
 src/journal/sd-journal.c                  |   12 
 src/shared/logs-show.c                    |  328 ++++++++-------
 src/shared/logs-show.h                    |    9 
 src/shared/util.c                         |    3 
 src/systemctl/systemctl.c                 |    8 
 units/.gitignore                          |    1 
 units/systemd-journal-gatewayd.service.in |   16 
 units/systemd-journal-gatewayd.socket     |   15 
 15 files changed, 937 insertions(+), 168 deletions(-)

New commits:
commit 7b17a7d72f5ba5ad838b19803534c56a46f3bce9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Sep 28 00:46:32 2012 +0200

    journal: add minimal journal gateway daemon based on GNU libmicrohttpd
    
    This minimal HTTP server can serve journal data via HTTP. Its primary
    purpose is synchronization of journal data across the network. It serves
    journal data in three formats:
    
           text/plain: the text format known from /var/log/messages
           application/json: the journal entries formatted as JSON
           application/vnd.fdo.journal: the binary export format of the journal
    
    The HTTP server also serves a small HTML5 app that makes use of the JSON
    serialization to present the journal data to the user.
    
    Examples:
    
    This downloads the journal in text format:
    
     # systemctl start systemd-journal-gatewayd.service
     # wget http://localhost:19531/entries
    
    Same for JSON:
    
     # curl -H"Accept: application/json" http://localhost:19531/entries
    
    Access via web browser:
    
     $ firefox http://localhost:19531/

diff --git a/.gitignore b/.gitignore
index db7fcdf..13d2df4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+/install-tree
+/systemd-journal-gatewayd
 /test-mmap-cache
 /test-unit-file
 /test-log
diff --git a/Makefile.am b/Makefile.am
index be92356..f724998 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2651,6 +2651,43 @@ EXTRA_DIST += \
 CLEANFILES += \
 	src/journal/journald-gperf.c
 
+if HAVE_MICROHTTPD
+
+gatewayddocumentrootdir=$(pkgdatadir)/gatewayd
+
+rootlibexec_PROGRAMS += \
+	systemd-journal-gatewayd
+
+systemd_journal_gatewayd_SOURCES = \
+	src/journal/journal-gatewayd.c
+
+systemd_journal_gatewayd_LDADD = \
+	libsystemd-shared.la \
+	libsystemd-logs.la \
+	libsystemd-journal-internal.la \
+	libsystemd-id128-internal.la \
+	libsystemd-daemon.la \
+	$(MICROHTTPD_LIBS)
+
+systemd_journal_gatewayd_CFLAGS = \
+	-DDOCUMENT_ROOT=\"$(gatewayddocumentrootdir)\" \
+	$(AM_CFLAGS) \
+	$(MICROHTTPD_CFLAGS)
+
+EXTRA_DIST += \
+	units/systemd-journal-gatewayd.service.in
+
+dist_systemunit_DATA += \
+	units/systemd-journal-gatewayd.socket
+
+nodist_systemunit_DATA += \
+	units/systemd-journal-gatewayd.service
+
+dist_gatewayddocumentroot_DATA = \
+	src/journal/browse.html
+
+endif
+
 # ------------------------------------------------------------------------------
 if ENABLE_COREDUMP
 systemd_coredump_SOURCES = \
diff --git a/README b/README
index 334c597..84ca3c0 100644
--- a/README
+++ b/README
@@ -48,6 +48,9 @@ REQUIREMENTS:
         libselinux (optional)
         liblzma (optional)
         tcpwrappers (optional)
+        libgcrypt (optional)
+        libqrencode (optional)
+        libmicrohttpd (optional)
 
         When you build from git you need the following additional dependencies:
 
diff --git a/configure.ac b/configure.ac
index 34eb5a8..79ce594 100644
--- a/configure.ac
+++ b/configure.ac
@@ -425,6 +425,18 @@ fi
 AM_CONDITIONAL(HAVE_QRENCODE, [test "$have_qrencode" = "yes"])
 
 # ------------------------------------------------------------------------------
+have_microhttpd=no
+AC_ARG_ENABLE(microhttpd, AS_HELP_STRING([--disable-microhttpd], [disable microhttpd support]))
+if test "x$enable_microhttpd" != "xno"; then
+        PKG_CHECK_MODULES(MICROHTTPD, [ libmicrohttpd ],
+                [AC_DEFINE(HAVE_MICROHTTPD, 1, [Define if microhttpd is available]) have_microhttpd=yes], have_microhttpd=no)
+        if test "x$have_microhttpd" = xno -a "x$enable_microhttpd" = xyes; then
+                AC_MSG_ERROR([*** microhttpd support requested but libraries not found])
+        fi
+fi
+AM_CONDITIONAL(HAVE_MICROHTTPD, [test "$have_microhttpd" = "yes"])
+
+# ------------------------------------------------------------------------------
 have_binfmt=no
 AC_ARG_ENABLE(binfmt, AS_HELP_STRING([--disable-binfmt], [disable binfmt tool]))
 if test "x$enable_binfmt" != "xno"; then
@@ -803,6 +815,7 @@ AC_MSG_RESULT([
         ACL:                     ${have_acl}
         GCRYPT:                  ${have_gcrypt}
         QRENCODE:                ${have_qrencode}
+        MICROHTTPD:              ${have_microhttpd}
         binfmt:                  ${have_binfmt}
         vconsole:                ${have_vconsole}
         readahead:               ${have_readahead}
diff --git a/src/journal/journal-gatewayd.c b/src/journal/journal-gatewayd.c
new file mode 100644
index 0000000..b7acfba
--- /dev/null
+++ b/src/journal/journal-gatewayd.c
@@ -0,0 +1,623 @@
+/*-*- 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 Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <microhttpd.h>
+
+#include "log.h"
+#include "util.h"
+#include "sd-journal.h"
+#include "sd-daemon.h"
+#include "logs-show.h"
+#include "virt.h"
+
+typedef struct RequestMeta {
+        sd_journal *journal;
+
+        OutputMode mode;
+
+        char *cursor;
+        int64_t n_skip;
+        uint64_t n_entries;
+        bool n_entries_set;
+
+        FILE *tmp;
+        uint64_t delta, size;
+} RequestMeta;
+
+static const char* const mime_types[_OUTPUT_MODE_MAX] = {
+        [OUTPUT_SHORT] = "text/plain",
+        [OUTPUT_JSON] = "application/json",
+        [OUTPUT_EXPORT] = "application/vnd.fdo.journal"
+};
+
+static RequestMeta *request_meta(void **connection_cls) {
+        RequestMeta *m;
+
+        if (*connection_cls)
+                return *connection_cls;
+
+        m = new0(RequestMeta, 1);
+        if (!m)
+                return NULL;
+
+        *connection_cls = m;
+        return m;
+}
+
+static void request_meta_free(
+                void *cls,
+                struct MHD_Connection *connection,
+                void **connection_cls,
+                enum MHD_RequestTerminationCode toe) {
+
+        RequestMeta *m = *connection_cls;
+
+        if (!m)
+                return;
+
+        if (m->journal)
+                sd_journal_close(m->journal);
+
+        if (m->tmp)
+                fclose(m->tmp);
+
+        free(m->cursor);
+        free(m);
+}
+
+static int open_journal(RequestMeta *m) {
+        assert(m);
+
+        if (m->journal)
+                return 0;
+
+        return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
+}
+
+
+static int respond_oom(struct MHD_Connection *connection) {
+        struct MHD_Response *response;
+        const char m[] = "Out of memory.\n";
+        int ret;
+
+        assert(connection);
+
+        response = MHD_create_response_from_buffer(sizeof(m)-1, (char*) m, MHD_RESPMEM_PERSISTENT);
+        if (!response)
+                return MHD_NO;
+
+        MHD_add_response_header(response, "Content-Type", "text/plain");
+        ret = MHD_queue_response(connection, MHD_HTTP_SERVICE_UNAVAILABLE, response);
+        MHD_destroy_response(response);
+
+        return ret;
+}
+
+static int respond_error(
+                struct MHD_Connection *connection,
+                unsigned code,
+                const char *format, ...) {
+
+        struct MHD_Response *response;
+        char *m;
+        int r;
+        va_list ap;
+
+        assert(connection);
+        assert(format);
+
+        va_start(ap, format);
+        r = vasprintf(&m, format, ap);
+        va_end(ap);
+
+        if (r < 0)
+                return respond_oom(connection);
+
+        response = MHD_create_response_from_buffer(strlen(m), m, MHD_RESPMEM_MUST_FREE);
+        if (!response) {
+                free(m);
+                return respond_oom(connection);
+        }
+
+        MHD_add_response_header(response, "Content-Type", "text/plain");
+        r = MHD_queue_response(connection, code, response);
+        MHD_destroy_response(response);
+
+        return r;
+}
+
+static ssize_t request_reader_entries(
+                void *cls,
+                uint64_t pos,
+                char *buf,
+                size_t max) {
+
+        RequestMeta *m = cls;
+        int r;
+        size_t n, k;
+
+        assert(m);
+        assert(buf);
+        assert(max > 0);
+        assert(pos >= m->delta);
+
+        pos -= m->delta;
+
+        while (pos >= m->size) {
+                off_t sz;
+
+                /* End of this entry, so let's serialize the next
+                 * one */
+
+                if (m->n_entries_set &&
+                    m->n_entries <= 0)
+                        return MHD_CONTENT_READER_END_OF_STREAM;
+
+                if (m->n_skip < 0) {
+                        r = sd_journal_previous_skip(m->journal, (uint64_t) -m->n_skip);
+
+                        /* We couldn't seek this far backwards? Then
+                         * let's try to look forward... */
+                        if (r == 0)
+                                r = sd_journal_next(m->journal);
+
+                } else if (m->n_skip > 0)
+                        r = sd_journal_next_skip(m->journal, (uint64_t) m->n_skip + 1);
+                else
+                        r = sd_journal_next(m->journal);
+
+                if (r < 0) {
+                        log_error("Failed to advance journal pointer: %s", strerror(-r));
+                        return MHD_CONTENT_READER_END_WITH_ERROR;
+                } else if (r == 0)
+                        return MHD_CONTENT_READER_END_OF_STREAM;
+
+                pos -= m->size;
+                m->delta += m->size;
+
+                if (m->n_entries_set)
+                        m->n_entries -= 1;
+
+                m->n_skip = 0;
+
+                if (m->tmp)
+                        rewind(m->tmp);
+                else {
+                        m->tmp = tmpfile();
+                        if (!m->tmp) {
+                                log_error("Failed to create temporary file: %m");
+                                return MHD_CONTENT_READER_END_WITH_ERROR;;
+                        }
+                }
+
+                r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH);
+                if (r < 0) {
+                        log_error("Failed to serialize item: %s", strerror(-r));
+                        return MHD_CONTENT_READER_END_WITH_ERROR;
+                }
+
+                sz = ftello(m->tmp);
+                if (sz == (off_t) -1) {
+                        log_error("Failed to retrieve file position: %m");
+                        return MHD_CONTENT_READER_END_WITH_ERROR;
+                }
+
+                m->size = (uint64_t) sz;
+        }
+
+        if (fseeko(m->tmp, pos, SEEK_SET) < 0) {
+                log_error("Failed to seek to position: %m");
+                return MHD_CONTENT_READER_END_WITH_ERROR;
+        }
+
+        n = m->size - pos;
+        if (n > max)
+                n = max;
+
+        errno = 0;
+        k = fread(buf, 1, n, m->tmp);
+        if (k != n) {
+                log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF");
+                return MHD_CONTENT_READER_END_WITH_ERROR;
+        }
+
+        return (ssize_t) k;
+}
+
+static int request_parse_accept(
+                RequestMeta *m,
+                struct MHD_Connection *connection) {
+
+        const char *accept;
+
+        assert(m);
+        assert(connection);
+
+        accept = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept");
+        if (!accept)
+                return 0;
+
+        if (streq(accept, mime_types[OUTPUT_JSON]))
+                m->mode = OUTPUT_JSON;
+        else if (streq(accept, mime_types[OUTPUT_EXPORT]))
+                m->mode = OUTPUT_EXPORT;
+        else
+                m->mode = OUTPUT_SHORT;
+
+        return 0;
+}
+
+static int request_parse_range(
+                RequestMeta *m,
+                struct MHD_Connection *connection) {
+
+        const char *range, *colon, *colon2;
+        int r;
+
+        assert(m);
+        assert(connection);
+
+        range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range");
+        if (!range)
+                return 0;
+
+        if (!startswith(range, "entries="))
+                return 0;
+
+        range += 8;
+        range += strspn(range, WHITESPACE);
+
+        colon = strchr(range, ':');
+        if (!colon)
+                m->cursor = strdup(range);
+        else {
+                const char *p;
+
+                colon2 = strchr(colon + 1, ':');
+                if (colon2) {
+                        char *t;
+
+                        t = strndup(colon + 1, colon2 - colon - 1);
+                        if (!t)
+                                return -ENOMEM;
+
+                        r = safe_atoi64(t, &m->n_skip);
+                        free(t);
+                        if (r < 0)
+                                return r;
+                }
+
+                p = (colon2 ? colon2 : colon) + 1;
+                if (*p) {
+                        r = safe_atou64(p, &m->n_entries);
+                        if (r < 0)
+                                return r;
+
+                        if (m->n_entries <= 0)
+                                return -EINVAL;
+
+                        m->n_entries_set = true;
+                }
+
+                m->cursor = strndup(range, colon - range);
+        }
+
+        if (!m->cursor)
+                return -ENOMEM;
+
+        m->cursor[strcspn(m->cursor, WHITESPACE)] = 0;
+        if (isempty(m->cursor)) {
+                free(m->cursor);
+                m->cursor = NULL;
+        }
+
+        return 0;
+}
+
+static int request_handler_entries(
+                struct MHD_Connection *connection,
+                void **connection_cls) {
+
+        struct MHD_Response *response;
+        RequestMeta *m;
+        int r;
+
+        assert(connection);
+        assert(connection_cls);
+
+        m = request_meta(connection_cls);
+        if (!m)
+                return respond_oom(connection);
+
+        r = open_journal(m);
+        if (r < 0)
+                return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
+
+        if (request_parse_accept(m, connection) < 0)
+                return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n");
+
+        if (request_parse_range(m, connection) < 0)
+                return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n");
+
+        /* log_info("cursor = %s", m->cursor); */
+        /* log_info("skip = %lli", m->n_skip); */
+        /* if (!m->n_entries_set) */
+        /*         log_info("n_entries not set!"); */
+        /* else */
+        /*         log_info("n_entries = %llu", m->n_entries); */
+
+        if (m->cursor)
+                r = sd_journal_seek_cursor(m->journal, m->cursor);
+        else if (m->n_skip >= 0)
+                r = sd_journal_seek_head(m->journal);
+        else if (m->n_skip < 0)
+                r = sd_journal_seek_tail(m->journal);
+        if (r < 0)
+                return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.\n");
+
+        response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL);
+        if (!response)
+                return respond_oom(connection);
+
+        MHD_add_response_header(response, "Content-Type", mime_types[m->mode]);
+
+        r = MHD_queue_response(connection, MHD_HTTP_OK, response);
+        MHD_destroy_response(response);
+
+        return r;
+}
+
+static int request_handler_redirect(
+                struct MHD_Connection *connection,
+                const char *target) {
+
+        char *page;
+        struct MHD_Response *response;
+        int ret;
+
+        assert(connection);
+        assert(page);
+
+        if (asprintf(&page, "<html><body>Please continue to the <a href=\"%s\">journal browser</a>.</body></html>", target) < 0)
+                return respond_oom(connection);
+
+        response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE);
+        if (!response) {
+                free(page);
+                return respond_oom(connection);
+        }
+
+        MHD_add_response_header(response, "Content-Type", "text/html");
+        MHD_add_response_header(response, "Location", target);
+
+        ret = MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response);
+        MHD_destroy_response(response);
+
+        return ret;
+}
+
+static int request_handler_file(
+                struct MHD_Connection *connection,
+                const char *path,
+                const char *mime_type) {
+
+        struct MHD_Response *response;
+        int ret;
+        _cleanup_close_ int fd = -1;
+        struct stat st;
+
+        assert(connection);
+        assert(path);
+        assert(mime_type);
+
+        fd = open(path, O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return respond_error(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path);
+
+        if (fstat(fd, &st) < 0)
+                return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n");
+
+        response = MHD_create_response_from_fd_at_offset(st.st_size, fd, 0);
+        if (!response)
+                return respond_oom(connection);
+
+        fd = -1;
+
+        MHD_add_response_header(response, "Content-Type", mime_type);
+
+        ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
+        MHD_destroy_response(response);
+
+        return ret;
+}
+
+static int request_handler_machine(
+                struct MHD_Connection *connection,
+                void **connection_cls) {
+
+        struct MHD_Response *response;
+        RequestMeta *m;
+        int r;
+        _cleanup_free_ char* hostname = NULL, *os_name = NULL;
+        uint64_t cutoff_from, cutoff_to, usage;
+        char *json;
+        sd_id128_t mid, bid;
+        const char *v = "bare";
+
+        assert(connection);
+
+        m = request_meta(connection_cls);
+        if (!m)
+                return respond_oom(connection);
+
+        r = open_journal(m);
+        if (r < 0)
+                return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
+
+        r = sd_id128_get_machine(&mid);
+        if (r < 0)
+                return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r));
+
+        r = sd_id128_get_boot(&bid);
+        if (r < 0)
+                return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r));
+
+        hostname = gethostname_malloc();
+        if (!hostname)
+                return respond_oom(connection);
+
+        r = sd_journal_get_usage(m->journal, &usage);
+        if (r < 0)
+                return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
+
+        r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to);
+        if (r < 0)
+                return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
+
+        parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL);
+
+        detect_virtualization(&v);
+
+        r = asprintf(&json,
+                     "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\","
+                     "\"boot_id\" : \"" SD_ID128_FORMAT_STR "\","
+                     "\"hostname\" : \"%s\","
+                     "\"os_pretty_name\" : \"%s\","
+                     "\"virtualization\" : \"%s\","
+                     "\"usage\" : \"%llu\","
+                     "\"cutoff_from_realtime\" : \"%llu\","
+                     "\"cutoff_to_realtime\" : \"%llu\" }\n",
+                     SD_ID128_FORMAT_VAL(mid),
+                     SD_ID128_FORMAT_VAL(bid),
+                     hostname_cleanup(hostname),
+                     os_name ? os_name : "Linux",
+                     v,
+                     (unsigned long long) usage,
+                     (unsigned long long) cutoff_from,
+                     (unsigned long long) cutoff_to);
+
+        if (r < 0)
+                return respond_oom(connection);
+
+        response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE);
+        if (!response) {
+                free(json);
+                return respond_oom(connection);
+        }
+
+        MHD_add_response_header(response, "Content-Type", "application/json");
+        r = MHD_queue_response(connection, MHD_HTTP_OK, response);
+        MHD_destroy_response(response);
+
+        return r;
+}
+
+static int request_handler(
+                void *cls,
+                struct MHD_Connection *connection,
+                const char *url,
+                const char *method,
+                const char *version,
+                const char *upload_data,
+                size_t *upload_data_size,
+                void **connection_cls) {
+
+        assert(connection);
+        assert(url);
+        assert(method);
+
+        if (!streq(method, "GET"))
+                return MHD_NO;
+
+        if (streq(url, "/"))
+                return request_handler_redirect(connection, "/browse");
+
+        if (streq(url, "/entries"))
+                return request_handler_entries(connection, connection_cls);
+
+        if (streq(url, "/browse"))
+                return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html");
+
+        if (streq(url, "/machine"))
+                return request_handler_machine(connection, connection_cls);
+
+        return respond_error(connection, MHD_HTTP_NOT_FOUND, "Not found.\n");
+}
+
+int main(int argc, char *argv[]) {
+        struct MHD_Daemon *daemon = NULL;
+        int r = EXIT_FAILURE, n;
+
+        if (argc > 1) {
+                log_error("This program does not take arguments.");
+                goto finish;
+        }
+
+        log_set_target(LOG_TARGET_KMSG);
+        log_parse_environment();
+        log_open();
+
+        n = sd_listen_fds(1);
+        if (n < 0) {
+                log_error("Failed to determine passed sockets: %s", strerror(-n));
+                goto finish;
+        } else if (n > 1) {
+                log_error("Can't listen on more than one socket.");
+                goto finish;
+        } else if (n > 0) {
+                daemon = MHD_start_daemon(
+                                MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG,
+                                19531,
+                                NULL, NULL,
+                                request_handler, NULL,
+                                MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START,
+                                MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL,
+                                MHD_OPTION_END);
+        } else {
+                daemon = MHD_start_daemon(
+                                MHD_USE_DEBUG|MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL,
+                                19531,
+                                NULL, NULL,
+                                request_handler, NULL,
+                                MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL,
+                                MHD_OPTION_END);
+        }
+
+        if (!daemon) {
+                log_error("Failed to start daemon!");
+                goto finish;
+        }
+
+        pause();
+
+        r = EXIT_SUCCESS;
+
+finish:
+        if (daemon)
+                MHD_stop_daemon(daemon);
+
+        return r;
+}
diff --git a/units/.gitignore b/units/.gitignore
index 74bff54..c72e2cb 100644
--- a/units/.gitignore
+++ b/units/.gitignore
@@ -1,3 +1,4 @@
+/systemd-journal-gatewayd.service
 /systemd-journal-flush.service
 /systemd-hibernate.service
 /systemd-suspend.service
diff --git a/units/systemd-journal-gatewayd.service.in b/units/systemd-journal-gatewayd.service.in
new file mode 100644
index 0000000..c3b5c72
--- /dev/null
+++ b/units/systemd-journal-gatewayd.service.in
@@ -0,0 +1,16 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Journal Gateway Service
+Requires=systemd-journal-gatewayd.socket
+
+[Service]
+ExecStart=@rootlibexecdir@/systemd-journal-gatewayd
+
+[Install]
+Also=systemd-journal-gatewayd.socket
diff --git a/units/systemd-journal-gatewayd.socket b/units/systemd-journal-gatewayd.socket
new file mode 100644
index 0000000..fd11058
--- /dev/null
+++ b/units/systemd-journal-gatewayd.socket
@@ -0,0 +1,15 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Journal Gateway Service Socket
+
+[Socket]
+ListenStream=19531
+
+[Install]
+WantedBy=sockets.target

commit be3ea5eaf24f4507efe88b450f751da860a9d21c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Sep 27 23:28:54 2012 +0200

    sd-journal: properly parse cursor strings

diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index acde84f..05a8445 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -983,35 +983,35 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
 
                 case 's':
                         seqnum_id_set = true;
-                        k = sd_id128_from_string(w+2, &seqnum_id);
+                        k = sd_id128_from_string(item+2, &seqnum_id);
                         break;
 
                 case 'i':
                         seqnum_set = true;
-                        if (sscanf(w+2, "%llx", &seqnum) != 1)
+                        if (sscanf(item+2, "%llx", &seqnum) != 1)
                                 k = -EINVAL;
                         break;
 
                 case 'b':
                         boot_id_set = true;
-                        k = sd_id128_from_string(w+2, &boot_id);
+                        k = sd_id128_from_string(item+2, &boot_id);
                         break;
 
                 case 'm':
                         monotonic_set = true;
-                        if (sscanf(w+2, "%llx", &monotonic) != 1)
+                        if (sscanf(item+2, "%llx", &monotonic) != 1)
                                 k = -EINVAL;
                         break;
 
                 case 't':
                         realtime_set = true;
-                        if (sscanf(w+2, "%llx", &realtime) != 1)
+                        if (sscanf(item+2, "%llx", &realtime) != 1)
                                 k = -EINVAL;
                         break;
 
                 case 'x':
                         xor_hash_set = true;
-                        if (sscanf(w+2, "%llx", &xor_hash) != 1)
+                        if (sscanf(item+2, "%llx", &xor_hash) != 1)
                                 k = -EINVAL;
                         break;
                 }

commit 08ace05beb1d09b6ebc5e9cafc2b972b39fa2437
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Sep 27 23:27:10 2012 +0200

    logs-show: various cleanups
    
    Among other cleanups this introduces a threshold for the size of binary
    blobs we serialize as integer arrays in the JSON output. THis can be
    disabled via --all.

diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index bac8729..cbf9833 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -730,7 +730,6 @@ static int verify(sd_journal *j) {
 int main(int argc, char *argv[]) {
         int r;
         sd_journal *j = NULL;
-        unsigned line = 0;
         bool need_seek = false;
         sd_id128_t previous_boot_id;
         bool previous_boot_id_valid = false;
@@ -904,9 +903,7 @@ int main(int argc, char *argv[]) {
                                 }
                         }
 
-                        line ++;
-
-                        r = output_journal(j, arg_output, line, 0, flags);
+                        r = output_journal(stdout, j, arg_output, 0, flags);
                         if (r < 0)
                                 goto finish;
 
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 2ddff15..725adb6 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -31,6 +31,7 @@
 #include "utf8.h"
 
 #define PRINT_THRESHOLD 128
+#define JSON_THRESHOLD 4096
 
 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
         size_t fl, nl;
@@ -63,8 +64,10 @@ static int parse_field(const void *data, size_t length, const char *field, char
         return 1;
 }
 
-static bool shall_print(bool show_all, char *p, size_t l) {
-        if (show_all)
+static bool shall_print(const char *p, size_t l, OutputFlags flags) {
+        assert(p);
+
+        if (flags & OUTPUT_SHOW_ALL)
                 return true;
 
         if (l > PRINT_THRESHOLD)
@@ -76,78 +79,82 @@ static bool shall_print(bool show_all, char *p, size_t l) {
         return true;
 }
 
-static int output_short(sd_journal *j, OutputMode mode, unsigned line, unsigned n_columns,
-                        OutputFlags flags) {
+static int output_short(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags) {
+
         int r;
         const void *data;
         size_t length;
         size_t n = 0;
-        char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
+        _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
         size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0;
         int p = LOG_INFO;
         const char *color_on = "", *color_off = "";
 
+        assert(f);
         assert(j);
 
         SD_JOURNAL_FOREACH_DATA(j, data, length) {
 
                 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "_PID=", &pid, &pid_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
                 else if (r > 0)
                         continue;
 
                 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
                 if (r < 0)
-                        goto finish;
+                        return r;
         }
 
-        if (!message) {
-                r = 0;
-                goto finish;
-        }
+        if (!message)
+                return 0;
 
         if (priority_len == 1 && *priority >= '0' && *priority <= '7')
                 p = *priority - '0';
@@ -166,12 +173,12 @@ static int output_short(sd_journal *j, OutputMode mode, unsigned line, unsigned
 
                 if (r < 0) {
                         log_error("Failed to get monotonic: %s", strerror(-r));
-                        goto finish;
+                        return r;
                 }
 
-                printf("[%5llu.%06llu]",
-                       (unsigned long long) (t / USEC_PER_SEC),
-                       (unsigned long long) (t % USEC_PER_SEC));
+                fprintf(f, "[%5llu.%06llu]",
+                        (unsigned long long) (t / USEC_PER_SEC),
+                        (unsigned long long) (t % USEC_PER_SEC));
 
                 n += 1 + 5 + 1 + 6 + 1;
 
@@ -191,42 +198,38 @@ static int output_short(sd_journal *j, OutputMode mode, unsigned line, unsigned
 
                 if (r < 0) {
                         log_error("Failed to get realtime: %s", strerror(-r));
-                        goto finish;
+                        return r;
                 }
 
                 t = (time_t) (x / USEC_PER_SEC);
                 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
                         log_error("Failed to format time.");
-                        goto finish;
+                        return r;
                 }
 
-                fputs(buf, stdout);
+                fputs(buf, f);
                 n += strlen(buf);
         }
 
-        if (hostname && shall_print(flags & OUTPUT_SHOW_ALL,
-                                    hostname, hostname_len)) {
-                printf(" %.*s", (int) hostname_len, hostname);
+        if (hostname && shall_print(hostname, hostname_len, flags)) {
+                fprintf(f, " %.*s", (int) hostname_len, hostname);
                 n += hostname_len + 1;
         }
 
-        if (identifier && shall_print(flags & OUTPUT_SHOW_ALL,
-                                      identifier, identifier_len)) {
-                printf(" %.*s", (int) identifier_len, identifier);
+        if (identifier && shall_print(identifier, identifier_len, flags)) {
+                fprintf(f, " %.*s", (int) identifier_len, identifier);
                 n += identifier_len + 1;
-        } else if (comm && shall_print(flags & OUTPUT_SHOW_ALL,
-                                       comm, comm_len)) {
-                printf(" %.*s", (int) comm_len, comm);
+        } else if (comm && shall_print(comm, comm_len, flags)) {
+                fprintf(f, " %.*s", (int) comm_len, comm);
                 n += comm_len + 1;
         } else
-                putchar(' ');
+                fputc(' ', f);
 
-        if (pid && shall_print(flags & OUTPUT_SHOW_ALL, pid, pid_len)) {
-                printf("[%.*s]", (int) pid_len, pid);
+        if (pid && shall_print(pid, pid_len, flags)) {
+                fprintf(f, "[%.*s]", (int) pid_len, pid);
                 n += pid_len + 2;
-        } else if (fake_pid && shall_print(flags & OUTPUT_SHOW_ALL,
-                                           fake_pid, fake_pid_len)) {
-                printf("[%.*s]", (int) fake_pid_len, fake_pid);
+        } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
+                fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
                 n += fake_pid_len + 2;
         }
 
@@ -240,46 +243,37 @@ static int output_short(sd_journal *j, OutputMode mode, unsigned line, unsigned
                 }
         }
 
-        if (flags & OUTPUT_SHOW_ALL)
-                printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
+        if (flags)
+                fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
         else if (!utf8_is_printable_n(message, message_len)) {
                 char bytes[FORMAT_BYTES_MAX];
-                printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
-        } else if ((flags & OUTPUT_FULL_WIDTH) ||
-                   (message_len + n + 1 < n_columns))
-                printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
+                fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
+        } else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns))
+                fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
         else if (n < n_columns && n_columns - n - 2 >= 3) {
                 char *e;
 
                 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
 
                 if (!e)
-                        printf(": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
+                        fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
                 else
-                        printf(": %s%s%s\n", color_on, e, color_off);
+                        fprintf(f, ": %s%s%s\n", color_on, e, color_off);
 
                 free(e);
         } else
-                fputs("\n", stdout);
-
-        r = 0;
+                fputs("\n", f);
 
-finish:
-        free(hostname);
-        free(identifier);
-        free(comm);
-        free(pid);
-        free(fake_pid);
-        free(message);
-        free(monotonic);
-        free(realtime);
-        free(priority);
-
-        return r;
+        return 0;
 }
 
-static int output_verbose(sd_journal *j, OutputMode mode, unsigned line,
-                          unsigned n_columns, OutputFlags flags) {
+static int output_verbose(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags) {
+
         const void *data;
         size_t length;
         char *cursor;
@@ -287,6 +281,7 @@ static int output_verbose(sd_journal *j, OutputMode mode, unsigned line,
         char ts[FORMAT_TIMESTAMP_MAX];
         int r;
 
+        assert(f);
         assert(j);
 
         r = sd_journal_get_realtime_usec(j, &realtime);
@@ -301,15 +296,14 @@ static int output_verbose(sd_journal *j, OutputMode mode, unsigned line,
                 return r;
         }
 
-        printf("%s [%s]\n",
-               format_timestamp(ts, sizeof(ts), realtime),
-               cursor);
+        fprintf(f, "%s [%s]\n",
+                format_timestamp(ts, sizeof(ts), realtime),
+                cursor);
 
         free(cursor);
 
         SD_JOURNAL_FOREACH_DATA(j, data, length) {
-                if (!(flags & OUTPUT_SHOW_ALL) && (length > PRINT_THRESHOLD ||
-                                  !utf8_is_printable_n(data, length))) {
+                if (!shall_print(data, length, flags)) {
                         const char *c;
                         char bytes[FORMAT_BYTES_MAX];
 
@@ -319,19 +313,24 @@ static int output_verbose(sd_journal *j, OutputMode mode, unsigned line,
                                 return -EINVAL;
                         }
 
-                        printf("\t%.*s=[%s blob data]\n",
+                        fprintf(f, "\t%.*s=[%s blob data]\n",
                                (int) (c - (const char*) data),
                                (const char*) data,
                                format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
                 } else
-                        printf("\t%.*s\n", (int) length, (const char*) data);
+                        fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
         }
 
         return 0;
 }
 
-static int output_export(sd_journal *j, OutputMode mode, unsigned line,
-                         unsigned n_columns, OutputFlags flags) {
+static int output_export(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags) {
+
         sd_id128_t boot_id;
         char sid[33];
         int r;
@@ -360,14 +359,15 @@ static int output_export(sd_journal *j, OutputMode mode, unsigned line,
                 return r;
         }
 
-        printf("__CURSOR=%s\n"
-               "__REALTIME_TIMESTAMP=%llu\n"
-               "__MONOTONIC_TIMESTAMP=%llu\n"
-               "_BOOT_ID=%s\n",
-               cursor,
-               (unsigned long long) realtime,
-               (unsigned long long) monotonic,
-               sd_id128_to_string(boot_id, sid));
+        fprintf(f,
+                "__CURSOR=%s\n"
+                "__REALTIME_TIMESTAMP=%llu\n"
+                "__MONOTONIC_TIMESTAMP=%llu\n"
+                "_BOOT_ID=%s\n",
+                cursor,
+                (unsigned long long) realtime,
+                (unsigned long long) monotonic,
+                sd_id128_to_string(boot_id, sid));
 
         free(cursor);
 
@@ -389,61 +389,80 @@ static int output_export(sd_journal *j, OutputMode mode, unsigned line,
                                 return -EINVAL;
                         }
 
-                        fwrite(data, c - (const char*) data, 1, stdout);
-                        fputc('\n', stdout);
+                        fwrite(data, c - (const char*) data, 1, f);
+                        fputc('\n', f);
                         le64 = htole64(length - (c - (const char*) data) - 1);
-                        fwrite(&le64, sizeof(le64), 1, stdout);
-                        fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
+                        fwrite(&le64, sizeof(le64), 1, f);
+                        fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
                 } else
-                        fwrite(data, length, 1, stdout);
+                        fwrite(data, length, 1, f);
 
-                fputc('\n', stdout);
+                fputc('\n', f);
         }
 
-        fputc('\n', stdout);
+        fputc('\n', f);
 
         return 0;
 }
 
-static void json_escape(const char* p, size_t l) {
-        if (!utf8_is_printable_n(p, l)) {
+static void json_escape(
+                FILE *f,
+                const char* p,
+                size_t l,
+                OutputFlags flags) {
+
+        assert(f);
+        assert(p);
+
+        if (!(flags & OUTPUT_SHOW_ALL) && l > JSON_THRESHOLD)
+
+                fputs("null", f);
+
+        else if (!utf8_is_printable_n(p, l)) {
                 bool not_first = false;
 
-                fputs("[ ", stdout);
+                fputs("[ ", f);
 
                 while (l > 0) {
                         if (not_first)
-                                printf(", %u", (uint8_t) *p);
+                                fprintf(f, ", %u", (uint8_t) *p);
                         else {
                                 not_first = true;
-                                printf("%u", (uint8_t) *p);
+                                fprintf(f, "%u", (uint8_t) *p);
                         }
 
                         p++;
                         l--;
                 }
 
-                fputs(" ]", stdout);
+                fputs(" ]", f);
         } else {
-                fputc('\"', stdout);
+                fputc('\"', f);
 
                 while (l > 0) {
                         if (*p == '"' || *p == '\\') {
-                                fputc('\\', stdout);
-                                fputc(*p, stdout);
-                        } else
-                                fputc(*p, stdout);
+                                fputc('\\', f);
+                                fputc(*p, f);
+                        } else if (*p < ' ')
+                                fprintf(f, "\\u%04x", *p);
+                        else
+                                fputc(*p, f);
 
                         p++;
                         l--;
                 }
 
-                fputc('\"', stdout);
+                fputc('\"', f);
         }
 }
 
-static int output_json(sd_journal *j, OutputMode mode, unsigned line,
-                       unsigned n_columns, OutputFlags flags) {
+static int output_json(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags) {
+
         uint64_t realtime, monotonic;
         char *cursor;
         const void *data;
@@ -473,24 +492,26 @@ static int output_json(sd_journal *j, OutputMode mode, unsigned line,
         }
 
         if (mode == OUTPUT_JSON_PRETTY)
-                printf("{\n"
-                       "\t\"__CURSOR\" : \"%s\",\n"
-                       "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
-                       "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
-                       "\t\"_BOOT_ID\" : \"%s\"",
-                       cursor,
-                       (unsigned long long) realtime,
-                       (unsigned long long) monotonic,
-                       sd_id128_to_string(boot_id, sid));
+                fprintf(f,
+                        "{\n"
+                        "\t\"__CURSOR\" : \"%s\",\n"
+                        "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
+                        "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
+                        "\t\"_BOOT_ID\" : \"%s\"",
+                        cursor,
+                        (unsigned long long) realtime,
+                        (unsigned long long) monotonic,
+                        sd_id128_to_string(boot_id, sid));
         else
-                printf("{ \"__CURSOR\" : \"%s\", "
-                       "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
-                       "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
-                       "\"_BOOT_ID\" : \"%s\"",
-                       cursor,
-                       (unsigned long long) realtime,
-                       (unsigned long long) monotonic,
-                       sd_id128_to_string(boot_id, sid));
+                fprintf(f,
+                        "{ \"__CURSOR\" : \"%s\", "
+                        "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
+                        "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
+                        "\"_BOOT_ID\" : \"%s\"",
+                        cursor,
+                        (unsigned long long) realtime,
+                        (unsigned long long) monotonic,
+                        sd_id128_to_string(boot_id, sid));
         free(cursor);
 
         SD_JOURNAL_FOREACH_DATA(j, data, length) {
@@ -509,30 +530,36 @@ static int output_json(sd_journal *j, OutputMode mode, unsigned line,
                 }
 
                 if (mode == OUTPUT_JSON_PRETTY)
-                        fputs(",\n\t", stdout);
+                        fputs(",\n\t", f);
                 else
-                        fputs(", ", stdout);
+                        fputs(", ", f);
 
-                json_escape(data, c - (const char*) data);
-                fputs(" : ", stdout);
-                json_escape(c + 1, length - (c - (const char*) data) - 1);
+                json_escape(f, data, c - (const char*) data, flags);
+                fputs(" : ", f);
+                json_escape(f, c + 1, length - (c - (const char*) data) - 1, flags);
         }
 
         if (mode == OUTPUT_JSON_PRETTY)
-                fputs("\n}\n", stdout);
+                fputs("\n}\n", f);
         else
-                fputs(" }\n", stdout);
+                fputs(" }\n", f);
 
         return 0;
 }
 
-static int output_cat(sd_journal *j, OutputMode mode, unsigned line,
-                      unsigned n_columns, OutputFlags flags) {
+static int output_cat(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags) {
+
         const void *data;
         size_t l;
         int r;
 
         assert(j);
+        assert(f);
 
         r = sd_journal_get_data(j, "MESSAGE", &data, &l);
         if (r < 0) {
@@ -546,14 +573,19 @@ static int output_cat(sd_journal *j, OutputMode mode, unsigned line,
 
         assert(l >= 8);
 
-        fwrite((const char*) data + 8, 1, l - 8, stdout);
-        putchar('\n');
+        fwrite((const char*) data + 8, 1, l - 8, f);
+        fputc('\n', f);
 
         return 0;
 }
 
-static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, OutputMode mode, unsigned line,
-                                             unsigned n_columns, OutputFlags flags) = {
+static int (*output_funcs[_OUTPUT_MODE_MAX])(
+                FILE *f,
+                sd_journal*j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags) = {
+
         [OUTPUT_SHORT] = output_short,
         [OUTPUT_SHORT_MONOTONIC] = output_short,
         [OUTPUT_VERBOSE] = output_verbose,
@@ -563,8 +595,13 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, OutputMode mode, unsi
         [OUTPUT_CAT] = output_cat
 };
 
-int output_journal(sd_journal *j, OutputMode mode, unsigned line,
-                   unsigned n_columns, OutputFlags flags) {
+int output_journal(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags) {
+
         int ret;
         assert(mode >= 0);
         assert(mode < _OUTPUT_MODE_MAX);
@@ -572,12 +609,13 @@ int output_journal(sd_journal *j, OutputMode mode, unsigned line,
         if (n_columns <= 0)
                 n_columns = columns();
 
-        ret = output_funcs[mode](j, mode, line, n_columns, flags);
+        ret = output_funcs[mode](f, j, mode, n_columns, flags);
         fflush(stdout);
         return ret;
 }
 
 int show_journal_by_unit(
+                FILE *f,
                 const char *unit,
                 OutputMode mode,
                 unsigned n_columns,
@@ -585,7 +623,7 @@ int show_journal_by_unit(
                 unsigned how_many,
                 OutputFlags flags) {
 
-        char *m1 = NULL, *m2 = NULL, *m3 = NULL;
+        _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
         sd_journal *j = NULL;
         int r;
         unsigned line = 0;
@@ -652,11 +690,6 @@ int show_journal_by_unit(
         if (r < 0)
                 goto finish;
 
-        if (mode == OUTPUT_JSON) {
-                fputc('[', stdout);
-                fflush(stdout);
-        }
-
         for (;;) {
                 for (;;) {
                         usec_t usec;
@@ -688,7 +721,7 @@ int show_journal_by_unit(
 
                         line ++;
 
-                        r = output_journal(j, mode, line, n_columns, flags);
+                        r = output_journal(f, j, mode, n_columns, flags);
                         if (r < 0)
                                 goto finish;
                 }
@@ -708,7 +741,7 @@ int show_journal_by_unit(
                                 goto finish;
 
                         if (r > 0 && not_before < cutoff)
-                                printf("Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
+                                fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
 
                         warn_cutoff = false;
                 }
@@ -722,14 +755,7 @@ int show_journal_by_unit(
 
         }
 
-        if (mode == OUTPUT_JSON)
-                fputs("\n]\n", stdout);
-
 finish:
-        free(m1);
-        free(m2);
-        free(m3);
-
         if (j)
                 sd_journal_close(j);
 
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 3e6b6e0..ea0f51f 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -47,10 +47,15 @@ typedef enum OutputFlags {
         OUTPUT_COLOR          = 1 << 4
 } OutputFlags;
 
-int output_journal(sd_journal *j, OutputMode mode, unsigned line,
-                   unsigned n_columns, OutputFlags flags);
+int output_journal(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags);
 
 int show_journal_by_unit(
+                FILE *f,
                 const char *unit,
                 OutputMode mode,
                 unsigned n_columns,
diff --git a/src/shared/util.c b/src/shared/util.c
index ed9b56e..d2ca3fc 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -1688,7 +1688,8 @@ char *xescape(const char *s, const char *bad) {
          * chars, in \xFF style escaping. May be reversed with
          * cunescape. */
 
-        if (!(r = new(char, strlen(s)*4+1)))
+        r = new(char, strlen(s) * 4 + 1);
+        if (!r)
                 return NULL;
 
         for (f = s, t = r; *f; f++) {
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index cc9c775..62c32cd 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2279,9 +2279,13 @@ static void print_status_info(UnitStatusInfo *i) {
                         on_tty() * OUTPUT_COLOR;
 
                 printf("\n");
-                show_journal_by_unit(i->id, arg_output, 0,
+                show_journal_by_unit(stdout,
+                                     i->id,
+                                     arg_output,
+                                     0,
                                      i->inactive_exit_timestamp_monotonic,
-                                     arg_lines, flags);
+                                     arg_lines,
+                                     flags);
         }
 
         if (i->need_daemon_reload)

commit 8f14c8327b1c2b578bbf1235723a77931c3d0223
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Sep 27 23:25:23 2012 +0200

    journalctl: add --cursor switch

diff --git a/man/journalctl.xml b/man/journalctl.xml
index 50c915d..651a8a5 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -255,6 +255,15 @@
                         </varlistentry>
 
                         <varlistentry>
+                                <term><option>--cursor=</option></term>
+                                <term><option>-c</option></term>
+
+                                <listitem><para>Jump to the location
+                                in the journal specified by the passed
+                                cursor.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><option>--directory=</option></term>
                                 <term><option>-D</option></term>
 
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 62bdcb7..bac8729 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -63,6 +63,7 @@ static bool arg_no_tail = false;
 static bool arg_quiet = false;
 static bool arg_merge = false;
 static bool arg_this_boot = false;
+static const char *arg_cursor = NULL;
 static const char *arg_directory = NULL;
 static int arg_priorities = 0xFF;
 static const char *arg_verify_key = NULL;
@@ -87,6 +88,7 @@ static int help(void) {
                "     --version           Show package version\n"
                "     --no-pager          Do not pipe output into a pager\n"
                "  -a --all               Show all fields, including long and unprintable\n"
+               "  -c --cursor=CURSOR     Jump to the specified cursor\n"
                "  -f --follow            Follow journal\n"
                "  -n --lines[=INTEGER]   Number of journal entries to show\n"
                "     --no-tail           Show all lines, even in follow mode\n"
@@ -148,6 +150,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "verify",       no_argument,       NULL, ARG_VERIFY       },
                 { "verify-key",   required_argument, NULL, ARG_VERIFY_KEY   },
                 { "disk-usage",   no_argument,       NULL, ARG_DISK_USAGE   },
+                { "cursor",       no_argument,       NULL, 'c'              },
                 { NULL,           0,                 NULL, 0                }
         };
 
@@ -156,7 +159,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -228,6 +231,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_directory = optarg;
                         break;
 
+                case 'c':
+                        arg_cursor = optarg;
+                        break;
+
                 case ARG_HEADER:
                         arg_action = ACTION_PRINT_HEADER;
                         break;
@@ -829,7 +836,16 @@ int main(int argc, char *argv[]) {
                 }
         }
 
-        if (arg_lines >= 0) {
+        if (arg_cursor) {
+                r = sd_journal_seek_cursor(j, arg_cursor);
+                if (r < 0) {
+                        log_error("Failed to seek to cursor: %s", strerror(-r));
+                        goto finish;
+                }
+
+                r = sd_journal_next(j);
+
+        } else if (arg_lines >= 0) {
                 r = sd_journal_seek_tail(j);
                 if (r < 0) {
                         log_error("Failed to seek to tail: %s", strerror(-r));
@@ -837,6 +853,7 @@ int main(int argc, char *argv[]) {
                 }
 
                 r = sd_journal_previous_skip(j, arg_lines);
+
         } else {
                 r = sd_journal_seek_head(j);
                 if (r < 0) {



More information about the systemd-commits mailing list