sd-journal.c revision 4a842cadb8d6b30fa9fdc8ff183633c14e02cf96
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering/***
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering This file is part of systemd.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Copyright 2011 Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering systemd is free software; you can redistribute it and/or modify it
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering under the terms of the GNU Lesser General Public License as published by
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering (at your option) any later version.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering systemd is distributed in the hope that it will be useful, but
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering Lesser General Public License for more details.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
5430f7f2bc7330f3088b894166bf3524a067e3d8Lennart Poettering You should have received a copy of the GNU Lesser General Public License
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering***/
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include <errno.h>
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include <fcntl.h>
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include <stddef.h>
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include <unistd.h>
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering#include <sys/inotify.h>
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering#include <sys/poll.h>
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering#include "sd-journal.h"
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering#include "journal-def.h"
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include "journal-file.h"
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include "hashmap.h"
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering#include "list.h"
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include "path-util.h"
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering#include "lookup3.h"
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering#include "compress.h"
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#include "journal-internal.h"
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering#define JOURNAL_FILES_MAX 1024
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic void detach_location(sd_journal *j) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Iterator i;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering JournalFile *f;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(j);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering j->current_file = NULL;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering j->current_field = 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering HASHMAP_FOREACH(f, j->files, i)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering f->current_offset = 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
bb99a35a873c35e80b0b47fe045081022660374dLennart Poetteringstatic void reset_location(sd_journal *j) {
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering assert(j);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering detach_location(j);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering zero(j->current_location);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic void init_location(Location *l, JournalFile *f, Object *o) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(l);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(f);
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering assert(o->object.type == OBJECT_ENTRY);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->type = LOCATION_DISCRETE;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->seqnum = le64toh(o->entry.seqnum);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->seqnum_id = f->header->seqnum_id;
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering l->realtime = le64toh(o->entry.realtime);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->monotonic = le64toh(o->entry.monotonic);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->boot_id = o->entry.boot_id;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->xor_hash = le64toh(o->entry.xor_hash);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering}
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offset) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(j);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(f);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(o);
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering init_location(&j->current_location, f, o);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering j->current_file = f;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering j->current_field = 0;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering f->current_offset = offset;
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering}
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic int match_is_valid(const void *data, size_t size) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering const char *b, *p;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(data);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (size < 2)
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering return false;
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (startswith(data, "__"))
d0bbc21caa6e68693a47db60c93e99422bf2a858Lennart Poettering return false;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering b = data;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering for (p = b; p < b + size; p++) {
25d042e81516246b1ebf706a57c47ac19abb0b8aLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (*p == '=')
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return p > b;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (*p == '_')
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering continue;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (*p >= 'A' && *p <= 'Z')
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering continue;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (*p >= '0' && *p <= '9')
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering continue;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return false;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
25d042e81516246b1ebf706a57c47ac19abb0b8aLennart Poettering return false;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering const uint8_t *a = _a, *b = _b;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering size_t j;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering for (j = 0; j < s && j < t; j++) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (a[j] != b[j])
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return false;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (a[j] == '=')
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return true;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering }
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return true;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic Match *match_new(Match *p, MatchType t) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Match *m;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering m = new0(Match, 1);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!m)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return NULL;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering m->type = t;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (p) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering m->parent = p;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering LIST_PREPEND(Match, matches, p->matches, m);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return m;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringstatic void match_free(Match *m) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(m);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering while (m->matches)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering match_free(m->matches);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (m->parent)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering LIST_REMOVE(Match, matches, m->parent->matches, m);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering free(m->data);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering free(m);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringstatic void match_free_if_empty(Match *m) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(m);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (m->matches)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering match_free(m);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering_public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering Match *l2, *l3, *add_here = NULL, *m;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering le64_t le_hash;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!j)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return -EINVAL;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!data)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return -EINVAL;
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (size == 0)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering size = strlen(data);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!match_is_valid(data, size))
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return -EINVAL;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering /* level 0: OR term
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * level 1: AND terms
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering * level 2: OR terms
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering * level 3: concrete matches */
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (!j->level0) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering j->level0 = match_new(NULL, MATCH_OR_TERM);
9058851be7821edac08c1fa7ecafe5cba9ab9022Lennart Poettering if (!j->level0)
9058851be7821edac08c1fa7ecafe5cba9ab9022Lennart Poettering return -ENOMEM;
9058851be7821edac08c1fa7ecafe5cba9ab9022Lennart Poettering }
9058851be7821edac08c1fa7ecafe5cba9ab9022Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!j->level1) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering j->level1 = match_new(j->level0, MATCH_AND_TERM);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!j->level1)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return -ENOMEM;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(j->level0->type == MATCH_OR_TERM);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(j->level1->type == MATCH_AND_TERM);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering le_hash = htole64(hash64(data, size));
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering LIST_FOREACH(matches, l2, j->level1->matches) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(l2->type == MATCH_OR_TERM);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering LIST_FOREACH(matches, l3, l2->matches) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(l3->type == MATCH_DISCRETE);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Exactly the same match already? Then ignore
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * this addition */
a5344d2c3b0f14e954ce1c0ef905c5b44bc5bf0aLennart Poettering if (l3->le_hash == le_hash &&
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering l3->size == size &&
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering memcmp(l3->data, data, size) == 0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Same field? Then let's add this to this OR term */
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (same_field(data, size, l3->data, l3->size)) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering add_here = l2;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering break;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (add_here)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering break;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering }
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!add_here) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering add_here = match_new(j->level1, MATCH_OR_TERM);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!add_here)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering goto fail;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering }
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering m = match_new(add_here, MATCH_DISCRETE);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!m)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering goto fail;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering m->le_hash = le_hash;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering m->size = size;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering m->data = memdup(data, size);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!m->data)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering goto fail;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering detach_location(j);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poetteringfail:
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (add_here)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering match_free_if_empty(add_here);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (j->level1)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering match_free_if_empty(j->level1);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (j->level0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering match_free_if_empty(j->level0);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return -ENOMEM;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering}
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering_public_ int sd_journal_add_disjunction(sd_journal *j) {
5ba081b0fb02380cee4c2ff5bc7e05f869eb8415Lennart Poettering Match *m;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(j);
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!j->level0)
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return 0;
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering if (!j->level1)
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!j->level1->matches)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering m = match_new(j->level0, MATCH_AND_TERM);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!m)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return -ENOMEM;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering j->level1 = m;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering return 0;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering}
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poetteringstatic char *match_make_string(Match *m) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering char *p, *r;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering Match *i;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering bool enclose = false;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!m)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return strdup("");
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (m->type == MATCH_DISCRETE)
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering return strndup(m->data, m->size);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering p = NULL;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering LIST_FOREACH(matches, i, m->matches) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering char *t, *k;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering t = match_make_string(i);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (!t) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering free(p);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return NULL;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering }
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (p) {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering free(p);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering free(t);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (!k)
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering return NULL;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering p = k;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering enclose = true;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering } else {
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering free(p);
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering p = t;
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering }
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering }
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering
0dad12c190b7493955cd60d2a1625199b1709f69Lennart Poettering if (enclose) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = strjoin("(", p, ")", NULL);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering free(p);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return p;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringchar *journal_make_match_string(sd_journal *j) {
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering assert(j);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49eLennart Poettering return match_make_string(j->level0);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering}
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering_public_ void sd_journal_flush_matches(sd_journal *j) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (!j)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (j->level0)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering match_free(j->level0);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering j->level0 = j->level1 = NULL;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering detach_location(j);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering}
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic int compare_order(JournalFile *af, Object *ao,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering JournalFile *bf, Object *bo) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering uint64_t a, b;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(af);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(ao);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(bf);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(bo);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* We operate on two different files here, hence we can access
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * two objects at the same time, which we normally can't.
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering *
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * If contents and timestamps match, these entries are
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * identical, even if the seqnum does not match */
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering ao->entry.monotonic == bo->entry.monotonic &&
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering ao->entry.realtime == bo->entry.realtime &&
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering ao->entry.xor_hash == bo->entry.xor_hash)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return 0;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* If this is from the same seqnum source, compare
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * seqnums */
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering a = le64toh(ao->entry.seqnum);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering b = le64toh(bo->entry.seqnum);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (a < b)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return -1;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (a > b)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return 1;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* Wow! This is weird, different data but the same
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * seqnums? Something is borked, but let's make the
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering * best of it and compare by time. */
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering }
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering /* If the boot id matches compare monotonic time */
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering a = le64toh(ao->entry.monotonic);
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering b = le64toh(bo->entry.monotonic);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a < b)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return -1;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a > b)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return 1;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering }
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering /* Otherwise compare UTC time */
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering a = le64toh(ao->entry.realtime);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering b = le64toh(bo->entry.realtime);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a < b)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return -1;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a > b)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return 1;
259d2e762041d8d50c2a17bfea90b1a96f6b880bLennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering /* Finally, compare by contents */
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering a = le64toh(ao->entry.xor_hash);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering b = le64toh(bo->entry.xor_hash);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a < b)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return -1;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a > b)
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering return 1;
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering return 0;
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering}
86b9b8e70d54e79db3ff4f67bbd5280ecfc82537Lennart Poettering
bb99a35a873c35e80b0b47fe045081022660374dLennart Poetteringstatic int compare_with_location(JournalFile *af, Object *ao, Location *l) {
bb99a35a873c35e80b0b47fe045081022660374dLennart Poettering uint64_t a;
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering assert(af);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering assert(ao);
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering assert(l);
8b38f3cc3eb73adf9536cb73d0f319e60d42ea0cLennart Poettering assert(l->type == LOCATION_DISCRETE);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering if (l->monotonic_set &&
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
8b38f3cc3eb73adf9536cb73d0f319e60d42ea0cLennart Poettering l->realtime_set &&
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering le64toh(ao->entry.realtime) == l->realtime &&
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering l->xor_hash_set &&
4cd9a9d9ecf3a8835e21930f3215a5f5b74144beLennart Poettering le64toh(ao->entry.xor_hash) == l->xor_hash)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return 0;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (l->seqnum_set &&
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering a = le64toh(ao->entry.seqnum);
224f2ee221e77c326d1d7761abb6e812432b2163Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a < l->seqnum)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return -1;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a > l->seqnum)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return 1;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering }
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (l->monotonic_set &&
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering a = le64toh(ao->entry.monotonic);
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a < l->monotonic)
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering return -1;
fe6521272ba203ec8f0d5a94f0729960b3f90525Lennart Poettering if (a > l->monotonic)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 1;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (l->realtime_set) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering a = le64toh(ao->entry.realtime);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (a < l->realtime)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return -1;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (a > l->realtime)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 1;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (l->xor_hash_set) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering a = le64toh(ao->entry.xor_hash);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (a < l->xor_hash)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return -1;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (a > l->xor_hash)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 1;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering}
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poetteringstatic int next_for_match(
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering sd_journal *j,
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering Match *m,
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering JournalFile *f,
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering uint64_t after_offset,
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering direction_t direction,
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering Object **ret,
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering uint64_t *offset) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering int r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering uint64_t np = 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering Object *n;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(j);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(m);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering assert(f);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (m->type == MATCH_DISCRETE) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering uint64_t dp;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (r <= 0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering } else if (m->type == MATCH_OR_TERM) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering Match *i;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Find the earliest match beyond after_offset */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering LIST_FOREACH(matches, i, m->matches) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering uint64_t cp;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (r < 0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering else if (r > 0) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering np = cp;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering } else if (m->type == MATCH_AND_TERM) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering Match *i;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering bool continue_looking;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering /* Always jump to the next matching entry and repeat
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * this until we fine and offset that matches for all
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering * matches. */
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (!m->matches)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering np = 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering do {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering continue_looking = false;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering LIST_FOREACH(matches, i, m->matches) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering uint64_t cp, limit;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (np == 0)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering limit = after_offset;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering else if (direction == DIRECTION_DOWN)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering limit = MAX(np, after_offset);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering else
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering limit = MIN(np, after_offset);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = next_for_match(j, i, f, limit, direction, NULL, &cp);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (r <= 0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) &&
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) {
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering np = cp;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering continue_looking = true;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering } while (continue_looking);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering }
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (np == 0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return 0;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering if (r < 0)
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering return r;
b070e7f3c9ed680c821bd89d42506695f2438506Lennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (ret)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering *ret = n;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering if (offset)
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering *offset = np;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering return 1;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering}
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poetteringstatic int find_location_for_match(
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering sd_journal *j,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering Match *m,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering JournalFile *f,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering direction_t direction,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering Object **ret,
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering uint64_t *offset) {
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering int r;
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(j);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering assert(m);
18c7ed186be28800a2eeb37ad31c9c44480d3d9cLennart Poettering 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_current = NULL;
Iterator i;
int r;
uint64_t new_offset = 0;
Object *new_entry = NULL;
if (!j)
return -EINVAL;
HASHMAP_FOREACH(f, j->files, i) {
Object *o;
uint64_t p;
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_current)
found = true;
else {
int k;
k = compare_order(f, o, new_current, new_entry);
if (direction == DIRECTION_DOWN)
found = k < 0;
else
found = k > 0;
}
if (found) {
new_current = f;
new_entry = o;
new_offset = p;
}
}
if (!new_current)
return 0;
set_location(j, new_current, new_entry, 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;p=%s",
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),
path_get_file_name(j->current_file->path)) < 0)
return -ENOMEM;
return 1;
}
_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
char *w;
size_t l;
char *state;
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 (!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(w+2, &seqnum_id);
break;
case 'i':
seqnum_set = true;
if (sscanf(w+2, "%llx", &seqnum) != 1)
k = -EINVAL;
break;
case 'b':
boot_id_set = true;
k = sd_id128_from_string(w+2, &boot_id);
break;
case 'm':
monotonic_set = true;
if (sscanf(w+2, "%llx", &monotonic) != 1)
k = -EINVAL;
break;
case 't':
realtime_set = true;
if (sscanf(w+2, "%llx", &realtime) != 1)
k = -EINVAL;
break;
case 'x':
xor_hash_set = true;
if (sscanf(w+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_DISCRETE;
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_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_DISCRETE;
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_DISCRETE;
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 int add_file(sd_journal *j, const char *prefix, const char *filename) {
char *path;
int r;
JournalFile *f;
assert(j);
assert(prefix);
assert(filename);
if ((j->flags & SD_JOURNAL_SYSTEM_ONLY) &&
!(streq(filename, "system.journal") ||
(startswith(filename, "system@") && endswith(filename, ".journal"))))
return 0;
path = strjoin(prefix, "/", filename, NULL);
if (!path)
return -ENOMEM;
if (hashmap_get(j->files, path)) {
free(path);
return 0;
}
if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
log_debug("Too many open journal files, not adding %s, ignoring.", path);
free(path);
return 0;
}
r = journal_file_open(path, O_RDONLY, 0, NULL, NULL, &f);
free(path);
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;
}
j->current_invalidate_counter ++;
log_debug("File %s got added.", f->path);
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);
journal_file_close(f);
j->current_invalidate_counter ++;
log_debug("File %s got removed.", f->path);
return 0;
}
static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
char *path;
int r;
DIR *d;
sd_id128_t id, mid;
Directory *m;
assert(j);
assert(prefix);
assert(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)))
return 0;
path = strjoin(prefix, "/", dirname, NULL);
if (!path)
return -ENOMEM;
d = opendir(path);
if (!d) {
log_debug("Failed to open %s: %m", path);
free(path);
if (errno == ENOENT)
return 0;
return -errno;
}
m = hashmap_get(j->directories_by_path, path);
if (!m) {
m = new0(Directory, 1);
if (!m) {
closedir(d);
free(path);
return -ENOMEM;
}
m->is_root = false;
m->path = path;
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
closedir(d);
free(m->path);
free(m);
return -ENOMEM;
}
j->current_invalidate_counter ++;
log_debug("Directory %s got added.", m->path);
} else if (m->is_root) {
free (path);
closedir(d);
return 0;
} else
free(path);
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_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 buf, *de;
r = readdir_r(d, &buf, &de);
if (r != 0 || !de)
break;
if (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));
}
}
closedir(d);
return 0;
}
static int add_root_directory(sd_journal *j, const char *p) {
DIR *d;
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) {
closedir(d);
return -ENOMEM;
}
m->is_root = true;
m->path = strdup(p);
if (!m->path) {
closedir(d);
free(m);
return -ENOMEM;
}
if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
closedir(d);
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) {
closedir(d);
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 buf, *de;
sd_id128_t id;
r = readdir_r(d, &buf, &de);
if (r != 0 || !de)
break;
if (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));
} 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));
}
}
closedir(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) {
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)
add_root_directory(j, p);
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;
if (path) {
j->path = strdup(path);
if (!j->path) {
free(j);
return NULL;
}
}
j->files = hashmap_new(string_hash_func, string_compare_func);
if (!j->files) {
free(j->path);
free(j);
return NULL;
}
j->directories_by_path = hashmap_new(string_hash_func, string_compare_func);
if (!j->directories_by_path) {
hashmap_free(j->files);
free(j->path);
free(j);
return NULL;
}
return j;
}
_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 || !path_is_absolute(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)
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;
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);
sd_journal_flush_matches(j);
free(j->path);
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))
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;
}
_public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
JournalFile *f;
uint64_t p, l, n;
le64_t le_hash;
int r;
Object *o;
size_t t;
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;
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))
return -EBADMSG;
*data = f->compress_buffer;
*size = (size_t) rsize;
#else
return -EPROTONOSUPPORT;
#endif
} else {
*data = o->data.payload;
*size = t;
}
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;
}
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")) {
/* 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));
} else if (e->mask & (IN_DELETE|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;
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;
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);
}
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;
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 = MIN(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;
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 = MIN(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_query_unique(sd_journal *j, const char *field) { */
/* if (!j) */
/* return -EINVAL; */
/* if (!field) */
/* return -EINVAL; */
/* return -ENOTSUP; */
/* } */
/* _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) { */
/* if (!j) */
/* return -EINVAL; */
/* if (!data) */
/* return -EINVAL; */
/* if (!l) */
/* return -EINVAL; */
/* return -ENOTSUP; */
/* } */
/* _public_ void sd_journal_restart_unique(sd_journal *j) { */
/* if (!j) */
/* return; */
/* } */