]> 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 fcaa54aa0c9b2e8235535a52f9793aad64dfdd5b..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.
 
@@ -94,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)
@@ -122,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;
 }
 
@@ -144,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;
@@ -189,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;
@@ -199,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");
@@ -214,8 +287,7 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
 
         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))
@@ -241,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;
@@ -254,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;
@@ -273,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;
                         }
                 }
@@ -286,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;
         }
@@ -389,8 +462,10 @@ 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;
@@ -405,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;
@@ -413,7 +488,7 @@ 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_errno(r, "Failed to import file '%s': %m", *f);
                         goto finish;
@@ -426,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();
@@ -435,16 +508,25 @@ 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);
 
+        strbuf_complete(sb);
+
         sz = write_catalog(database, sb, items, n);
         if (sz < 0)
                 r = log_error_errno(sz, "Failed to write %s: %m", database);
@@ -587,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) {
@@ -595,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;
         }
 }