]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/journal/sd-journal.c
tree-wide: replace strjoin() with path_join()
[thirdparty/systemd.git] / src / journal / sd-journal.c
index 4deee461c3f87383254a07abc6b8d216c3f6dc7f..a4f173161375b40d9cb8ae6d893af1d4a0afcdb0 100644 (file)
@@ -1,22 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
-/***
-  This file is part of systemd.
-
-  Copyright 2011 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 <errno.h>
 #include <fcntl.h>
@@ -34,6 +16,8 @@
 #include "catalog.h"
 #include "compress.h"
 #include "dirent-util.h"
+#include "env-file.h"
+#include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
 #include "list.h"
 #include "lookup3.h"
 #include "missing.h"
+#include "nulstr-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "replace-var.h"
 #include "stat-util.h"
-#include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -399,7 +383,7 @@ static char *match_make_string(Match *m) {
                 return strdup("none");
 
         if (m->type == MATCH_DISCRETE)
-                return strndup(m->data, m->size);
+                return cescape_length(m->data, m->size);
 
         LIST_FOREACH(matches, i, m->matches) {
                 char *t, *k;
@@ -451,6 +435,8 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
 }
 
 _pure_ static int compare_with_location(JournalFile *f, Location *l) {
+        int r;
+
         assert(f);
         assert(l);
         assert(f->location_type == LOCATION_SEEK);
@@ -467,35 +453,31 @@ _pure_ static int compare_with_location(JournalFile *f, Location *l) {
         if (l->seqnum_set &&
             sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
 
-                if (f->current_seqnum < l->seqnum)
-                        return -1;
-                if (f->current_seqnum > l->seqnum)
-                        return 1;
+                r = CMP(f->current_seqnum, l->seqnum);
+                if (r != 0)
+                        return r;
         }
 
         if (l->monotonic_set &&
             sd_id128_equal(f->current_boot_id, l->boot_id)) {
 
-                if (f->current_monotonic < l->monotonic)
-                        return -1;
-                if (f->current_monotonic > l->monotonic)
-                        return 1;
+                r = CMP(f->current_monotonic, l->monotonic);
+                if (r != 0)
+                        return r;
         }
 
         if (l->realtime_set) {
 
-                if (f->current_realtime < l->realtime)
-                        return -1;
-                if (f->current_realtime > l->realtime)
-                        return 1;
+                r = CMP(f->current_realtime, l->realtime);
+                if (r != 0)
+                        return r;
         }
 
         if (l->xor_hash_set) {
 
-                if (f->current_xor_hash < l->xor_hash)
-                        return -1;
-                if (f->current_xor_hash > l->xor_hash)
-                        return 1;
+                r = CMP(f->current_xor_hash, l->xor_hash);
+                if (r != 0)
+                        return r;
         }
 
         return 0;
@@ -1202,8 +1184,7 @@ static bool file_has_type_prefix(const char *prefix, const char *filename) {
         tilded = strjoina(full, "~");
         atted = strjoina(prefix, "@");
 
-        return streq(filename, full) ||
-               streq(filename, tilded) ||
+        return STR_IN_SET(filename, full, tilded) ||
                startswith(filename, atted);
 }
 
@@ -1243,6 +1224,16 @@ static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix)
         return path_startswith(path, prefix);
 }
 
+static void track_file_disposition(sd_journal *j, JournalFile *f) {
+        assert(j);
+        assert(f);
+
+        if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
+                j->has_runtime_files = true;
+        else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
+                j->has_persistent_files = true;
+}
+
 static const char *skip_slash(const char *p) {
 
         if (!p)
@@ -1254,86 +1245,125 @@ static const char *skip_slash(const char *p) {
         return p;
 }
 
-static int add_any_file(sd_journal *j, int fd, const char *path) {
-        JournalFile *f = NULL;
+static int add_any_file(
+                sd_journal *j,
+                int fd,
+                const char *path) {
+
         bool close_fd = false;
+        JournalFile *f;
+        struct stat st;
         int r, k;
 
         assert(j);
         assert(fd >= 0 || path);
 
-        if (path) {
-                f = ordered_hashmap_get(j->files, path);
-                if (f) {
-                        /* Mark this file as seen in this generation. This is used to GC old files in
-                         * process_q_overflow() to detect journal files that are still and discern them from those who
-                         * are gone. */
-                        f->last_seen_generation = j->generation;
-                        return 0;
+        if (fd < 0) {
+                if (j->toplevel_fd >= 0)
+                        /* If there's a top-level fd defined make the path relative, explicitly, since otherwise
+                         * openat() ignores the first argument. */
+
+                        fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+                else
+                        fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+                if (fd < 0) {
+                        r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
+                        goto finish;
+                }
+
+                close_fd = true;
+
+                r = fd_nonblock(fd, false);
+                if (r < 0) {
+                        r = log_debug_errno(errno, "Failed to turn off O_NONBLOCK for %s: %m", path);
+                        goto finish;
                 }
         }
 
-        if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
-                log_debug("Too many open journal files, not adding %s.", path);
-                r = -ETOOMANYREFS;
-                goto fail;
+        if (fstat(fd, &st) < 0) {
+                r = log_debug_errno(errno, "Failed to fstat file '%s': %m", path);
+                goto finish;
         }
 
-        if (fd < 0 && j->toplevel_fd >= 0) {
+        r = stat_verify_regular(&st);
+        if (r < 0) {
+                log_debug_errno(r, "Refusing to open '%s', as it is not a regular file.", path);
+                goto finish;
+        }
 
-                /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative,
-                 * explicitly, since otherwise openat() ignores the first argument.) */
+        f = ordered_hashmap_get(j->files, path);
+        if (f) {
+                if (f->last_stat.st_dev == st.st_dev &&
+                    f->last_stat.st_ino == st.st_ino) {
 
-                fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC);
-                if (fd < 0) {
-                        r = log_debug_errno(errno, "Failed to open journal file %s: %m", path);
-                        goto fail;
+                        /* We already track this file, under the same path and with the same device/inode numbers, it's
+                         * hence really the same. Mark this file as seen in this generation. This is used to GC old
+                         * files in process_q_overflow() to detect journal files that are still there and discern them
+                         * from those which are gone. */
+
+                        f->last_seen_generation = j->generation;
+                        r = 0;
+                        goto finish;
                 }
 
-                close_fd = true;
+                /* So we tracked a file under this name, but it has a different inode/device. In that case, it got
+                 * replaced (probably due to rotation?), let's drop it hence from our list. */
+                remove_file_real(j, f);
+                f = NULL;
+        }
+
+        if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
+                log_debug("Too many open journal files, not adding %s.", path);
+                r = -ETOOMANYREFS;
+                goto finish;
         }
 
-        r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f);
+        r = journal_file_open(fd, path, O_RDONLY, 0, false, 0, false, NULL, j->mmap, NULL, NULL, &f);
         if (r < 0) {
-                if (close_fd)
-                        safe_close(fd);
                 log_debug_errno(r, "Failed to open journal file %s: %m", path);
-                goto fail;
+                goto finish;
         }
 
         /* journal_file_dump(f); */
 
         r = ordered_hashmap_put(j->files, f->path, f);
         if (r < 0) {
-                f->close_fd = close_fd;
+                f->close_fd = false; /* make sure journal_file_close() doesn't close the caller's fd (or our own). We'll let the caller do that, or ourselves */
                 (void) journal_file_close(f);
-                goto fail;
+                goto finish;
         }
 
-        f->last_seen_generation = j->generation;
-
-        if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run"))
-                j->has_runtime_files = true;
-        else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var"))
-                j->has_persistent_files = true;
+        close_fd = false; /* the fd is now owned by the JournalFile object */
 
-        log_debug("File %s added.", f->path);
+        f->last_seen_generation = j->generation;
 
+        track_file_disposition(j, f);
         check_network(j, f->fd);
 
         j->current_invalidate_counter++;
 
-        return 0;
+        log_debug("File %s added.", f->path);
 
-fail:
-        k = journal_put_error(j, r, path);
-        if (k < 0)
-                return k;
+        r = 0;
+
+finish:
+        if (close_fd)
+                safe_close(fd);
+
+        if (r < 0) {
+                k = journal_put_error(j, r, path);
+                if (k < 0)
+                        return k;
+        }
 
         return r;
 }
 
-static int add_file(sd_journal *j, const char *prefix, const char *filename) {
+static int add_file_by_name(
+                sd_journal *j,
+                const char *prefix,
+                const char *filename) {
+
         const char *path;
 
         assert(j);
@@ -1350,7 +1380,11 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
         return add_any_file(j, -1, path);
 }
 
-static void remove_file(sd_journal *j, const char *prefix, const char *filename) {
+static void remove_file_by_name(
+                sd_journal *j,
+                const char *prefix,
+                const char *filename) {
+
         const char *path;
         JournalFile *f;
 
@@ -1370,7 +1404,7 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {
         assert(j);
         assert(f);
 
-        ordered_hashmap_remove(j->files, f->path);
+        (void) ordered_hashmap_remove(j->files, f->path);
 
         log_debug("File %s removed.", f->path);
 
@@ -1463,8 +1497,9 @@ static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
         assert(d);
 
         FOREACH_DIRENT_ALL(de, d, goto fail) {
+
                 if (dirent_is_journal_file(de))
-                        (void) add_file(j, m->path, de->d_name);
+                        (void) add_file_by_name(j, m->path, de->d_name);
 
                 if (m->is_root && dirent_is_id128_subdir(de))
                         (void) add_directory(j, m->path, de->d_name);
@@ -1518,10 +1553,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
         /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
          * and reenumerates directory contents */
 
-        if (dirname)
-                path = strjoin(prefix, "/", dirname);
-        else
-                path = strdup(prefix);
+        path = path_join(prefix, dirname);
         if (!path) {
                 r = -ENOMEM;
                 goto fail;
@@ -1699,7 +1731,7 @@ static void remove_directory(sd_journal *j, Directory *d) {
                 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
 
                 if (j->inotify_fd >= 0)
-                        inotify_rm_watch(j->inotify_fd, d->wd);
+                        (void) inotify_rm_watch(j->inotify_fd, d->wd);
         }
 
         hashmap_remove(j->directories_by_path, d->path);
@@ -1773,7 +1805,7 @@ static int allocate_inotify(sd_journal *j) {
 }
 
 static sd_journal *journal_new(int flags, const char *path) {
-        sd_journal *j;
+        _cleanup_(sd_journal_closep) sd_journal *j = NULL;
 
         j = new0(sd_journal, 1);
         if (!j)
@@ -1790,7 +1822,7 @@ static sd_journal *journal_new(int flags, const char *path) {
 
                 t = strdup(path);
                 if (!t)
-                        goto fail;
+                        return NULL;
 
                 if (flags & SD_JOURNAL_OS_ROOT)
                         j->prefix = t;
@@ -1800,19 +1832,15 @@ static sd_journal *journal_new(int flags, const char *path) {
 
         j->files = ordered_hashmap_new(&path_hash_ops);
         if (!j->files)
-                goto fail;
+                return NULL;
 
         j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
         j->directories_by_path = hashmap_new(&path_hash_ops);
         j->mmap = mmap_cache_new();
         if (!j->files_cache || !j->directories_by_path || !j->mmap)
-                goto fail;
-
-        return j;
+                return NULL;
 
-fail:
-        sd_journal_close(j);
-        return NULL;
+        return TAKE_PTR(j);
 }
 
 #define OPEN_ALLOWED_FLAGS                              \
@@ -1821,7 +1849,7 @@ fail:
          SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)
 
 _public_ int sd_journal_open(sd_journal **ret, int flags) {
-        sd_journal *j;
+        _cleanup_(sd_journal_closep) sd_journal *j = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
@@ -1833,15 +1861,10 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) {
 
         r = add_search_paths(j);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = j;
+        *ret = TAKE_PTR(j);
         return 0;
-
-fail:
-        sd_journal_close(j);
-
-        return r;
 }
 
 #define OPEN_CONTAINER_ALLOWED_FLAGS                    \
@@ -1849,7 +1872,7 @@ fail:
 
 _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
         _cleanup_free_ char *root = NULL, *class = NULL;
-        sd_journal *j;
+        _cleanup_(sd_journal_closep) sd_journal *j = NULL;
         char *p;
         int r;
 
@@ -1862,7 +1885,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
         assert_return(machine_name_is_valid(machine), -EINVAL);
 
         p = strjoina("/run/systemd/machines/", machine);
-        r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
+        r = parse_env_file(NULL, p,
+                           "ROOT", &root,
+                           "CLASS", &class);
         if (r == -ENOENT)
                 return -EHOSTDOWN;
         if (r < 0)
@@ -1879,14 +1904,10 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
 
         r = add_search_paths(j);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = j;
+        *ret = TAKE_PTR(j);
         return 0;
-
-fail:
-        sd_journal_close(j);
-        return r;
 }
 
 #define OPEN_DIRECTORY_ALLOWED_FLAGS                    \
@@ -1894,7 +1915,7 @@ fail:
          SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
 
 _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
-        sd_journal *j;
+        _cleanup_(sd_journal_closep) sd_journal *j = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
@@ -1910,18 +1931,14 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
         else
                 r = add_root_directory(j, path, false);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = j;
+        *ret = TAKE_PTR(j);
         return 0;
-
-fail:
-        sd_journal_close(j);
-        return r;
 }
 
 _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
-        sd_journal *j;
+        _cleanup_(sd_journal_closep) sd_journal *j = NULL;
         const char **path;
         int r;
 
@@ -1935,17 +1952,13 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
         STRV_FOREACH(path, paths) {
                 r = add_any_file(j, -1, *path);
                 if (r < 0)
-                        goto fail;
+                        return r;
         }
 
         j->no_new_files = true;
 
-        *ret = j;
+        *ret = TAKE_PTR(j);
         return 0;
-
-fail:
-        sd_journal_close(j);
-        return r;
 }
 
 #define OPEN_DIRECTORY_FD_ALLOWED_FLAGS         \
@@ -1953,7 +1966,7 @@ fail:
          SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
 
 _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
-        sd_journal *j;
+        _cleanup_(sd_journal_closep) sd_journal *j = NULL;
         struct stat st;
         int r;
 
@@ -1978,20 +1991,16 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
         else
                 r = add_root_directory(j, NULL, false);
         if (r < 0)
-                goto fail;
+                return r;
 
-        *ret = j;
+        *ret = TAKE_PTR(j);
         return 0;
-
-fail:
-        sd_journal_close(j);
-        return r;
 }
 
 _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
         Iterator iterator;
         JournalFile *f;
-        sd_journal *j;
+        _cleanup_(sd_journal_closep) sd_journal *j = NULL;
         unsigned i;
         int r;
 
@@ -2016,10 +2025,9 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd
                         goto fail;
                 }
 
-                if (!S_ISREG(st.st_mode)) {
-                        r = -EBADFD;
+                r = stat_verify_regular(&st);
+                if (r < 0)
                         goto fail;
-                }
 
                 r = add_any_file(j, fds[i], NULL);
                 if (r < 0)
@@ -2029,7 +2037,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd
         j->no_new_files = true;
         j->no_inotify = true;
 
-        *ret = j;
+        *ret = TAKE_PTR(j);
         return 0;
 
 fail:
@@ -2038,7 +2046,6 @@ fail:
         ORDERED_HASHMAP_FOREACH(f, j->files, iterator)
                 f->close_fd = false;
 
-        sd_journal_close(j);
         return r;
 }
 
@@ -2495,9 +2502,9 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
                         /* Event for a journal file */
 
                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB))
-                                (void) add_file(j, d->path, e->name);
+                                (void) add_file_by_name(j, d->path, e->name);
                         else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT))
-                                remove_file(j, d->path, e->name);
+                                remove_file_by_name(j, d->path, e->name);
 
                 } else if (!d->is_root && e->len == 0) {
 
@@ -2815,31 +2822,30 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
                         return r;
 
                 /* Let's do the type check by hand, since we used 0 context above. */
-                if (o->object.type != OBJECT_DATA) {
-                        log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
-                                  j->unique_file->path, j->unique_offset,
-                                  o->object.type, OBJECT_DATA);
-                        return -EBADMSG;
-                }
+                if (o->object.type != OBJECT_DATA)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "%s:offset " OFSfmt ": object has type %d, expected %d",
+                                               j->unique_file->path,
+                                               j->unique_offset,
+                                               o->object.type, OBJECT_DATA);
 
                 r = return_data(j, j->unique_file, o, &odata, &ol);
                 if (r < 0)
                         return r;
 
                 /* Check if we have at least the field name and "=". */
-                if (ol <= k) {
-                        log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
-                                  j->unique_file->path, j->unique_offset,
-                                  ol, k + 1);
-                        return -EBADMSG;
-                }
-
-                if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
-                        log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
-                                  j->unique_file->path, j->unique_offset,
-                                  j->unique_field);
-                        return -EBADMSG;
-                }
+                if (ol <= k)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
+                                               j->unique_file->path,
+                                               j->unique_offset, ol, k + 1);
+
+                if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=')
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "%s:offset " OFSfmt ": object does not start with \"%s=\"",
+                                               j->unique_file->path,
+                                               j->unique_offset,
+                                               j->unique_field);
 
                 /* OK, now let's see if we already returned this data
                  * object by checking if it exists in the earlier
@@ -2970,10 +2976,11 @@ _public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
                         return r;
 
                 /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
-                if (o->object.type != OBJECT_FIELD) {
-                        log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
-                        return -EBADMSG;
-                }
+                if (o->object.type != OBJECT_FIELD)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "%s:offset " OFSfmt ": object has type %i, expected %i",
+                                               f->path, j->fields_offset,
+                                               o->object.type, OBJECT_FIELD);
 
                 sz = le64toh(o->object.size) - offsetof(Object, field.payload);