[systemd-commits] 4 commits - .gitignore Makefile.am TODO catalog/systemd.catalog man/journalctl.xml src/hostname src/journal src/shared src/systemd

Lennart Poettering lennart at kemper.freedesktop.org
Thu Nov 15 14:19:08 PST 2012


 .gitignore                         |    1 
 Makefile.am                        |   29 +
 TODO                               |    2 
 catalog/systemd.catalog            |   99 ++++++
 man/journalctl.xml                 |   39 ++
 src/hostname/hostnamectl.c         |    4 
 src/journal/catalog.c              |  584 +++++++++++++++++++++++++++++++++++++
 src/journal/catalog.h              |   28 +
 src/journal/journalctl.c           |   41 ++
 src/journal/libsystemd-journal.sym |    1 
 src/journal/sd-journal.c           |   60 +++
 src/journal/test-catalog.c         |   48 +++
 src/shared/logs-show.c             |   26 +
 src/shared/logs-show.h             |    3 
 src/shared/polkit.c                |   11 
 src/systemd/sd-journal.h           |    2 
 16 files changed, 965 insertions(+), 13 deletions(-)

New commits:
commit d4205751d4643c272059a3728045929dd0e5e800
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 15 23:03:31 2012 +0100

    journal: implement message catalog
    
    The message catalog can be used to attach short help texts to log lines,
    keyed by their MESSAGE_ID= fields. This is useful to help the
    administrator understand the context and cause of a message, find
    possible solutions and find further related documentation.
    
    Since this is keyed off MESSAGE_ID= this will only work for native
    journal messages.
    
    The message catalog supports i18n, and is useful to augment english
    language system messages with explanations in the local language.
    
    This commit only includes short explanatory messages for a few example
    message IDs, we'll add more complete documentation for the relevant
    systemd messages later on.

diff --git a/.gitignore b/.gitignore
index 3fbd83e..2291e5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/test-catalog
 /test-replace-var
 /test-journal-enum
 /test-sleep
diff --git a/Makefile.am b/Makefile.am
index 42fed59..3c59009 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -78,9 +78,10 @@ systemsleepdir=$(rootlibexecdir)/system-sleep
 systemunitdir=$(rootprefix)/lib/systemd/system
 systempresetdir=$(rootprefix)/lib/systemd/system-preset
 udevlibexecdir=$(rootprefix)/lib/udev
-udevhomedir = $(udevlibexecdir)
-udevrulesdir = $(udevlibexecdir)/rules.d
-udevhwdbdir = $(udevlibexecdir)/hwdb.d
+udevhomedir=$(udevlibexecdir)
+udevrulesdir=$(udevlibexecdir)/rules.d
+udevhwdbdir=$(udevlibexecdir)/hwdb.d
+catalogdir=$(prefix)/lib/systemd/catalog
 
 # And these are the special ones for /
 rootprefix=@rootprefix@
@@ -2565,6 +2566,15 @@ test_mmap_cache_LDADD = \
 	libsystemd-shared.la \
 	libsystemd-journal-internal.la
 
+test_catalog_SOURCES = \
+	src/journal/test-catalog.c
+
+test_catalog_LDADD = \
+	libsystemd-shared.la \
+	libsystemd-label.la \
+	libsystemd-journal-internal.la \
+	libsystemd-id128-internal.la
+
 libsystemd_journal_la_SOURCES = \
 	src/journal/sd-journal.c \
 	src/systemd/sd-journal.h \
@@ -2579,6 +2589,8 @@ libsystemd_journal_la_SOURCES = \
 	src/journal/journal-send.c \
 	src/journal/journal-def.h \
 	src/journal/compress.h \
+	src/journal/catalog.c \
+	src/journal/catalog.h \
 	src/journal/mmap-cache.c \
 	src/journal/mmap-cache.h
 
@@ -2594,6 +2606,7 @@ libsystemd_journal_la_LDFLAGS = \
 
 libsystemd_journal_la_LIBADD = \
 	libsystemd-shared.la \
+	libsystemd-label.la \
 	libsystemd-id128-internal.la
 
 libsystemd_journal_internal_la_SOURCES = \
@@ -2621,7 +2634,9 @@ libsystemd_journal_internal_la_LIBADD = \
 	libsystemd-label.la \
 	libsystemd-audit.la \
 	libsystemd-daemon.la \
-	libudev.la
+	libudev.la \
+	libsystemd-shared.la \
+	libsystemd-label.la
 
 nodist_libsystemd_journal_internal_la_SOURCES = \
 	src/journal/journald-gperf.c
@@ -2703,7 +2718,8 @@ noinst_PROGRAMS += \
 	test-journal-enum \
 	test-journal-stream \
 	test-journal-verify \
-	test-mmap-cache
+	test-mmap-cache \
+	test-catalog
 
 TESTS += \
 	test-journal \
@@ -2747,6 +2763,9 @@ dist_pkgsysconf_DATA += \
 pkgconfiglib_DATA += \
 	src/journal/libsystemd-journal.pc
 
+dist_catalog_DATA = \
+	catalog/systemd.catalog
+
 journal-install-data-hook:
 	$(MKDIR_P) -m 0755 \
 		$(DESTDIR)$(systemunitdir)/sockets.target.wants \
diff --git a/catalog/systemd.catalog b/catalog/systemd.catalog
new file mode 100644
index 0000000..91d0408
--- /dev/null
+++ b/catalog/systemd.catalog
@@ -0,0 +1,99 @@
+-- fc2e22bc6ee647b6b90729ab34a250b1
+Subject: Process @COREDUMP_PID@ (@COREDUMP_COMM@) dumped core
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+Process @COREDUMP_PID@ (@COREDUMP_COMM@) crashed and dumped core.
+
+This usually indicates a programming error in the crashing program and
+should be reported to the vendor as a bug.
+
+-- fc2e22bc6ee647b6b90729ab34a250b1 de
+Subject: Speicherabbild für Prozess @COREDUMP_PID@ (@COREDUMP_COMM) generiert
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+Prozess @COREDUMP_PID@ (@COREDUMP_COMM@) ist abgebrochen worden und
+ein Speicherabbild wurde generiert.
+
+Ãœblicherweise ist dies ein Hinweis auf einen Programmfehler und sollte
+als Fehler dem Hersteller gemeldet werden.
+
+-- c7a787079b354eaaa9e77b371893cd27
+Subject: Time change
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+The system clock has been changed.
+
+-- c7a787079b354eaaa9e77b371893cd27 de
+Subject: Zeitänderung
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+Die System-Zeit wurde geändert.
+
+-- 45f82f4aef7a4bbf942ce861d1f20990
+Subject: Time zone change
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+The system time zone has been changed.
+
+-- f77379a8490b408bbe5f6940505a777b
+Subject: The Journal has been started
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+The system journal process has been starting up, opened the journal
+files for writing and is now ready to process requests.
+
+-- d93fb3c9c24d451a97cea615ce59c00b
+Subject: The Journal has been stopped
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+The system journal process has shut down and closed all currently
+active journal files.
+
+-- fcbefc5da23d428093f97c82a9290f7b
+Subject: A new seat @SEAT_ID@ is now available
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+A new seat @SEAT_ID@ has been configured and is now available.
+
+-- 8d45620c1a4348dbb17410da57c60c66
+Subject: A new session @SESSION_ID@ has been created for user @USER_ID@
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+A new session with the ID @SESSION_ID@ has been created for the user @USER_ID at .
+
+The leading process of the session is @LEADER at .
+
+-- a596d6fe7bfa4994828e72309e95d61e
+Subject: Messages from a service have been suppressed
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Developer-Description: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+See: man:journald.conf(5)
+
+A service has logged too many messages within a time period. Messages
+from the service have been dropped.
+
+Note that only messages from the service in question have been
+dropped, other services' messages are unaffected.
+
+The limits when messages are dropped may be configured with
+RateLimitInterval= and RateLimitBurst= in
+/etc/systemd/journald.conf. See journald.conf(5) for details.
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 026bb22..e7fff0c 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -233,6 +233,25 @@
                         </varlistentry>
 
                         <varlistentry>
+                                <term><option>--catalog</option></term>
+                                <term><option>-x</option></term>
+
+                                <listitem><para>Augment log lines with
+                                explanation texts from the message
+                                catalog. This will add explanatory
+                                help texts to log messages in the
+                                output where this is available. These
+                                short help texts will explain the
+                                context of an error or log event,
+                                possible solutions, as well as
+                                pointers to support forums, developer
+                                documentation and any other relevant
+                                manuals. Note that help texts are not
+                                available for all messages but only
+                                for selected ones.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><option>--quiet</option></term>
                                 <term><option>-q</option></term>
 
@@ -405,6 +424,26 @@
                         </varlistentry>
 
                         <varlistentry>
+                                <term><option>--list-catalog</option></term>
+
+                                <listitem><para>List the contents of
+                                the message catalog, as table of
+                                message IDs plus their short
+                                description strings.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><option>--update-catalog</option></term>
+
+                                <listitem><para>Update the message
+                                catalog index. This command needs to
+                                be executed each time new catalog
+                                files are installed, removed or
+                                updated to rebuild the binary catalog
+                                index.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><option>--setup-keys</option></term>
 
                                 <listitem><para>Instead of showing
diff --git a/src/journal/catalog.c b/src/journal/catalog.c
new file mode 100644
index 0000000..7be0d20
--- /dev/null
+++ b/src/journal/catalog.c
@@ -0,0 +1,584 @@
+/*-*- 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 <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <locale.h>
+
+#include "util.h"
+#include "log.h"
+#include "sparse-endian.h"
+#include "sd-id128.h"
+#include "hashmap.h"
+#include "strv.h"
+#include "strbuf.h"
+#include "conf-files.h"
+#include "mkdir.h"
+#include "catalog.h"
+
+static const char * const conf_file_dirs[] = {
+        "/usr/local/lib/systemd/catalog/",
+        "/usr/lib/systemd/catalog/",
+        NULL
+};
+
+#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' }
+
+typedef struct CatalogHeader {
+        uint8_t signature[8];  /* "RHHHKSLP" */
+        le32_t compatible_flags;
+        le32_t incompatible_flags;
+        le32_t header_size;
+        le32_t n_items;
+} CatalogHeader;
+
+typedef struct CatalogItem {
+        sd_id128_t id;
+        char language[32];
+        le32_t offset;
+} CatalogItem;
+
+static unsigned catalog_hash_func(const void *p) {
+        const CatalogItem *i = p;
+
+        assert_cc(sizeof(unsigned) == sizeof(uint8_t)*4);
+
+        return (((unsigned) i->id.bytes[0] << 24) |
+                ((unsigned) i->id.bytes[1] << 16) |
+                ((unsigned) i->id.bytes[2] << 8) |
+                ((unsigned) i->id.bytes[3])) ^
+               (((unsigned) i->id.bytes[4] << 24) |
+                ((unsigned) i->id.bytes[5] << 16) |
+                ((unsigned) i->id.bytes[6] << 8) |
+                ((unsigned) i->id.bytes[7])) ^
+               (((unsigned) i->id.bytes[8] << 24) |
+                ((unsigned) i->id.bytes[9] << 16) |
+                ((unsigned) i->id.bytes[10] << 8) |
+                ((unsigned) i->id.bytes[11])) ^
+               (((unsigned) i->id.bytes[12] << 24) |
+                ((unsigned) i->id.bytes[13] << 16) |
+                ((unsigned) i->id.bytes[14] << 8) |
+                ((unsigned) i->id.bytes[15])) ^
+                string_hash_func(i->language);
+}
+
+static int catalog_compare_func(const void *a, const void *b) {
+        const CatalogItem *i = a, *j = b;
+        unsigned k;
+
+        for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) {
+                 if (i->id.bytes[k] < j->id.bytes[k])
+                        return -1;
+                 if (i->id.bytes[k] > j->id.bytes[k])
+                        return 1;
+        }
+
+        return strncmp(i->language, j->language, sizeof(i->language));
+}
+
+static int finish_item(
+                Hashmap *h,
+                struct strbuf *sb,
+                sd_id128_t id,
+                const char *language,
+                const char *payload) {
+
+        ssize_t offset;
+        CatalogItem *i;
+        int r;
+
+        assert(h);
+        assert(sb);
+        assert(payload);
+
+        offset = strbuf_add_string(sb, payload, strlen(payload));
+        if (offset < 0)
+                return log_oom();
+
+        if (offset > 0xFFFFFFFF) {
+                log_error("Too many catalog entries.");
+                return -E2BIG;
+        }
+
+        i = new0(CatalogItem, 1);
+        if (!i)
+                return log_oom();
+
+        i->id = id;
+        strncpy(i->language, language, sizeof(i->language));
+        i->offset = htole32((uint32_t) offset);
+
+        r = hashmap_put(h, i, i);
+        if (r == EEXIST) {
+                log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.", SD_ID128_FORMAT_VAL(id), language ? language : "C");
+                free(i);
+                return 0;
+        }
+
+        return 0;
+}
+
+static int import_file(Hashmap *h, struct strbuf *sb, const char *path) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *payload = NULL;
+        unsigned n = 0;
+        sd_id128_t id;
+        char language[32];
+        bool got_id = false, empty_line = true;
+        int r;
+
+        assert(h);
+        assert(sb);
+        assert(path);
+
+        f = fopen(path, "re");
+        if (!f) {
+                log_error("Failed to open file %s: %m", path);
+                return -errno;
+        }
+
+        for (;;) {
+                char line[LINE_MAX];
+                size_t a, b, c;
+                char *t;
+
+                if (!fgets(line, sizeof(line), f)) {
+                        if (feof(f))
+                                break;
+
+                        log_error("Failed to read file %s: %m", path);
+                        return -errno;
+                }
+
+                n++;
+
+                truncate_nl(line);
+
+                if (line[0] == 0) {
+                        empty_line = true;
+                        continue;
+                }
+
+                if (strchr(COMMENTS, line[0]))
+                        continue;
+
+                if (empty_line &&
+                    strlen(line) >= 2+1+32 &&
+                    line[0] == '-' &&
+                    line[1] == '-' &&
+                    line[2] == ' ' &&
+                    (line[2+1+32] == ' ' || line[2+1+32] == 0)) {
+
+                        bool with_language;
+                        sd_id128_t jd;
+
+                        /* New entry */
+
+                        with_language = line[2+1+32] != 0;
+                        line[2+1+32] = 0;
+
+                        if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
+
+                                if (got_id) {
+                                        r = finish_item(h, sb, id, language, payload);
+                                        if (r < 0)
+                                                return r;
+                                }
+
+                                if (with_language) {
+                                        t = strstrip(line + 2 + 1 + 32 + 1);
+
+                                        c = strlen(t);
+                                        if (c <= 0) {
+                                                log_error("[%s:%u] Language too short.", path, n);
+                                                return -EINVAL;
+                                        }
+                                        if (c > sizeof(language)) {
+                                                log_error("[%s:%u] language too long.", path, n);
+                                                return -EINVAL;
+                                        }
+
+                                        strncpy(language, t, sizeof(language));
+                                } else
+                                        zero(language);
+
+                                got_id = true;
+                                empty_line = false;
+                                id = jd;
+
+                                if (payload)
+                                        payload[0] = 0;
+
+                                continue;
+                        }
+                }
+
+                /* Payload */
+                if (!got_id) {
+                        log_error("[%s:%u] Got payload before ID.", path, n);
+                        return -EINVAL;
+                }
+
+                a = payload ? strlen(payload) : 0;
+                b = strlen(line);
+
+                c = a + (empty_line ? 1 : 0) + b + 1 + 1;
+                t = realloc(payload, c);
+                if (!t)
+                        return log_oom();
+
+                if (empty_line) {
+                        t[a] = '\n';
+                        memcpy(t + a + 1, line, b);
+                        t[a+b+1] = '\n';
+                        t[a+b+2] = 0;
+                } else {
+                        memcpy(t + a, line, b);
+                        t[a+b] = '\n';
+                        t[a+b+1] = 0;
+                }
+
+                payload = t;
+                empty_line = false;
+        }
+
+        if (got_id) {
+                r = finish_item(h, sb, id, language, payload);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int catalog_update(void) {
+        _cleanup_strv_free_ char **files = NULL;
+        _cleanup_fclose_ FILE *w = NULL;
+        _cleanup_free_ char *p = NULL;
+        char **f;
+        Hashmap *h;
+        struct strbuf *sb = NULL;
+        _cleanup_free_ CatalogItem *items = NULL;
+        CatalogItem *i;
+        CatalogHeader header;
+        size_t k;
+        Iterator j;
+        unsigned n;
+        int r;
+
+        h = hashmap_new(catalog_hash_func, catalog_compare_func);
+        if (!h)
+                return -ENOMEM;
+
+        sb = strbuf_new();
+        if (!sb) {
+                r = log_oom();
+                goto finish;
+        }
+
+        r = conf_files_list_strv(&files, ".catalog", (const char **) conf_file_dirs);
+        if (r < 0) {
+                log_error("Failed to get catalog files: %s", strerror(-r));
+                goto finish;
+        }
+
+        STRV_FOREACH(f, files) {
+                log_debug("reading file '%s'", *f);
+                import_file(h, sb, *f);
+        }
+
+        if (hashmap_size(h) <= 0) {
+                log_info("No items in catalog.");
+                r = 0;
+                goto finish;
+        }
+
+        strbuf_complete(sb);
+
+        items = new(CatalogItem, hashmap_size(h));
+        if (!items) {
+                r = log_oom();
+                goto finish;
+        }
+
+        n = 0;
+        HASHMAP_FOREACH(i, h, j) {
+                log_debug("Found " SD_ID128_FORMAT_STR ", language %s", SD_ID128_FORMAT_VAL(i->id), isempty(i->language) ? "C" : i->language);
+                items[n++] = *i;
+        }
+
+        assert(n == hashmap_size(h));
+        qsort(items, n, sizeof(CatalogItem), catalog_compare_func);
+
+        mkdir_p("/var/lib/systemd/catalog", 0775);
+
+        r = fopen_temporary("/var/lib/systemd/catalog/database", &w, &p);
+        if (r < 0) {
+                log_error("Failed to open database for writing: %s", strerror(-r));
+                goto finish;
+        }
+
+        zero(header);
+        memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
+        header.header_size = htole32(ALIGN_TO(sizeof(CatalogHeader), 8));
+        header.n_items = htole32(hashmap_size(h));
+
+        k = fwrite(&header, 1, sizeof(header), w);
+        if (k != sizeof(header)) {
+                log_error("Failed to write header.");
+                goto finish;
+        }
+
+        k = fwrite(items, 1, n * sizeof(CatalogItem), w);
+        if (k != n * sizeof(CatalogItem)) {
+                log_error("Failed to write database.");
+                goto finish;
+        }
+
+        k = fwrite(sb->buf, 1, sb->len, w);
+        if (k != sb->len) {
+                log_error("Failed to write strings.");
+                goto finish;
+        }
+
+        fflush(w);
+
+        if (ferror(w)) {
+                log_error("Failed to write database.");
+                goto finish;
+        }
+
+        fchmod(fileno(w), 0644);
+
+        if (rename(p, "/var/lib/systemd/catalog/database") < 0) {
+                log_error("rename() failed: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        free(p);
+        p = NULL;
+
+        r = 0;
+
+finish:
+        hashmap_free_free(h);
+
+        if (sb)
+                strbuf_cleanup(sb);
+
+        if (p)
+                unlink(p);
+
+        return r;
+}
+
+static int open_mmap(int *_fd, struct stat *_st, void **_p) {
+        const CatalogHeader *h;
+        int fd;
+        void *p;
+        struct stat st;
+
+        assert(_fd);
+        assert(_st);
+        assert(_p);
+
+        fd = open("/var/lib/systemd/catalog/database", O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (fstat(fd, &st) < 0) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        if (st.st_size < (off_t) sizeof(CatalogHeader)) {
+                close_nointr_nofail(fd);
+                return -EINVAL;
+        }
+
+        p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0);
+        if (p == MAP_FAILED) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        h = p;
+        if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 ||
+            le32toh(h->header_size) < sizeof(CatalogHeader) ||
+            h->incompatible_flags != 0 ||
+            le32toh(h->n_items) <= 0 ||
+            st.st_size < (off_t) (le32toh(h->header_size) + sizeof(CatalogItem) * le32toh(h->n_items))) {
+                close_nointr_nofail(fd);
+                munmap(p, st.st_size);
+                return -EBADMSG;
+        }
+
+        *_fd = fd;
+        *_st = st;
+        *_p = p;
+
+        return 0;
+}
+
+static const char *find_id(void *p, sd_id128_t id) {
+        CatalogItem key, *f = NULL;
+        const CatalogHeader *h = p;
+        const char *loc;
+
+        zero(key);
+        key.id = id;
+
+        loc = setlocale(LC_MESSAGES, NULL);
+        if (loc && loc[0] && !streq(loc, "C") && !streq(loc, "POSIX")) {
+                strncpy(key.language, loc, sizeof(key.language));
+                key.language[strcspn(key.language, ".@")] = 0;
+
+                f = bsearch(&key, (const uint8_t*) p + le32toh(h->header_size), le32toh(h->n_items), sizeof(CatalogItem), catalog_compare_func);
+                if (!f) {
+                        char *e;
+
+                        e = strchr(key.language, '_');
+                        if (e) {
+                                *e = 0;
+                                f = bsearch(&key, (const uint8_t*) p + le32toh(h->header_size), le32toh(h->n_items), sizeof(CatalogItem), catalog_compare_func);
+                        }
+                }
+        }
+
+        if (!f) {
+                zero(key.language);
+                f = bsearch(&key, (const uint8_t*) p + le32toh(h->header_size), le32toh(h->n_items), sizeof(CatalogItem), catalog_compare_func);
+        }
+
+        if (!f)
+                return NULL;
+
+        return (const char*) p +
+                le32toh(h->header_size) +
+                le32toh(h->n_items) * sizeof(CatalogItem) +
+                le32toh(f->offset);
+}
+
+int catalog_get(sd_id128_t id, char **_text) {
+        _cleanup_close_ int fd = -1;
+        void *p = NULL;
+        struct stat st;
+        char *text = NULL;
+        int r;
+        const char *s;
+
+        assert(_text);
+
+        r = open_mmap(&fd, &st, &p);
+        if (r < 0)
+                return r;
+
+        s = find_id(p, id);
+        if (!s) {
+                r = -ENOENT;
+                goto finish;
+        }
+
+        text = strdup(s);
+        if (!text) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        *_text = text;
+        r = 0;
+
+finish:
+        if (p)
+                munmap(p, st.st_size);
+
+        return r;
+}
+
+static char *find_header(const char *s, const char *header) {
+
+        for (;;) {
+                const char *v, *e;
+
+                v = startswith(s, header);
+                if (v) {
+                        v += strspn(v, WHITESPACE);
+                        return strndup(v, strcspn(v, NEWLINE));
+                }
+
+                /* End of text */
+                e = strchr(s, '\n');
+                if (!e)
+                        return NULL;
+
+                /* End of header */
+                if (e == s)
+                        return NULL;
+
+                s = e + 1;
+        }
+}
+
+int catalog_list(FILE *f) {
+        _cleanup_close_ int fd = -1;
+        void *p = NULL;
+        struct stat st;
+        const CatalogHeader *h;
+        const CatalogItem *items;
+        int r;
+        unsigned n;
+        sd_id128_t last_id;
+        bool last_id_set = false;
+
+        r = open_mmap(&fd, &st, &p);
+        if (r < 0)
+                return r;
+
+        h = p;
+        items = (const CatalogItem*) ((const uint8_t*) p + le32toh(h->header_size));
+
+        for (n = 0; n < le32toh(h->n_items); n++) {
+                const char *s;
+                _cleanup_free_ char *subject = NULL, *defined_by = NULL;
+
+                if (last_id_set && sd_id128_equal(last_id, items[n].id))
+                        continue;
+
+                assert_se(s = find_id(p, items[n].id));
+
+                subject = find_header(s, "Subject:");
+                defined_by = find_header(s, "Defined-By:");
+
+                fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", SD_ID128_FORMAT_VAL(items[n].id), strna(defined_by), strna(subject));
+
+                last_id_set = true;
+                last_id = items[n].id;
+        }
+
+        munmap(p, st.st_size);
+
+        return 0;
+}
diff --git a/src/journal/catalog.h b/src/journal/catalog.h
new file mode 100644
index 0000000..9add773
--- /dev/null
+++ b/src/journal/catalog.h
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  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 "sd-id128.h"
+
+int catalog_update(void);
+int catalog_get(sd_id128_t id, char **data);
+int catalog_list(FILE *f);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 011a11b..46a7be2 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -51,6 +51,7 @@
 #include "journal-qrcode.h"
 #include "fsprg.h"
 #include "unit-name.h"
+#include "catalog.h"
 
 #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
 
@@ -74,6 +75,7 @@ static usec_t arg_since, arg_until;
 static bool arg_since_set = false, arg_until_set = false;
 static const char *arg_unit = NULL;
 static const char *arg_field = NULL;
+static bool arg_catalog = false;
 
 static enum {
         ACTION_SHOW,
@@ -82,6 +84,8 @@ static enum {
         ACTION_SETUP_KEYS,
         ACTION_VERIFY,
         ACTION_DISK_USAGE,
+        ACTION_LIST_CATALOG,
+        ACTION_UPDATE_CATALOG
 } arg_action = ACTION_SHOW;
 
 static int help(void) {
@@ -100,6 +104,7 @@ static int help(void) {
                "     --no-tail           Show all lines, even in follow mode\n"
                "  -o --output=STRING     Change journal output mode (short, short-monotonic,\n"
                "                         verbose, export, json, json-pretty, json-sse, cat)\n"
+               "  -x --catalog           Add message explanations where available\n"
                "  -a --all               Show all fields, including long and unprintable\n"
                "  -q --quiet             Don't show privilege warning\n"
                "     --no-pager          Do not pipe output into a pager\n"
@@ -116,6 +121,8 @@ static int help(void) {
                "     --header            Show journal header information\n"
                "     --disk-usage        Show total disk usage\n"
                "  -F --field=FIELD       List all values a certain field takes\n"
+               "     --list-catalog      Show message IDs of all entries in the message catalog\n"
+               "     --update-catalog    Update the message catalog database\n"
 #ifdef HAVE_GCRYPT
                "     --setup-keys        Generate new FSS key pair\n"
                "     --verify            Verify journal file consistency\n"
@@ -139,7 +146,9 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VERIFY_KEY,
                 ARG_DISK_USAGE,
                 ARG_SINCE,
-                ARG_UNTIL
+                ARG_UNTIL,
+                ARG_LIST_CATALOG,
+                ARG_UPDATE_CATALOG
         };
 
         static const struct option options[] = {
@@ -168,6 +177,9 @@ static int parse_argv(int argc, char *argv[]) {
                 { "until",        required_argument, NULL, ARG_UNTIL        },
                 { "unit",         required_argument, NULL, 'u'              },
                 { "field",        required_argument, NULL, 'F'              },
+                { "catalog",      no_argument,       NULL, 'x'              },
+                { "list-catalog", no_argument,       NULL, ARG_LIST_CATALOG },
+                { "update-catalog",no_argument,      NULL, ARG_UPDATE_CATALOG },
                 { NULL,           0,                 NULL, 0                }
         };
 
@@ -176,7 +188,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:x", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -376,6 +388,18 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_field = optarg;
                         break;
 
+                case 'x':
+                        arg_catalog = true;
+                        break;
+
+                case ARG_LIST_CATALOG:
+                        arg_action = ACTION_LIST_CATALOG;
+                        break;
+
+                case ARG_UPDATE_CATALOG:
+                        arg_action = ACTION_UPDATE_CATALOG;
+                        break;
+
                 default:
                         log_error("Unknown option code %c", c);
                         return -EINVAL;
@@ -841,6 +865,16 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
+        if (arg_action == ACTION_LIST_CATALOG)  {
+                r = catalog_list(stdout);
+                goto finish;
+        }
+
+        if (arg_action == ACTION_UPDATE_CATALOG)  {
+                r = catalog_update();
+                goto finish;
+        }
+
         r = access_check();
         if (r < 0)
                 goto finish;
@@ -1030,7 +1064,8 @@ int main(int argc, char *argv[]) {
                         flags =
                                 arg_all * OUTPUT_SHOW_ALL |
                                 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
-                                on_tty() * OUTPUT_COLOR;
+                                on_tty() * OUTPUT_COLOR |
+                                arg_catalog * OUTPUT_CATALOG;
 
                         r = output_journal(stdout, j, arg_output, 0, flags);
                         if (r < 0)
diff --git a/src/journal/libsystemd-journal.sym b/src/journal/libsystemd-journal.sym
index ad78fcc..d4b0c32 100644
--- a/src/journal/libsystemd-journal.sym
+++ b/src/journal/libsystemd-journal.sym
@@ -84,4 +84,5 @@ global:
 LIBSYSTEMD_JOURNAL_196 {
 global:
         sd_journal_fd_reliable;
+        sd_journal_get_catalog;
 } LIBSYSTEMD_JOURNAL_195;
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index a346691..c86f1ea 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -38,11 +38,15 @@
 #include "compress.h"
 #include "journal-internal.h"
 #include "missing.h"
+#include "catalog.h"
+#include "replace-var.h"
 
 #define JOURNAL_FILES_MAX 1024
 
 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
 
+#define REPLACE_VAR_MAX 256
+
 static void detach_location(sd_journal *j) {
         Iterator i;
         JournalFile *f;
@@ -2389,3 +2393,59 @@ _public_ int sd_journal_reliable_fd(sd_journal *j) {
 
         return !j->on_network;
 }
+
+static char *lookup_field(const char *field, void *userdata) {
+        sd_journal *j = userdata;
+        const void *data;
+        size_t size, d;
+        int r;
+
+        assert(field);
+        assert(j);
+
+        r = sd_journal_get_data(j, field, &data, &size);
+        if (r < 0 ||
+            size > REPLACE_VAR_MAX)
+                return strdup(field);
+
+        d = strlen(field) + 1;
+
+        return strndup((const char*) data + d, size - d);
+}
+
+_public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
+        const void *data;
+        size_t size;
+        sd_id128_t id;
+        _cleanup_free_ char *text = NULL, *cid = NULL;
+        char *t;
+        int r;
+
+        if (!j)
+                return -EINVAL;
+        if (!ret)
+                return -EINVAL;
+
+        r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
+        if (r < 0)
+                return r;
+
+        cid = strndup((const char*) data + 11, size - 11);
+        if (!cid)
+                return -ENOMEM;
+
+        r = sd_id128_from_string(cid, &id);
+        if (r < 0)
+                return r;
+
+        r = catalog_get(id, &text);
+        if (r < 0)
+                return r;
+
+        t = replace_var(text, lookup_field, j);
+        if (!t)
+                return -ENOMEM;
+
+        *ret = t;
+        return 0;
+}
diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c
new file mode 100644
index 0000000..cec8a11
--- /dev/null
+++ b/src/journal/test-catalog.c
@@ -0,0 +1,48 @@
+/*-*- 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 <locale.h>
+
+#include "util.h"
+#include "log.h"
+#include "catalog.h"
+#include "sd-messages.h"
+
+int main(int argc, char *argv[]) {
+
+        _cleanup_free_ char *text = NULL;
+
+        setlocale(LC_ALL, "de_DE.UTF-8");
+
+        log_set_max_level(LOG_DEBUG);
+
+        assert_se(catalog_update() >= 0);
+
+        assert_se(catalog_list(stdout) >= 0);
+
+        assert_se(catalog_get(SD_MESSAGE_COREDUMP, &text) >= 0);
+
+        printf(">>>%s<<<\n", text);
+
+        fflush(stdout);
+
+        return 0;
+}
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 36cce73..cb93761 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -34,6 +34,26 @@
 #define PRINT_THRESHOLD 128
 #define JSON_THRESHOLD 4096
 
+static int print_catalog(FILE *f, sd_journal *j) {
+        int r;
+        _cleanup_free_ char *t = NULL, *z = NULL;
+
+
+        r = sd_journal_get_catalog(j, &t);
+        if (r < 0)
+                return r;
+
+        z = strreplace(strstrip(t), "\n", "\n-- ");
+        if (!z)
+                return log_oom();
+
+        fputs("-- ", f);
+        fputs(z, f);
+        fputc('\n', f);
+
+        return 0;
+}
+
 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
         size_t fl, nl;
         void *buf;
@@ -265,6 +285,9 @@ static int output_short(
         } else
                 fputs("\n", f);
 
+        if (flags & OUTPUT_CATALOG)
+                print_catalog(f, j);
+
         return 0;
 }
 
@@ -322,6 +345,9 @@ static int output_verbose(
                         fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
         }
 
+        if (flags & OUTPUT_CATALOG)
+                print_catalog(f, j);
+
         return 0;
 }
 
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 0608280..11cb41a 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -45,7 +45,8 @@ typedef enum OutputFlags {
         OUTPUT_FOLLOW         = 1 << 1,
         OUTPUT_WARN_CUTOFF    = 1 << 2,
         OUTPUT_FULL_WIDTH     = 1 << 3,
-        OUTPUT_COLOR          = 1 << 4
+        OUTPUT_COLOR          = 1 << 4,
+        OUTPUT_CATALOG        = 1 << 5
 } OutputFlags;
 
 int output_journal(
diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
index 7241173..f9919b2 100644
--- a/src/systemd/sd-journal.h
+++ b/src/systemd/sd-journal.h
@@ -128,6 +128,8 @@ int sd_journal_reliable_fd(sd_journal *j);
 int sd_journal_process(sd_journal *j);
 int sd_journal_wait(sd_journal *j, uint64_t timeout_usec);
 
+int sd_journal_get_catalog(sd_journal *j, char **text);
+
 #define SD_JOURNAL_FOREACH(j)                                           \
         if (sd_journal_seek_head(j) >= 0)                               \
                 while (sd_journal_next(j) > 0)

commit 59f432ea6d6d441d0af7c76c37e80730c8df473a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 15 22:47:04 2012 +0100

    hostnamectl: fix parsing of --no-ask-password

diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index b7ae5cc..265c7ec 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -403,6 +403,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_set_static = true;
                         break;
 
+                case ARG_NO_ASK_PASSWORD:
+                        arg_ask_password = false;
+                        break;
+
                 case '?':
                         return -EINVAL;
 

commit 8e6054f732b4bc980d3af3e1386ca94b3a602eb8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 15 22:46:50 2012 +0100

    Update TODO

diff --git a/TODO b/TODO
index 2488ee3..309166e 100644
--- a/TODO
+++ b/TODO
@@ -21,6 +21,8 @@ F18:
 
 Features:
 
+* Add ConditionBatteryPower= or ConditionACPower=? (but definitely not both)
+
 * add API to close/reopen/get fd for journal client fd in libsystemd-journal.
 
 * maybe add API to send pairs of iovecs via sd_journal_send

commit 8885064fd00d74feb16334d645f9c479735090c3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Nov 15 22:45:49 2012 +0100

    polkit: if PK is not around, consider this a permission denied error
    
    Uninstalling PK should cleanly disable PK authorization but not result
    in further runtime errors.

diff --git a/src/shared/polkit.c b/src/shared/polkit.c
index 126096e..8269445 100644
--- a/src/shared/polkit.c
+++ b/src/shared/polkit.c
@@ -112,11 +112,14 @@ int verify_polkit(
 
         reply = dbus_connection_send_with_reply_and_block(c, m, -1, error);
         if (!reply) {
-                r = -EIO;
-                goto finish;
-        }
 
-        if (dbus_set_error_from_message(error, reply)) {
+                /* Treat no PK available as access denied */
+                if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN)) {
+                        r = -EACCES;
+                        dbus_error_free(error);
+                        goto finish;
+                }
+
                 r = -EIO;
                 goto finish;
         }



More information about the systemd-commits mailing list