sd-journal.c revision 39887731d4a36292674f92effa30e5941419c201
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/***
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2011 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering***/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <errno.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <fcntl.h>
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering#include <stddef.h>
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <unistd.h>
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include <sys/inotify.h>
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering#include <sys/poll.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <sys/vfs.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include <linux/magic.h>
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include "sd-journal.h"
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen#include "journal-def.h"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include "journal-file.h"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include "hashmap.h"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include "list.h"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include "path-util.h"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include "lookup3.h"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include "compress.h"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#include "journal-internal.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "missing.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "catalog.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "replace-var.h"
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#define JOURNAL_FILES_MAX 1024
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering
3c0cf502796be355431d4a64d738e75f543aa51dLennart Poettering#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering#define REPLACE_VAR_MAX 256
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#define DEFAULT_DATA_THRESHOLD (64*1024)
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/* We return an error here only if we didn't manage to
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering memorize the real error. */
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poetteringstatic int set_put_error(sd_journal *j, int r) {
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering int k;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering if (r >= 0)
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering return r;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering if (k < 0)
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering return k;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering return set_put(j->errors, INT_TO_PTR(r));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic void detach_location(sd_journal *j) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Iterator i;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen JournalFile *f;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering assert(j);
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen j->current_file = NULL;
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen j->current_field = 0;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering HASHMAP_FOREACH(f, j->files, i)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering f->current_offset = 0;
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poetteringstatic void reset_location(sd_journal *j) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering assert(j);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering detach_location(j);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering zero(j->current_location);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering}
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poetteringstatic void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering assert(l);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering assert(f);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering assert(o->object.type == OBJECT_ENTRY);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering l->type = type;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering l->seqnum = le64toh(o->entry.seqnum);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering l->seqnum_id = f->header->seqnum_id;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering l->realtime = le64toh(o->entry.realtime);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering l->monotonic = le64toh(o->entry.monotonic);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering l->boot_id = o->entry.boot_id;
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering l->xor_hash = le64toh(o->entry.xor_hash);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poetteringstatic void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o, uint64_t offset) {
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering assert(j);
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering assert(f);
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering assert(o);
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering init_location(&j->current_location, type, f, o);
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (j->current_file)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering j->current_file->current_offset = 0;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering j->current_file = f;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering j->current_field = 0;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen f->current_offset = offset;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int match_is_valid(const void *data, size_t size) {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen const char *b, *p;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
cab5b05903096e1c9cf5575ccc73f89d15c8db69Lennart Poettering assert(data);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (size < 2)
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return false;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen if (startswith(data, "__"))
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return false;
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering b = data;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering for (p = b; p < b + size; p++) {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (*p == '=')
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return p > b;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (*p == '_')
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering continue;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (*p >= 'A' && *p <= 'Z')
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering continue;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (*p >= '0' && *p <= '9')
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen continue;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return false;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering }
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return false;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering}
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poetteringstatic bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering const uint8_t *a = _a, *b = _b;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering size_t j;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering for (j = 0; j < s && j < t; j++) {
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (a[j] != b[j])
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return false;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering if (a[j] == '=')
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return true;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering }
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return true;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering}
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poetteringstatic Match *match_new(Match *p, MatchType t) {
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering Match *m;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering m = new0(Match, 1);
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (!m)
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return NULL;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering m->type = t;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (p) {
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering m->parent = p;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering LIST_PREPEND(Match, matches, p->matches, m);
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering }
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen return m;
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen}
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poetteringstatic void match_free(Match *m) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering assert(m);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering while (m->matches)
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering match_free(m->matches);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering if (m->parent)
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering LIST_REMOVE(Match, matches, m->parent->matches, m);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering free(m->data);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering free(m);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering}
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poetteringstatic void match_free_if_empty(Match *m) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering assert(m);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering if (m->matches)
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering return;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering match_free(m);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering}
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering Match *l3, *l4, *add_here = NULL, *m;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering le64_t le_hash;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering if (!j)
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering return -EINVAL;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering if (!data)
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering return -EINVAL;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering if (size == 0)
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering size = strlen(data);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering if (!match_is_valid(data, size))
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering return -EINVAL;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering /* level 0: AND term
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering * level 1: OR terms
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering * level 2: AND terms
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering * level 3: OR terms
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen * level 4: concrete matches */
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (!j->level0) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering j->level0 = match_new(NULL, MATCH_AND_TERM);
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering if (!j->level0)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return -ENOMEM;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (!j->level1) {
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen j->level1 = match_new(j->level0, MATCH_OR_TERM);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (!j->level1)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return -ENOMEM;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (!j->level2) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering j->level2 = match_new(j->level1, MATCH_AND_TERM);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (!j->level2)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return -ENOMEM;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering assert(j->level0->type == MATCH_AND_TERM);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering assert(j->level1->type == MATCH_OR_TERM);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering assert(j->level2->type == MATCH_AND_TERM);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering le_hash = htole64(hash64(data, size));
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering LIST_FOREACH(matches, l3, j->level2->matches) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering assert(l3->type == MATCH_OR_TERM);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering LIST_FOREACH(matches, l4, l3->matches) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering assert(l4->type == MATCH_DISCRETE);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering /* Exactly the same match already? Then ignore
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering * this addition */
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (l4->le_hash == le_hash &&
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering l4->size == size &&
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering memcmp(l4->data, data, size) == 0)
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen return 0;
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen /* Same field? Then let's add this to this OR term */
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (same_field(data, size, l4->data, l4->size)) {
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen add_here = l3;
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen break;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen }
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen }
efd46a696d31097c38f653b36921e00c4df62319Lennart Poettering
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (add_here)
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen break;
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen }
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen if (!add_here) {
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen add_here = match_new(j->level2, MATCH_OR_TERM);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (!add_here)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering goto fail;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering m = match_new(add_here, MATCH_DISCRETE);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (!m)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering goto fail;
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering m->le_hash = le_hash;
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering m->size = size;
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering m->data = memdup(data, size);
84129d46cd6e95e142973da93aede4c7433c9600Lennart Poettering if (!m->data)
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen goto fail;
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering detach_location(j);
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen return 0;
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poetteringfail:
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (add_here)
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering match_free_if_empty(add_here);
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen if (j->level2)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering match_free_if_empty(j->level2);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (j->level1)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering match_free_if_empty(j->level1);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (j->level0)
571370c1555d2aa697733479a50957aff024bbcbLennart Poettering match_free_if_empty(j->level0);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return -ENOMEM;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering}
571370c1555d2aa697733479a50957aff024bbcbLennart Poettering
571370c1555d2aa697733479a50957aff024bbcbLennart Poettering_public_ int sd_journal_add_conjunction(sd_journal *j) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering assert(j);
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen if (!j->level0)
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering return 0;
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering if (!j->level1)
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering return 0;
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering if (!j->level1->matches)
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering return 0;
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering j->level1 = NULL;
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering j->level2 = NULL;
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen}
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen_public_ int sd_journal_add_disjunction(sd_journal *j) {
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen assert(j);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (!j->level0)
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (!j->level1)
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (!j->level2)
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (!j->level2->matches)
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen j->level2 = NULL;
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering return 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen}
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poetteringstatic char *match_make_string(Match *m) {
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen char *p, *r;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen Match *i;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen bool enclose = false;
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (!m)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return strdup("");
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (m->type == MATCH_DISCRETE)
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return strndup(m->data, m->size);
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen p = NULL;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen LIST_FOREACH(matches, i, m->matches) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering char *t, *k;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering t = match_make_string(i);
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering if (!t) {
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering free(p);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return NULL;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (p) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
6a1a5eec43892dee3ff6e208bceb1931c25c782eLennart Poettering free(p);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering free(t);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6a1a5eec43892dee3ff6e208bceb1931c25c782eLennart Poettering if (!k)
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return NULL;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering p = k;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering enclose = true;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering } else {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering free(p);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering p = t;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (enclose) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering r = strjoin("(", p, ")", NULL);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering free(p);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return r;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering }
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return p;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering}
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poetteringchar *journal_make_match_string(sd_journal *j) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering assert(j);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return match_make_string(j->level0);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering}
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering_public_ void sd_journal_flush_matches(sd_journal *j) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (!j)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering return;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (j->level0)
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering match_free(j->level0);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering j->level0 = j->level1 = j->level2 = NULL;
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering detach_location(j);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering}
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poetteringstatic int compare_entry_order(JournalFile *af, Object *_ao,
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering JournalFile *bf, uint64_t bp) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering uint64_t a, b;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen Object *ao, *bo;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen int r;
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen assert(af);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen assert(bf);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering assert(_ao);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering /* The mmap cache might invalidate the object from the first
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering * file if we look at the one from the second file. Hence
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering * temporarily copy the header of the first one, and look at
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering * that only. */
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering ao = alloca(offsetof(EntryObject, items));
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering memcpy(ao, _ao, offsetof(EntryObject, items));
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering if (r < 0)
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering return strcmp(af->path, bf->path);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering /* We operate on two different files here, hence we can access
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering * two objects at the same time, which we normally can't.
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering *
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering * If contents and timestamps match, these entries are
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering * identical, even if the seqnum does not match */
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering ao->entry.monotonic == bo->entry.monotonic &&
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering ao->entry.realtime == bo->entry.realtime &&
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering ao->entry.xor_hash == bo->entry.xor_hash)
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering return 0;
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering /* If this is from the same seqnum source, compare
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen * seqnums */
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering a = le64toh(ao->entry.seqnum);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering b = le64toh(bo->entry.seqnum);
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (a < b)
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen return -1;
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen if (a > b)
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering return 1;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt /* Wow! This is weird, different data but the same
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering * seqnums? Something is borked, but let's make the
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering * best of it and compare by time. */
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering }
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering /* If the boot id matches compare monotonic time */
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering a = le64toh(ao->entry.monotonic);
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering b = le64toh(bo->entry.monotonic);
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt if (a < b)
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt return -1;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt if (a > b)
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt return 1;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering }
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering /* Otherwise compare UTC time */
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering a = le64toh(ao->entry.realtime);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering b = le64toh(bo->entry.realtime);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (a < b)
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering return -1;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (a > b)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return 1;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering /* Finally, compare by contents */
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering a = le64toh(ao->entry.xor_hash);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering b = le64toh(bo->entry.xor_hash);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering if (a < b)
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering return -1;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (a > b)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return 1;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return 0;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering}
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering uint64_t a;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering assert(af);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering assert(ao);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering assert(l);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (l->monotonic_set &&
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering l->realtime_set &&
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering le64toh(ao->entry.realtime) == l->realtime &&
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering l->xor_hash_set &&
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering le64toh(ao->entry.xor_hash) == l->xor_hash)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return 0;
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (l->seqnum_set &&
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering a = le64toh(ao->entry.seqnum);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (a < l->seqnum)
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering return -1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (a > l->seqnum)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return 1;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering }
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (l->monotonic_set &&
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering a = le64toh(ao->entry.monotonic);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (a < l->monotonic)
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering return -1;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (a > l->monotonic)
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering return 1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering }
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (l->realtime_set) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering a = le64toh(ao->entry.realtime);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (a < l->realtime)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return -1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (a > l->realtime)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return 1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering }
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (l->xor_hash_set) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering a = le64toh(ao->entry.xor_hash);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (a < l->xor_hash)
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return -1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (a > l->xor_hash)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return 1;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering }
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return 0;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering}
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poetteringstatic int next_for_match(
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering sd_journal *j,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering Match *m,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering JournalFile *f,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering uint64_t after_offset,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering direction_t direction,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering Object **ret,
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering uint64_t *offset) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering int r;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering uint64_t np = 0;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering Object *n;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering assert(j);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering assert(m);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering assert(f);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (m->type == MATCH_DISCRETE) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering uint64_t dp;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r <= 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return r;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering } else if (m->type == MATCH_OR_TERM) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering Match *i;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering /* Find the earliest match beyond after_offset */
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering LIST_FOREACH(matches, i, m->matches) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering uint64_t cp;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (r < 0)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return r;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering else if (r > 0) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering np = cp;
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering }
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering }
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering } else if (m->type == MATCH_AND_TERM) {
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering Match *i;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering bool continue_looking;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering /* Always jump to the next matching entry and repeat
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering * this until we fine and offset that matches for all
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering * matches. */
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering if (!m->matches)
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering return 0;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen np = 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen do {
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen continue_looking = false;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen
9c5e12a4314e7192e834e1b855e5e80111e636a6Tom Gundersen LIST_FOREACH(matches, i, m->matches) {
7586f4d172dd9c3ccc3126fc47dca9e49adec132Tom Gundersen uint64_t cp, limit;
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (np == 0)
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen limit = after_offset;
else if (direction == DIRECTION_DOWN)
limit = MAX(np, after_offset);
else
limit = MIN(np, after_offset);
r = next_for_match(j, i, f, limit, direction, NULL, &cp);
if (r <= 0)
return r;
if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
(np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))) {
np = cp;
continue_looking = true;
}
}
} while (continue_looking);
}
if (np == 0)
return 0;
r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
if (r < 0)
return r;
if (ret)
*ret = n;
if (offset)
*offset = np;
return 1;
}
static int find_location_for_match(
sd_journal *j,
Match *m,
JournalFile *f,
direction_t direction,
Object **ret,
uint64_t *offset) {
int r;
assert(j);
assert(m);
assert(f);
if (m->type == MATCH_DISCRETE) {
uint64_t dp;
r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
if (r <= 0)
return r;
/* FIXME: missing: find by monotonic */
if (j->current_location.type == LOCATION_HEAD)
return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
if (j->current_location.type == LOCATION_TAIL)
return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
if (j->current_location.monotonic_set) {
r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
if (r != -ENOENT)
return r;
}
if (j->current_location.realtime_set)
return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
} else if (m->type == MATCH_OR_TERM) {
uint64_t np = 0;
Object *n;
Match *i;
/* Find the earliest match */
LIST_FOREACH(matches, i, m->matches) {
uint64_t cp;
r = find_location_for_match(j, i, f, direction, NULL, &cp);
if (r < 0)
return r;
else if (r > 0) {
if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
np = cp;
}
}
if (np == 0)
return 0;
r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
if (r < 0)
return r;
if (ret)
*ret = n;
if (offset)
*offset = np;
return 1;
} else {
Match *i;
uint64_t np = 0;
assert(m->type == MATCH_AND_TERM);
/* First jump to the last match, and then find the
* next one where all matches match */
if (!m->matches)
return 0;
LIST_FOREACH(matches, i, m->matches) {
uint64_t cp;
r = find_location_for_match(j, i, f, direction, NULL, &cp);
if (r <= 0)
return r;
if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp))
np = cp;
}
return next_for_match(j, m, f, np, direction, ret, offset);
}
}
static int find_location_with_matches(
sd_journal *j,
JournalFile *f,
direction_t direction,
Object **ret,
uint64_t *offset) {
int r;
assert(j);
assert(f);
assert(ret);
assert(offset);
if (!j->level0) {
/* No matches is simple */
if (j->current_location.type == LOCATION_HEAD)
return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
if (j->current_location.type == LOCATION_TAIL)
return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
if (j->current_location.monotonic_set) {
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
if (r != -ENOENT)
return r;
}
if (j->current_location.realtime_set)
return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
} else
return find_location_for_match(j, j->level0, f, direction, ret, offset);
}
static int next_with_matches(
sd_journal *j,
JournalFile *f,
direction_t direction,
Object **ret,
uint64_t *offset) {
Object *c;
uint64_t cp;
assert(j);
assert(f);
assert(ret);
assert(offset);
c = *ret;
cp = *offset;
/* No matches is easy. We simple advance the file
* pointer by one. */
if (!j->level0)
return journal_file_next_entry(f, c, cp, direction, ret, offset);
/* If we have a match then we look for the next matching entry
* with an offset at least one step larger */
return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
}
static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
Object *c;
uint64_t cp;
int r;
assert(j);
assert(f);
if (f->current_offset > 0) {
cp = f->current_offset;
r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
if (r < 0)
return r;
r = next_with_matches(j, f, direction, &c, &cp);
if (r <= 0)
return r;
} else {
r = find_location_with_matches(j, f, direction, &c, &cp);
if (r <= 0)
return r;
}
/* OK, we found the spot, now let's advance until to an entry
* that is actually different from what we were previously
* looking at. This is necessary to handle entries which exist
* in two (or more) journal files, and which shall all be
* suppressed but one. */
for (;;) {
bool found;
if (j->current_location.type == LOCATION_DISCRETE) {
int k;
k = compare_with_location(f, c, &j->current_location);
if (direction == DIRECTION_DOWN)
found = k > 0;
else
found = k < 0;
} else
found = true;
if (found) {
if (ret)
*ret = c;
if (offset)
*offset = cp;
return 1;
}
r = next_with_matches(j, f, direction, &c, &cp);
if (r <= 0)
return r;
}
}
static int real_journal_next(sd_journal *j, direction_t direction) {
JournalFile *f, *new_file = NULL;
uint64_t new_offset = 0;
Object *o;
uint64_t p;
Iterator i;
int r;
if (!j)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
bool found;
r = next_beyond_location(j, f, direction, &o, &p);
if (r < 0) {
log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
continue;
} else if (r == 0)
continue;
if (!new_file)
found = true;
else {
int k;
k = compare_entry_order(f, o, new_file, new_offset);
if (direction == DIRECTION_DOWN)
found = k < 0;
else
found = k > 0;
}
if (found) {
new_file = f;
new_offset = p;
}
}
if (!new_file)
return 0;
r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
if (r < 0)
return r;
set_location(j, LOCATION_DISCRETE, new_file, o, new_offset);
return 1;
}
_public_ int sd_journal_next(sd_journal *j) {
return real_journal_next(j, DIRECTION_DOWN);
}
_public_ int sd_journal_previous(sd_journal *j) {
return real_journal_next(j, DIRECTION_UP);
}
static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
int c = 0, r;
if (!j)
return -EINVAL;
if (skip == 0) {
/* If this is not a discrete skip, then at least
* resolve the current location */
if (j->current_location.type != LOCATION_DISCRETE)
return real_journal_next(j, direction);
return 0;
}
do {
r = real_journal_next(j, direction);
if (r < 0)
return r;
if (r == 0)
return c;
skip--;
c++;
} while (skip > 0);
return c;
}
_public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
return real_journal_next_skip(j, DIRECTION_DOWN, skip);
}
_public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
return real_journal_next_skip(j, DIRECTION_UP, skip);
}
_public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
Object *o;
int r;
char bid[33], sid[33];
if (!j)
return -EINVAL;
if (!cursor)
return -EINVAL;
if (!j->current_file || j->current_file->current_offset <= 0)
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
if (r < 0)
return r;
sd_id128_to_string(j->current_file->header->seqnum_id, sid);
sd_id128_to_string(o->entry.boot_id, bid);
if (asprintf(cursor,
"s=%s;i=%llx;b=%s;m=%llx;t=%llx;x=%llx",
sid, (unsigned long long) le64toh(o->entry.seqnum),
bid, (unsigned long long) le64toh(o->entry.monotonic),
(unsigned long long) le64toh(o->entry.realtime),
(unsigned long long) le64toh(o->entry.xor_hash)) < 0)
return -ENOMEM;
return 0;
}
_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
char *w, *state;
size_t l;
unsigned long long seqnum, monotonic, realtime, xor_hash;
bool
seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
sd_id128_t seqnum_id, boot_id;
if (!j)
return -EINVAL;
if (isempty(cursor))
return -EINVAL;
FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
char *item;
int k = 0;
if (l < 2 || w[1] != '=')
return -EINVAL;
item = strndup(w, l);
if (!item)
return -ENOMEM;
switch (w[0]) {
case 's':
seqnum_id_set = true;
k = sd_id128_from_string(item+2, &seqnum_id);
break;
case 'i':
seqnum_set = true;
if (sscanf(item+2, "%llx", &seqnum) != 1)
k = -EINVAL;
break;
case 'b':
boot_id_set = true;
k = sd_id128_from_string(item+2, &boot_id);
break;
case 'm':
monotonic_set = true;
if (sscanf(item+2, "%llx", &monotonic) != 1)
k = -EINVAL;
break;
case 't':
realtime_set = true;
if (sscanf(item+2, "%llx", &realtime) != 1)
k = -EINVAL;
break;
case 'x':
xor_hash_set = true;
if (sscanf(item+2, "%llx", &xor_hash) != 1)
k = -EINVAL;
break;
}
free(item);
if (k < 0)
return k;
}
if ((!seqnum_set || !seqnum_id_set) &&
(!monotonic_set || !boot_id_set) &&
!realtime_set)
return -EINVAL;
reset_location(j);
j->current_location.type = LOCATION_SEEK;
if (realtime_set) {
j->current_location.realtime = (uint64_t) realtime;
j->current_location.realtime_set = true;
}
if (seqnum_set && seqnum_id_set) {
j->current_location.seqnum = (uint64_t) seqnum;
j->current_location.seqnum_id = seqnum_id;
j->current_location.seqnum_set = true;
}
if (monotonic_set && boot_id_set) {
j->current_location.monotonic = (uint64_t) monotonic;
j->current_location.boot_id = boot_id;
j->current_location.monotonic_set = true;
}
if (xor_hash_set) {
j->current_location.xor_hash = (uint64_t) xor_hash;
j->current_location.xor_hash_set = true;
}
return 0;
}
_public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
int r;
char *w, *state;
size_t l;
Object *o;
if (!j)
return -EINVAL;
if (isempty(cursor))
return -EINVAL;
if (!j->current_file || j->current_file->current_offset <= 0)
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
if (r < 0)
return r;
FOREACH_WORD_SEPARATOR(w, l, cursor, ";", state) {
_cleanup_free_ char *item = NULL;
sd_id128_t id;
unsigned long long ll;
int k = 0;
if (l < 2 || w[1] != '=')
return -EINVAL;
item = strndup(w, l);
if (!item)
return -ENOMEM;
switch (w[0]) {
case 's':
k = sd_id128_from_string(item+2, &id);
if (k < 0)
return k;
if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
return 0;
break;
case 'i':
if (sscanf(item+2, "%llx", &ll) != 1)
return -EINVAL;
if (ll != le64toh(o->entry.seqnum))
return 0;
break;
case 'b':
k = sd_id128_from_string(item+2, &id);
if (k < 0)
return k;
if (!sd_id128_equal(id, o->entry.boot_id))
return 0;
break;
case 'm':
if (sscanf(item+2, "%llx", &ll) != 1)
return -EINVAL;
if (ll != le64toh(o->entry.monotonic))
return 0;
break;
case 't':
if (sscanf(item+2, "%llx", &ll) != 1)
return -EINVAL;
if (ll != le64toh(o->entry.realtime))
return 0;
break;
case 'x':
if (sscanf(item+2, "%llx", &ll) != 1)
return -EINVAL;
if (ll != le64toh(o->entry.xor_hash))
return 0;
break;
}
}
return 1;
}
_public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
if (!j)
return -EINVAL;
reset_location(j);
j->current_location.type = LOCATION_SEEK;
j->current_location.boot_id = boot_id;
j->current_location.monotonic = usec;
j->current_location.monotonic_set = true;
return 0;
}
_public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
if (!j)
return -EINVAL;
reset_location(j);
j->current_location.type = LOCATION_SEEK;
j->current_location.realtime = usec;
j->current_location.realtime_set = true;
return 0;
}
_public_ int sd_journal_seek_head(sd_journal *j) {
if (!j)
return -EINVAL;
reset_location(j);
j->current_location.type = LOCATION_HEAD;
return 0;
}
_public_ int sd_journal_seek_tail(sd_journal *j) {
if (!j)
return -EINVAL;
reset_location(j);
j->current_location.type = LOCATION_TAIL;
return 0;
}
static void check_network(sd_journal *j, int fd) {
struct statfs sfs;
assert(j);
if (j->on_network)
return;
if (fstatfs(fd, &sfs) < 0)
return;
j->on_network =
F_TYPE_CMP(sfs.f_type, CIFS_MAGIC_NUMBER) ||
F_TYPE_CMP(sfs.f_type, CODA_SUPER_MAGIC) ||
F_TYPE_CMP(sfs.f_type, NCP_SUPER_MAGIC) ||
F_TYPE_CMP(sfs.f_type, NFS_SUPER_MAGIC) ||
F_TYPE_CMP(sfs.f_type, SMB_SUPER_MAGIC);
}
static int add_file(sd_journal *j, const char *prefix, const char *filename) {
_cleanup_free_ char *path = NULL;
int r;
JournalFile *f;
assert(j);
assert(prefix);
assert(filename);
if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
!(streq(filename, "system.journal") ||
streq(filename, "system.journal~") ||
(startswith(filename, "system@") &&
(endswith(filename, ".journal") || endswith(filename, ".journal~")))))
return 0;
path = strjoin(prefix, "/", filename, NULL);
if (!path)
return -ENOMEM;
if (hashmap_get(j->files, path))
return 0;
if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
log_debug("Too many open journal files, not adding %s, ignoring.", path);
return set_put_error(j, -ETOOMANYREFS);
}
r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
if (r < 0) {
if (errno == ENOENT)
return 0;
return r;
}
/* journal_file_dump(f); */
r = hashmap_put(j->files, f->path, f);
if (r < 0) {
journal_file_close(f);
return r;
}
log_debug("File %s got added.", f->path);
check_network(j, f->fd);
j->current_invalidate_counter ++;
return 0;
}
static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
char *path;
JournalFile *f;
assert(j);
assert(prefix);
assert(filename);
path = strjoin(prefix, "/", filename, NULL);
if (!path)
return -ENOMEM;
f = hashmap_get(j->files, path);
free(path);
if (!f)
return 0;
hashmap_remove(j->files, f->path);
log_debug("File %s got removed.", f->path);
if (j->current_file == f) {
j->current_file = NULL;
j->current_field = 0;
}
if (j->unique_file == f) {
j->unique_file = NULL;
j->unique_offset = 0;
}
journal_file_close(f);
j->current_invalidate_counter ++;
return 0;
}
static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
_cleanup_free_ char *path = NULL;
int r;
_cleanup_closedir_ DIR *d = NULL;
sd_id128_t id, mid;
Directory *m;
assert(j);
assert(prefix);
assert(dirname);
log_debug("Considering %s/%s.", prefix, dirname);
if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
(sd_id128_from_string(dirname, &id) < 0 ||
sd_id128_get_machine(&mid) < 0 ||
!(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
return 0;
path = strjoin(prefix, "/", dirname, NULL);
if (!path)
return -ENOMEM;
d = opendir(path);
if (!d) {
log_debug("Failed to open %s: %m", path);
if (errno == ENOENT)
return 0;
return -errno;
}
m = hashmap_get(j->directories_by_path, path);
if (!m) {
m = new0(Directory, 1);
if (!m)
return -ENOMEM;
m->is_root = false;
m->path = path;
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
free(m);
return -ENOMEM;
}
path = NULL; /* avoid freeing in cleanup */
j->current_invalidate_counter ++;
log_debug("Directory %s got added.", m->path);
} else if (m->is_root)
return 0;
if (m->wd <= 0 && j->inotify_fd >= 0) {
m->wd = inotify_add_watch(j->inotify_fd, m->path,
IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
IN_ONLYDIR);
if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
inotify_rm_watch(j->inotify_fd, m->wd);
}
for (;;) {
struct dirent *de;
union dirent_storage buf;
r = readdir_r(d, &buf.de, &de);
if (r != 0 || !de)
break;
if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~")) {
r = add_file(j, m->path, de->d_name);
if (r < 0) {
log_debug("Failed to add file %s/%s: %s",
m->path, de->d_name, strerror(-r));
r = set_put_error(j, r);
if (r < 0)
return r;
}
}
}
check_network(j, dirfd(d));
return 0;
}
static int add_root_directory(sd_journal *j, const char *p) {
_cleanup_closedir_ DIR *d = NULL;
Directory *m;
int r;
assert(j);
assert(p);
if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
!path_startswith(p, "/run"))
return -EINVAL;
d = opendir(p);
if (!d)
return -errno;
m = hashmap_get(j->directories_by_path, p);
if (!m) {
m = new0(Directory, 1);
if (!m)
return -ENOMEM;
m->is_root = true;
m->path = strdup(p);
if (!m->path) {
free(m);
return -ENOMEM;
}
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
free(m->path);
free(m);
return -ENOMEM;
}
j->current_invalidate_counter ++;
log_debug("Root directory %s got added.", m->path);
} else if (!m->is_root)
return 0;
if (m->wd <= 0 && j->inotify_fd >= 0) {
m->wd = inotify_add_watch(j->inotify_fd, m->path,
IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
IN_ONLYDIR);
if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
inotify_rm_watch(j->inotify_fd, m->wd);
}
for (;;) {
struct dirent *de;
union dirent_storage buf;
sd_id128_t id;
r = readdir_r(d, &buf.de, &de);
if (r != 0 || !de)
break;
if (dirent_is_file_with_suffix(de, ".journal") ||
dirent_is_file_with_suffix(de, ".journal~")) {
r = add_file(j, m->path, de->d_name);
if (r < 0) {
log_debug("Failed to add file %s/%s: %s",
m->path, de->d_name, strerror(-r));
r = set_put_error(j, r);
if (r < 0)
return r;
}
} else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
sd_id128_from_string(de->d_name, &id) >= 0) {
r = add_directory(j, m->path, de->d_name);
if (r < 0)
log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r));
}
}
check_network(j, dirfd(d));
return 0;
}
static int remove_directory(sd_journal *j, Directory *d) {
assert(j);
if (d->wd > 0) {
hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
if (j->inotify_fd >= 0)
inotify_rm_watch(j->inotify_fd, d->wd);
}
hashmap_remove(j->directories_by_path, d->path);
if (d->is_root)
log_debug("Root directory %s got removed.", d->path);
else
log_debug("Directory %s got removed.", d->path);
free(d->path);
free(d);
return 0;
}
static int add_search_paths(sd_journal *j) {
int r;
const char search_paths[] =
"/run/log/journal\0"
"/var/log/journal\0";
const char *p;
assert(j);
/* We ignore most errors here, since the idea is to only open
* what's actually accessible, and ignore the rest. */
NULSTR_FOREACH(p, search_paths) {
r = add_root_directory(j, p);
if (r < 0 && r != -ENOENT) {
r = set_put_error(j, r);
if (r < 0)
return r;
}
}
return 0;
}
static int allocate_inotify(sd_journal *j) {
assert(j);
if (j->inotify_fd < 0) {
j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
if (j->inotify_fd < 0)
return -errno;
}
if (!j->directories_by_wd) {
j->directories_by_wd = hashmap_new(trivial_hash_func, trivial_compare_func);
if (!j->directories_by_wd)
return -ENOMEM;
}
return 0;
}
static sd_journal *journal_new(int flags, const char *path) {
sd_journal *j;
j = new0(sd_journal, 1);
if (!j)
return NULL;
j->inotify_fd = -1;
j->flags = flags;
j->data_threshold = DEFAULT_DATA_THRESHOLD;
if (path) {
j->path = strdup(path);
if (!j->path)
goto fail;
}
j->files = hashmap_new(string_hash_func, string_compare_func);
j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
j->mmap = mmap_cache_new();
if (!j->files || !j->directories_by_path || !j->mmap)
goto fail;
return j;
fail:
sd_journal_close(j);
return NULL;
}
_public_ int sd_journal_open(sd_journal **ret, int flags) {
sd_journal *j;
int r;
if (!ret)
return -EINVAL;
if (flags & ~(SD_JOURNAL_LOCAL_ONLY|
SD_JOURNAL_RUNTIME_ONLY|
SD_JOURNAL_SYSTEM_ONLY))
return -EINVAL;
j = journal_new(flags, NULL);
if (!j)
return -ENOMEM;
r = add_search_paths(j);
if (r < 0)
goto fail;
*ret = j;
return 0;
fail:
sd_journal_close(j);
return r;
}
_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
sd_journal *j;
int r;
if (!ret)
return -EINVAL;
if (!path)
return -EINVAL;
if (flags != 0)
return -EINVAL;
j = journal_new(flags, path);
if (!j)
return -ENOMEM;
r = add_root_directory(j, path);
if (r < 0) {
set_put_error(j, r);
goto fail;
}
*ret = j;
return 0;
fail:
sd_journal_close(j);
return r;
}
_public_ void sd_journal_close(sd_journal *j) {
Directory *d;
JournalFile *f;
if (!j)
return;
sd_journal_flush_matches(j);
while ((f = hashmap_steal_first(j->files)))
journal_file_close(f);
hashmap_free(j->files);
while ((d = hashmap_first(j->directories_by_path)))
remove_directory(j, d);
while ((d = hashmap_first(j->directories_by_wd)))
remove_directory(j, d);
hashmap_free(j->directories_by_path);
hashmap_free(j->directories_by_wd);
if (j->inotify_fd >= 0)
close_nointr_nofail(j->inotify_fd);
if (j->mmap)
mmap_cache_unref(j->mmap);
free(j->path);
free(j->unique_field);
set_free(j->errors);
free(j);
}
_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
Object *o;
JournalFile *f;
int r;
if (!j)
return -EINVAL;
if (!ret)
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0)
return r;
*ret = le64toh(o->entry.realtime);
return 0;
}
_public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
Object *o;
JournalFile *f;
int r;
sd_id128_t id;
if (!j)
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0)
return r;
if (ret_boot_id)
*ret_boot_id = o->entry.boot_id;
else {
r = sd_id128_get_boot(&id);
if (r < 0)
return r;
if (!sd_id128_equal(id, o->entry.boot_id))
return -ESTALE;
}
if (ret)
*ret = le64toh(o->entry.monotonic);
return 0;
}
static bool field_is_valid(const char *field) {
const char *p;
assert(field);
if (isempty(field))
return false;
if (startswith(field, "__"))
return false;
for (p = field; *p; p++) {
if (*p == '_')
continue;
if (*p >= 'A' && *p <= 'Z')
continue;
if (*p >= '0' && *p <= '9')
continue;
return false;
}
return true;
}
_public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
JournalFile *f;
uint64_t i, n;
size_t field_length;
int r;
Object *o;
if (!j)
return -EINVAL;
if (!field)
return -EINVAL;
if (!data)
return -EINVAL;
if (!size)
return -EINVAL;
if (!field_is_valid(field))
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0)
return r;
field_length = strlen(field);
n = journal_file_entry_n_items(o);
for (i = 0; i < n; i++) {
uint64_t p, l;
le64_t le_hash;
size_t t;
p = le64toh(o->entry.items[i].object_offset);
le_hash = o->entry.items[i].hash;
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
if (le_hash != o->data.hash)
return -EBADMSG;
l = le64toh(o->object.size) - offsetof(Object, data.payload);
if (o->object.flags & OBJECT_COMPRESSED) {
#ifdef HAVE_XZ
if (uncompress_startswith(o->data.payload, l,
&f->compress_buffer, &f->compress_buffer_size,
field, field_length, '=')) {
uint64_t rsize;
if (!uncompress_blob(o->data.payload, l,
&f->compress_buffer, &f->compress_buffer_size, &rsize,
j->data_threshold))
return -EBADMSG;
*data = f->compress_buffer;
*size = (size_t) rsize;
return 0;
}
#else
return -EPROTONOSUPPORT;
#endif
} else if (l >= field_length+1 &&
memcmp(o->data.payload, field, field_length) == 0 &&
o->data.payload[field_length] == '=') {
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
*data = o->data.payload;
*size = t;
return 0;
}
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0)
return r;
}
return -ENOENT;
}
static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
size_t t;
uint64_t l;
l = le64toh(o->object.size) - offsetof(Object, data.payload);
t = (size_t) l;
/* We can't read objects larger than 4G on a 32bit machine */
if ((uint64_t) t != l)
return -E2BIG;
if (o->object.flags & OBJECT_COMPRESSED) {
#ifdef HAVE_XZ
uint64_t rsize;
if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, j->data_threshold))
return -EBADMSG;
*data = f->compress_buffer;
*size = (size_t) rsize;
#else
return -EPROTONOSUPPORT;
#endif
} else {
*data = o->data.payload;
*size = t;
}
return 0;
}
_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
JournalFile *f;
uint64_t p, n;
le64_t le_hash;
int r;
Object *o;
if (!j)
return -EINVAL;
if (!data)
return -EINVAL;
if (!size)
return -EINVAL;
f = j->current_file;
if (!f)
return -EADDRNOTAVAIL;
if (f->current_offset <= 0)
return -EADDRNOTAVAIL;
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0)
return r;
n = journal_file_entry_n_items(o);
if (j->current_field >= n)
return 0;
p = le64toh(o->entry.items[j->current_field].object_offset);
le_hash = o->entry.items[j->current_field].hash;
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
if (le_hash != o->data.hash)
return -EBADMSG;
r = return_data(j, f, o, data, size);
if (r < 0)
return r;
j->current_field ++;
return 1;
}
_public_ void sd_journal_restart_data(sd_journal *j) {
if (!j)
return;
j->current_field = 0;
}
_public_ int sd_journal_get_fd(sd_journal *j) {
int r;
if (!j)
return -EINVAL;
if (j->inotify_fd >= 0)
return j->inotify_fd;
r = allocate_inotify(j);
if (r < 0)
return r;
/* Iterate through all dirs again, to add them to the
* inotify */
if (j->path)
r = add_root_directory(j, j->path);
else
r = add_search_paths(j);
if (r < 0)
return r;
return j->inotify_fd;
}
_public_ int sd_journal_get_events(sd_journal *j) {
int fd;
if (!j)
return -EINVAL;
fd = sd_journal_get_fd(j);
if (fd < 0)
return fd;
return POLLIN;
}
_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
int fd;
if (!j)
return -EINVAL;
if (!timeout_usec)
return -EINVAL;
fd = sd_journal_get_fd(j);
if (fd < 0)
return fd;
if (!j->on_network) {
*timeout_usec = (uint64_t) -1;
return 0;
}
/* If we are on the network we need to regularly check for
* changes manually */
*timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
return 1;
}
static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
Directory *d;
int r;
assert(j);
assert(e);
/* Is this a subdirectory we watch? */
d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
if (d) {
sd_id128_t id;
if (!(e->mask & IN_ISDIR) && e->len > 0 &&
(endswith(e->name, ".journal") ||
endswith(e->name, ".journal~"))) {
/* Event for a journal file */
if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
r = add_file(j, d->path, e->name);
if (r < 0) {
log_debug("Failed to add file %s/%s: %s",
d->path, e->name, strerror(-r));
set_put_error(j, r);
}
} else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
r = remove_file(j, d->path, e->name);
if (r < 0)
log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r));
}
} else if (!d->is_root && e->len == 0) {
/* Event for a subdirectory */
if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
r = remove_directory(j, d);
if (r < 0)
log_debug("Failed to remove directory %s: %s", d->path, strerror(-r));
}
} else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
/* Event for root directory */
if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
r = add_directory(j, d->path, e->name);
if (r < 0)
log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r));
}
}
return;
}
if (e->mask & IN_IGNORED)
return;
log_warning("Unknown inotify event.");
}
static int determine_change(sd_journal *j) {
bool b;
assert(j);
b = j->current_invalidate_counter != j->last_invalidate_counter;
j->last_invalidate_counter = j->current_invalidate_counter;
return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
}
_public_ int sd_journal_process(sd_journal *j) {
uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event);
bool got_something = false;
if (!j)
return -EINVAL;
j->last_process_usec = now(CLOCK_MONOTONIC);
for (;;) {
struct inotify_event *e;
ssize_t l;
l = read(j->inotify_fd, buffer, sizeof(buffer));
if (l < 0) {
if (errno == EAGAIN || errno == EINTR)
return got_something ? determine_change(j) : SD_JOURNAL_NOP;
return -errno;
}
got_something = true;
e = (struct inotify_event*) buffer;
while (l > 0) {
size_t step;
process_inotify_event(j, e);
step = sizeof(struct inotify_event) + e->len;
assert(step <= (size_t) l);
e = (struct inotify_event*) ((uint8_t*) e + step);
l -= step;
}
}
return determine_change(j);
}
_public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
int r;
uint64_t t;
assert(j);
if (j->inotify_fd < 0) {
/* This is the first invocation, hence create the
* inotify watch */
r = sd_journal_get_fd(j);
if (r < 0)
return r;
/* The journal might have changed since the context
* object was created and we weren't watching before,
* hence don't wait for anything, and return
* immediately. */
return determine_change(j);
}
r = sd_journal_get_timeout(j, &t);
if (r < 0)
return r;
if (t != (uint64_t) -1) {
usec_t n;
n = now(CLOCK_MONOTONIC);
t = t > n ? t - n : 0;
if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
timeout_usec = t;
}
do {
r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
} while (r == -EINTR);
if (r < 0)
return r;
return sd_journal_process(j);
}
_public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
Iterator i;
JournalFile *f;
bool first = true;
int r;
if (!j)
return -EINVAL;
if (!from && !to)
return -EINVAL;
if (from == to)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
usec_t fr, t;
r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
if (r == 0)
continue;
if (first) {
if (from)
*from = fr;
if (to)
*to = t;
first = false;
} else {
if (from)
*from = MIN(fr, *from);
if (to)
*to = MAX(t, *to);
}
}
return first ? 0 : 1;
}
_public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
Iterator i;
JournalFile *f;
bool first = true;
int r;
if (!j)
return -EINVAL;
if (!from && !to)
return -EINVAL;
if (from == to)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
usec_t fr, t;
r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
if (r == 0)
continue;
if (first) {
if (from)
*from = fr;
if (to)
*to = t;
first = false;
} else {
if (from)
*from = MIN(fr, *from);
if (to)
*to = MAX(t, *to);
}
}
return first ? 0 : 1;
}
void journal_print_header(sd_journal *j) {
Iterator i;
JournalFile *f;
bool newline = false;
assert(j);
HASHMAP_FOREACH(f, j->files, i) {
if (newline)
putchar('\n');
else
newline = true;
journal_file_print_header(f);
}
}
_public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
Iterator i;
JournalFile *f;
uint64_t sum = 0;
if (!j)
return -EINVAL;
if (!bytes)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
struct stat st;
if (fstat(f->fd, &st) < 0)
return -errno;
sum += (uint64_t) st.st_blocks * 512ULL;
}
*bytes = sum;
return 0;
}
_public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
char *f;
if (!j)
return -EINVAL;
if (isempty(field))
return -EINVAL;
if (!field_is_valid(field))
return -EINVAL;
f = strdup(field);
if (!f)
return -ENOMEM;
free(j->unique_field);
j->unique_field = f;
j->unique_file = NULL;
j->unique_offset = 0;
return 0;
}
_public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
Object *o;
size_t k;
int r;
if (!j)
return -EINVAL;
if (!data)
return -EINVAL;
if (!l)
return -EINVAL;
if (!j->unique_field)
return -EINVAL;
k = strlen(j->unique_field);
if (!j->unique_file) {
j->unique_file = hashmap_first(j->files);
if (!j->unique_file)
return 0;
j->unique_offset = 0;
}
for (;;) {
JournalFile *of;
Iterator i;
const void *odata;
size_t ol;
bool found;
/* Proceed to next data object in the field's linked list */
if (j->unique_offset == 0) {
r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
if (r < 0)
return r;
j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
} else {
r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
if (r < 0)
return r;
j->unique_offset = le64toh(o->data.next_field_offset);
}
/* We reached the end of the list? Then start again, with the next file */
if (j->unique_offset == 0) {
JournalFile *n;
n = hashmap_next(j->files, j->unique_file->path);
if (!n)
return 0;
j->unique_file = n;
continue;
}
/* We do not use the type context here, but 0 instead,
* so that we can look at this data object at the same
* time as one on another file */
r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
if (r < 0)
return r;
/* Let's do the type check by hand, since we used 0 context above. */
if (o->object.type != OBJECT_DATA)
return -EBADMSG;
r = return_data(j, j->unique_file, o, &odata, &ol);
if (r < 0)
return r;
/* OK, now let's see if we already returned this data
* object by checking if it exists in the earlier
* traversed files. */
found = false;
HASHMAP_FOREACH(of, j->files, i) {
Object *oo;
uint64_t op;
if (of == j->unique_file)
break;
/* Skip this file it didn't have any fields
* indexed */
if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
le64toh(of->header->n_fields) <= 0)
continue;
r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
if (r < 0)
return r;
if (r > 0)
found = true;
}
if (found)
continue;
r = return_data(j, j->unique_file, o, data, l);
if (r < 0)
return r;
return 1;
}
}
_public_ void sd_journal_restart_unique(sd_journal *j) {
if (!j)
return;
j->unique_file = NULL;
j->unique_offset = 0;
}
_public_ int sd_journal_reliable_fd(sd_journal *j) {
if (!j)
return -EINVAL;
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(CATALOG_DATABASE, id, &text);
if (r < 0)
return r;
t = replace_var(text, lookup_field, j);
if (!t)
return -ENOMEM;
*ret = t;
return 0;
}
_public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
if (!ret)
return -EINVAL;
return catalog_get(CATALOG_DATABASE, id, ret);
}
_public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
if (!j)
return -EINVAL;
j->data_threshold = sz;
return 0;
}
_public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
if (!j)
return -EINVAL;
if (!sz)
return -EINVAL;
*sz = j->data_threshold;
return 0;
}