]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/journal/catalog.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal / catalog.c
index a3e51e2f52db0efaad5446fa24244e5517bdf5b5..6775535b173f478d9809ab9fa6c4c752cda2f2a9 100644 (file)
@@ -1,5 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <errno.h>
 #include <fcntl.h>
+#include <locale.h>
 #include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
 #include <string.h>
 #include <sys/mman.h>
-#include <locale.h>
+#include <unistd.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 "alloc-util.h"
+#include "catalog.h"
 #include "conf-files.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "log.h"
 #include "mkdir.h"
-#include "catalog.h"
+#include "path-util.h"
 #include "siphash24.h"
+#include "sparse-endian.h"
+#include "strbuf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "util.h"
 
 const char * const catalog_file_dirs[] = {
         "/usr/local/lib/systemd/catalog/",
@@ -62,21 +67,11 @@ typedef struct CatalogItem {
         le64_t offset;
 } CatalogItem;
 
-static unsigned long catalog_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+static void catalog_hash_func(const void *p, struct siphash *state) {
         const CatalogItem *i = p;
-        uint64_t u;
-        size_t l, sz;
-        void *v;
-
-        l = strlen(i->language);
-        sz = sizeof(i->id) + l;
-        v = alloca(sz);
-
-        memcpy(mempcpy(v, &i->id, sizeof(i->id)), i->language, l);
 
-        siphash24((uint8_t*) &u, v, sz, hash_key);
-
-        return (unsigned long) u;
+        siphash24_compress(&i->id, sizeof(i->id), state);
+        siphash24_compress(i->language, strlen(i->language), state);
 }
 
 static int catalog_compare_func(const void *a, const void *b) {
@@ -98,24 +93,86 @@ const struct hash_ops catalog_hash_ops = {
         .compare = catalog_compare_func
 };
 
+static bool next_header(const char **s) {
+        const char *e;
+
+        e = strchr(*s, '\n');
+
+        /* Unexpected end */
+        if (!e)
+                return false;
+
+        /* End of headers */
+        if (e == *s)
+                return false;
+
+        *s = e + 1;
+        return true;
+}
+
+static const char *skip_header(const char *s) {
+        while (next_header(&s))
+                ;
+        return s;
+}
+
+static char *combine_entries(const char *one, const char *two) {
+        const char *b1, *b2;
+        size_t l1, l2, n;
+        char *dest, *p;
+
+        /* Find split point of headers to body */
+        b1 = skip_header(one);
+        b2 = skip_header(two);
+
+        l1 = strlen(one);
+        l2 = strlen(two);
+        dest = new(char, l1 + l2 + 1);
+        if (!dest) {
+                log_oom();
+                return NULL;
+        }
+
+        p = dest;
+
+        /* Headers from @one */
+        n = b1 - one;
+        p = mempcpy(p, one, n);
+
+        /* Headers from @two, these will only be found if not present above */
+        n = b2 - two;
+        p = mempcpy(p, two, n);
+
+        /* Body from @one */
+        n = l1 - (b1 - one);
+        if (n > 0) {
+                memcpy(p, b1, n);
+                p += n;
+
+        /* Body from @two */
+        } else {
+                n = l2 - (b2 - two);
+                memcpy(p, b2, n);
+                p += n;
+        }
+
+        assert(p - dest <= (ptrdiff_t)(l1 + l2));
+        p[0] = '\0';
+        return dest;
+}
+
 static int finish_item(
                 Hashmap *h,
-                struct strbuf *sb,
                 sd_id128_t id,
                 const char *language,
-                const char *payload) {
+                char *payload, size_t payload_size) {
 
-        ssize_t offset;
         _cleanup_free_ CatalogItem *i = NULL;
-        int r;
+        _cleanup_free_ char *prev = NULL, *combined = NULL;
 
         assert(h);
-        assert(sb);
         assert(payload);
-
-        offset = strbuf_add_string(sb, payload, strlen(payload));
-        if (offset < 0)
-                return log_oom();
+        assert(payload_size > 0);
 
         i = new0(CatalogItem, 1);
         if (!i)
@@ -126,17 +183,29 @@ static int finish_item(
                 assert(strlen(language) > 1 && strlen(language) < 32);
                 strcpy(i->language, language);
         }
-        i->offset = htole64((uint64_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");
-                return 0;
-        } else if (r < 0)
-                return r;
+        prev = hashmap_get(h, i);
+        if (prev) {
+                /* Already have such an item, combine them */
+                combined = combine_entries(payload, prev);
+                if (!combined)
+                        return log_oom();
+
+                if (hashmap_update(h, i, combined) < 0)
+                        return log_oom();
+                combined = NULL;
+        } else {
+                /* A new item */
+                combined = memdup(payload, payload_size + 1);
+                if (!combined)
+                        return log_oom();
+
+                if (hashmap_put(h, i, combined) < 0)
+                        return log_oom();
+                i = NULL;
+                combined = NULL;
+        }
 
-        i = NULL;
         return 0;
 }
 
@@ -148,8 +217,8 @@ int catalog_file_lang(const char* filename, char **lang) {
                 return 0;
 
         beg = end - 1;
-        while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32)
-                beg --;
+        while (beg > filename && !IN_SET(*beg, '.', '/') && end - beg < 32)
+                beg--;
 
         if (*beg != '.' || end <= beg + 1)
                 return 0;
@@ -193,9 +262,10 @@ static int catalog_entry_lang(const char* filename, int line,
         return 0;
 }
 
-int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
+int catalog_import_file(Hashmap *h, const char *path) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *payload = NULL;
+        size_t payload_size = 0, payload_allocated = 0;
         unsigned n = 0;
         sd_id128_t id;
         _cleanup_free_ char *deflang = NULL, *lang = NULL;
@@ -203,7 +273,6 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
         int r;
 
         assert(h);
-        assert(sb);
         assert(path);
 
         f = fopen(path, "re");
@@ -212,21 +281,19 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
 
         r = catalog_file_lang(path, &deflang);
         if (r < 0)
-                log_error_errno(errno, "Failed to determine language for file %s: %m", path);
+                log_error_errno(r, "Failed to determine language for file %s: %m", path);
         if (r == 1)
                 log_debug("File %s has language %s.", path, deflang);
 
         for (;;) {
                 char line[LINE_MAX];
-                size_t a, b, c;
-                char *t;
+                size_t line_len;
 
                 if (!fgets(line, sizeof(line), f)) {
                         if (feof(f))
                                 break;
 
-                        log_error_errno(errno, "Failed to read file %s: %m", path);
-                        return -errno;
+                        return log_error_errno(errno, "Failed to read file %s: %m", path);
                 }
 
                 n++;
@@ -246,7 +313,7 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
                     line[0] == '-' &&
                     line[1] == '-' &&
                     line[2] == ' ' &&
-                    (line[2+1+32] == ' ' || line[2+1+32] == '\0')) {
+                    IN_SET(line[2+1+32], ' ', '\0')) {
 
                         bool with_language;
                         sd_id128_t jd;
@@ -259,16 +326,23 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
                         if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
 
                                 if (got_id) {
-                                        r = finish_item(h, sb, id, lang ?: deflang, payload);
+                                        if (payload_size == 0) {
+                                                log_error("[%s:%u] No payload text.", path, n);
+                                                return -EINVAL;
+                                        }
+
+                                        r = finish_item(h, id, lang ?: deflang, payload, payload_size);
                                         if (r < 0)
                                                 return r;
 
                                         lang = mfree(lang);
+                                        payload_size = 0;
                                 }
 
                                 if (with_language) {
-                                        t = strstrip(line + 2 + 1 + 32 + 1);
+                                        char *t;
 
+                                        t = strstrip(line + 2 + 1 + 32 + 1);
                                         r = catalog_entry_lang(path, n, t, deflang, &lang);
                                         if (r < 0)
                                                 return r;
@@ -278,9 +352,6 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
                                 empty_line = false;
                                 id = jd;
 
-                                if (payload)
-                                        payload[0] = '\0';
-
                                 continue;
                         }
                 }
@@ -291,31 +362,28 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
                         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)
+                line_len = strlen(line);
+                if (!GREEDY_REALLOC(payload, payload_allocated,
+                                    payload_size + (empty_line ? 1 : 0) + line_len + 1 + 1))
                         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;
-                }
+                if (empty_line)
+                        payload[payload_size++] = '\n';
+                memcpy(payload + payload_size, line, line_len);
+                payload_size += line_len;
+                payload[payload_size++] = '\n';
+                payload[payload_size] = '\0';
 
-                payload = t;
                 empty_line = false;
         }
 
         if (got_id) {
-                r = finish_item(h, sb, id, lang ?: deflang, payload);
+                if (payload_size == 0) {
+                        log_error("[%s:%u] No payload text.", path, n);
+                        return -EINVAL;
+                }
+
+                r = finish_item(h, id, lang ?: deflang, payload, payload_size);
                 if (r < 0)
                         return r;
         }
@@ -323,8 +391,8 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
         return 0;
 }
 
-static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
-                          CatalogItem *items, size_t n) {
+static int64_t write_catalog(const char *database, struct strbuf *sb,
+                             CatalogItem *items, size_t n) {
         CatalogHeader header;
         _cleanup_fclose_ FILE *w = NULL;
         int r;
@@ -348,7 +416,7 @@ static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
         memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature));
         header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8));
         header.catalog_item_size = htole64(sizeof(CatalogItem));
-        header.n_items = htole64(hashmap_size(h));
+        header.n_items = htole64(n);
 
         r = -EIO;
 
@@ -383,7 +451,7 @@ static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb,
                 goto error;
         }
 
-        return ftell(w);
+        return ftello(w);
 
 error:
         (void) unlink(p);
@@ -394,12 +462,15 @@ int catalog_update(const char* database, const char* root, const char* const* di
         _cleanup_strv_free_ char **files = NULL;
         char **f;
         struct strbuf *sb = NULL;
-        _cleanup_hashmap_free_free_ Hashmap *h = NULL;
+        _cleanup_hashmap_free_free_free_ Hashmap *h = NULL;
         _cleanup_free_ CatalogItem *items = NULL;
+        ssize_t offset;
+        char *payload;
         CatalogItem *i;
         Iterator j;
         unsigned n;
-        long r;
+        int r;
+        int64_t sz;
 
         h = hashmap_new(&catalog_hash_ops);
         sb = strbuf_new();
@@ -409,7 +480,7 @@ int catalog_update(const char* database, const char* root, const char* const* di
                 goto finish;
         }
 
-        r = conf_files_list_strv(&files, ".catalog", root, dirs);
+        r = conf_files_list_strv(&files, ".catalog", root, 0, dirs);
         if (r < 0) {
                 log_error_errno(r, "Failed to get catalog files: %m");
                 goto finish;
@@ -417,10 +488,9 @@ int catalog_update(const char* database, const char* root, const char* const* di
 
         STRV_FOREACH(f, files) {
                 log_debug("Reading file '%s'", *f);
-                r = catalog_import_file(h, sb, *f);
+                r = catalog_import_file(h, *f);
                 if (r < 0) {
-                        log_error("Failed to import file '%s': %s.",
-                                  *f, strerror(-r));
+                        log_error_errno(r, "Failed to import file '%s': %m", *f);
                         goto finish;
                 }
         }
@@ -431,8 +501,6 @@ int catalog_update(const char* database, const char* root, const char* const* di
         } else
                 log_debug("Found %u items in catalog.", hashmap_size(h));
 
-        strbuf_complete(sb);
-
         items = new(CatalogItem, hashmap_size(h));
         if (!items) {
                 r = log_oom();
@@ -440,28 +508,38 @@ int catalog_update(const char* database, const char* root, const char* const* di
         }
 
         n = 0;
-        HASHMAP_FOREACH(i, h, j) {
+        HASHMAP_FOREACH_KEY(payload, i, h, j) {
                 log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
                           SD_ID128_FORMAT_VAL(i->id),
                           isempty(i->language) ? "C" : i->language);
+
+                offset = strbuf_add_string(sb, payload, strlen(payload));
+                if (offset < 0) {
+                        r = log_oom();
+                        goto finish;
+                }
+                i->offset = htole64((uint64_t) offset);
                 items[n++] = *i;
         }
 
         assert(n == hashmap_size(h));
         qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
 
-        r = write_catalog(database, h, sb, items, n);
-        if (r < 0)
-                log_error_errno(r, "Failed to write %s: %m", database);
-        else
-                log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.",
-                          database, n, sb->len, r);
+        strbuf_complete(sb);
+
+        sz = write_catalog(database, sb, items, n);
+        if (sz < 0)
+                r = log_error_errno(sz, "Failed to write %s: %m", database);
+        else {
+                r = 0;
+                log_debug("%s: wrote %u items, with %zu bytes of strings, %"PRIi64" total size.",
+                          database, n, sb->len, sz);
+        }
 
 finish:
-        if (sb)
-                strbuf_cleanup(sb);
+        strbuf_cleanup(sb);
 
-        return r < 0 ? r : 0;
+        return r;
 }
 
 static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) {
@@ -591,7 +669,7 @@ finish:
 static char *find_header(const char *s, const char *header) {
 
         for (;;) {
-                const char *v, *e;
+                const char *v;
 
                 v = startswith(s, header);
                 if (v) {
@@ -599,16 +677,8 @@ static char *find_header(const char *s, const char *header) {
                         return strndup(v, strcspn(v, NEWLINE));
                 }
 
-                /* End of text */
-                e = strchr(s, '\n');
-                if (!e)
+                if (!next_header(&s))
                         return NULL;
-
-                /* End of header */
-                if (e == s)
-                        return NULL;
-
-                s = e + 1;
         }
 }
 
@@ -676,8 +746,7 @@ int catalog_list_items(FILE *f, const char *database, bool oneline, char **items
 
                 k = sd_id128_from_string(*item, &id);
                 if (k < 0) {
-                        log_error_errno(k, "Failed to parse id128 '%s': %m",
-                                        *item);
+                        log_error_errno(k, "Failed to parse id128 '%s': %m", *item);
                         if (r == 0)
                                 r = k;
                         continue;
@@ -685,9 +754,8 @@ int catalog_list_items(FILE *f, const char *database, bool oneline, char **items
 
                 k = catalog_get(database, id, &msg);
                 if (k < 0) {
-                        log_full(k == -ENOENT ? LOG_NOTICE : LOG_ERR,
-                                 "Failed to retrieve catalog entry for '%s': %s",
-                                  *item, strerror(-k));
+                        log_full_errno(k == -ENOENT ? LOG_NOTICE : LOG_ERR, k,
+                                       "Failed to retrieve catalog entry for '%s': %m", *item);
                         if (r == 0)
                                 r = k;
                         continue;