LCOV - code coverage report
Current view: top level - journal - sd-journal.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 757 1446 52.4 %
Date: 2015-07-29 18:47:03 Functions: 51 82 62.2 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2011 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <errno.h>
      23             : #include <fcntl.h>
      24             : #include <stddef.h>
      25             : #include <unistd.h>
      26             : #include <sys/inotify.h>
      27             : #include <poll.h>
      28             : #include <sys/vfs.h>
      29             : #include <linux/magic.h>
      30             : 
      31             : #include "sd-journal.h"
      32             : #include "journal-def.h"
      33             : #include "journal-file.h"
      34             : #include "hashmap.h"
      35             : #include "list.h"
      36             : #include "strv.h"
      37             : #include "path-util.h"
      38             : #include "lookup3.h"
      39             : #include "compress.h"
      40             : #include "journal-internal.h"
      41             : #include "missing.h"
      42             : #include "catalog.h"
      43             : #include "replace-var.h"
      44             : #include "fileio.h"
      45             : #include "formats-util.h"
      46             : 
      47             : #define JOURNAL_FILES_MAX 7168
      48             : 
      49             : #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
      50             : 
      51             : #define REPLACE_VAR_MAX 256
      52             : 
      53             : #define DEFAULT_DATA_THRESHOLD (64*1024)
      54             : 
      55             : static void remove_file_real(sd_journal *j, JournalFile *f);
      56             : 
      57       12113 : static bool journal_pid_changed(sd_journal *j) {
      58       12113 :         assert(j);
      59             : 
      60             :         /* We don't support people creating a journal object and
      61             :          * keeping it around over a fork(). Let's complain. */
      62             : 
      63       12113 :         return j->original_pid != getpid();
      64             : }
      65             : 
      66             : /* We return an error here only if we didn't manage to
      67             :    memorize the real error. */
      68         816 : static int set_put_error(sd_journal *j, int r) {
      69             :         int k;
      70             : 
      71         816 :         if (r >= 0)
      72           0 :                 return r;
      73             : 
      74         816 :         k = set_ensure_allocated(&j->errors, NULL);
      75         816 :         if (k < 0)
      76           0 :                 return k;
      77             : 
      78         816 :         return set_put(j->errors, INT_TO_PTR(r));
      79             : }
      80             : 
      81         248 : static void detach_location(sd_journal *j) {
      82             :         Iterator i;
      83             :         JournalFile *f;
      84             : 
      85         248 :         assert(j);
      86             : 
      87         248 :         j->current_file = NULL;
      88         248 :         j->current_field = 0;
      89             : 
      90        1160 :         ORDERED_HASHMAP_FOREACH(f, j->files, i)
      91         664 :                 journal_file_reset_location(f);
      92         248 : }
      93             : 
      94          14 : static void reset_location(sd_journal *j) {
      95          14 :         assert(j);
      96             : 
      97          14 :         detach_location(j);
      98          14 :         zero(j->current_location);
      99          14 : }
     100             : 
     101       10367 : static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
     102       10367 :         assert(l);
     103       10367 :         assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
     104       10367 :         assert(f);
     105       10367 :         assert(o->object.type == OBJECT_ENTRY);
     106             : 
     107       10367 :         l->type = type;
     108       10367 :         l->seqnum = le64toh(o->entry.seqnum);
     109       10367 :         l->seqnum_id = f->header->seqnum_id;
     110       10367 :         l->realtime = le64toh(o->entry.realtime);
     111       10367 :         l->monotonic = le64toh(o->entry.monotonic);
     112       10367 :         l->boot_id = o->entry.boot_id;
     113       10367 :         l->xor_hash = le64toh(o->entry.xor_hash);
     114             : 
     115       10367 :         l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
     116       10367 : }
     117             : 
     118       10367 : static void set_location(sd_journal *j, JournalFile *f, Object *o) {
     119       10367 :         assert(j);
     120       10367 :         assert(f);
     121       10367 :         assert(o);
     122             : 
     123       10367 :         init_location(&j->current_location, LOCATION_DISCRETE, f, o);
     124             : 
     125       10367 :         j->current_file = f;
     126       10367 :         j->current_field = 0;
     127             : 
     128             :         /* Let f know its candidate entry was picked. */
     129       10367 :         assert(f->location_type == LOCATION_SEEK);
     130       10367 :         f->location_type = LOCATION_DISCRETE;
     131       10367 : }
     132             : 
     133          27 : static int match_is_valid(const void *data, size_t size) {
     134             :         const char *b, *p;
     135             : 
     136          27 :         assert(data);
     137             : 
     138          27 :         if (size < 2)
     139           2 :                 return false;
     140             : 
     141          25 :         if (startswith(data, "__"))
     142           0 :                 return false;
     143             : 
     144          25 :         b = data;
     145         117 :         for (p = b; p < b + size; p++) {
     146             : 
     147         117 :                 if (*p == '=')
     148          23 :                         return p > b;
     149             : 
     150          94 :                 if (*p == '_')
     151           4 :                         continue;
     152             : 
     153          90 :                 if (*p >= 'A' && *p <= 'Z')
     154          78 :                         continue;
     155             : 
     156          12 :                 if (*p >= '0' && *p <= '9')
     157          10 :                         continue;
     158             : 
     159           2 :                 return false;
     160             :         }
     161             : 
     162           0 :         return false;
     163             : }
     164             : 
     165          22 : static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
     166          22 :         const uint8_t *a = _a, *b = _b;
     167             :         size_t j;
     168             : 
     169          66 :         for (j = 0; j < s && j < t; j++) {
     170             : 
     171          66 :                 if (a[j] != b[j])
     172          13 :                         return false;
     173             : 
     174          53 :                 if (a[j] == '=')
     175           9 :                         return true;
     176             :         }
     177             : 
     178           0 :         assert_not_reached("\"=\" not found");
     179             : }
     180             : 
     181          49 : static Match *match_new(Match *p, MatchType t) {
     182             :         Match *m;
     183             : 
     184          49 :         m = new0(Match, 1);
     185          49 :         if (!m)
     186           0 :                 return NULL;
     187             : 
     188          49 :         m->type = t;
     189             : 
     190          49 :         if (p) {
     191          45 :                 m->parent = p;
     192          45 :                 LIST_PREPEND(matches, p->matches, m);
     193             :         }
     194             : 
     195          49 :         return m;
     196             : }
     197             : 
     198          49 : static void match_free(Match *m) {
     199          49 :         assert(m);
     200             : 
     201         143 :         while (m->matches)
     202          45 :                 match_free(m->matches);
     203             : 
     204          49 :         if (m->parent)
     205          45 :                 LIST_REMOVE(matches, m->parent->matches, m);
     206             : 
     207          49 :         free(m->data);
     208          49 :         free(m);
     209          49 : }
     210             : 
     211           0 : static void match_free_if_empty(Match *m) {
     212           0 :         if (!m || m->matches)
     213           0 :                 return;
     214             : 
     215           0 :         match_free(m);
     216             : }
     217             : 
     218          27 : _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
     219          27 :         Match *l3, *l4, *add_here = NULL, *m;
     220             :         le64_t le_hash;
     221             : 
     222          27 :         assert_return(j, -EINVAL);
     223          27 :         assert_return(!journal_pid_changed(j), -ECHILD);
     224          27 :         assert_return(data, -EINVAL);
     225             : 
     226          27 :         if (size == 0)
     227          27 :                 size = strlen(data);
     228             : 
     229          27 :         assert_return(match_is_valid(data, size), -EINVAL);
     230             : 
     231             :         /* level 0: AND term
     232             :          * level 1: OR terms
     233             :          * level 2: AND terms
     234             :          * level 3: OR terms
     235             :          * level 4: concrete matches */
     236             : 
     237          22 :         if (!j->level0) {
     238           4 :                 j->level0 = match_new(NULL, MATCH_AND_TERM);
     239           4 :                 if (!j->level0)
     240           0 :                         return -ENOMEM;
     241             :         }
     242             : 
     243          22 :         if (!j->level1) {
     244           5 :                 j->level1 = match_new(j->level0, MATCH_OR_TERM);
     245           5 :                 if (!j->level1)
     246           0 :                         return -ENOMEM;
     247             :         }
     248             : 
     249          22 :         if (!j->level2) {
     250           7 :                 j->level2 = match_new(j->level1, MATCH_AND_TERM);
     251           7 :                 if (!j->level2)
     252           0 :                         return -ENOMEM;
     253             :         }
     254             : 
     255          22 :         assert(j->level0->type == MATCH_AND_TERM);
     256          22 :         assert(j->level1->type == MATCH_OR_TERM);
     257          22 :         assert(j->level2->type == MATCH_AND_TERM);
     258             : 
     259          22 :         le_hash = htole64(hash64(data, size));
     260             : 
     261          29 :         LIST_FOREACH(matches, l3, j->level2->matches) {
     262          17 :                 assert(l3->type == MATCH_OR_TERM);
     263             : 
     264          30 :                 LIST_FOREACH(matches, l4, l3->matches) {
     265          23 :                         assert(l4->type == MATCH_DISCRETE);
     266             : 
     267             :                         /* Exactly the same match already? Then ignore
     268             :                          * this addition */
     269          24 :                         if (l4->le_hash == le_hash &&
     270           2 :                             l4->size == size &&
     271           1 :                             memcmp(l4->data, data, size) == 0)
     272           1 :                                 return 0;
     273             : 
     274             :                         /* Same field? Then let's add this to this OR term */
     275          22 :                         if (same_field(data, size, l4->data, l4->size)) {
     276           9 :                                 add_here = l3;
     277           9 :                                 break;
     278             :                         }
     279             :                 }
     280             : 
     281          16 :                 if (add_here)
     282           9 :                         break;
     283             :         }
     284             : 
     285          21 :         if (!add_here) {
     286          12 :                 add_here = match_new(j->level2, MATCH_OR_TERM);
     287          12 :                 if (!add_here)
     288           0 :                         goto fail;
     289             :         }
     290             : 
     291          21 :         m = match_new(add_here, MATCH_DISCRETE);
     292          21 :         if (!m)
     293           0 :                 goto fail;
     294             : 
     295          21 :         m->le_hash = le_hash;
     296          21 :         m->size = size;
     297          21 :         m->data = memdup(data, size);
     298          21 :         if (!m->data)
     299           0 :                 goto fail;
     300             : 
     301          21 :         detach_location(j);
     302             : 
     303          21 :         return 0;
     304             : 
     305             : fail:
     306           0 :         match_free_if_empty(add_here);
     307           0 :         match_free_if_empty(j->level2);
     308           0 :         match_free_if_empty(j->level1);
     309           0 :         match_free_if_empty(j->level0);
     310             : 
     311           0 :         return -ENOMEM;
     312             : }
     313             : 
     314           1 : _public_ int sd_journal_add_conjunction(sd_journal *j) {
     315           1 :         assert_return(j, -EINVAL);
     316           1 :         assert_return(!journal_pid_changed(j), -ECHILD);
     317             : 
     318           1 :         if (!j->level0)
     319           0 :                 return 0;
     320             : 
     321           1 :         if (!j->level1)
     322           0 :                 return 0;
     323             : 
     324           1 :         if (!j->level1->matches)
     325           0 :                 return 0;
     326             : 
     327           1 :         j->level1 = NULL;
     328           1 :         j->level2 = NULL;
     329             : 
     330           1 :         return 0;
     331             : }
     332             : 
     333           2 : _public_ int sd_journal_add_disjunction(sd_journal *j) {
     334           2 :         assert_return(j, -EINVAL);
     335           2 :         assert_return(!journal_pid_changed(j), -ECHILD);
     336             : 
     337           2 :         if (!j->level0)
     338           0 :                 return 0;
     339             : 
     340           2 :         if (!j->level1)
     341           0 :                 return 0;
     342             : 
     343           2 :         if (!j->level2)
     344           0 :                 return 0;
     345             : 
     346           2 :         if (!j->level2->matches)
     347           0 :                 return 0;
     348             : 
     349           2 :         j->level2 = NULL;
     350           2 :         return 0;
     351             : }
     352             : 
     353          44 : static char *match_make_string(Match *m) {
     354             :         char *p, *r;
     355             :         Match *i;
     356          44 :         bool enclose = false;
     357             : 
     358          44 :         if (!m)
     359           0 :                 return strdup("none");
     360             : 
     361          44 :         if (m->type == MATCH_DISCRETE)
     362          20 :                 return strndup(m->data, m->size);
     363             : 
     364          24 :         p = NULL;
     365          65 :         LIST_FOREACH(matches, i, m->matches) {
     366             :                 char *t, *k;
     367             : 
     368          41 :                 t = match_make_string(i);
     369          41 :                 if (!t) {
     370           0 :                         free(p);
     371           0 :                         return NULL;
     372             :                 }
     373             : 
     374          41 :                 if (p) {
     375          17 :                         k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL);
     376          17 :                         free(p);
     377          17 :                         free(t);
     378             : 
     379          17 :                         if (!k)
     380           0 :                                 return NULL;
     381             : 
     382          17 :                         p = k;
     383             : 
     384          17 :                         enclose = true;
     385             :                 } else
     386          24 :                         p = t;
     387             :         }
     388             : 
     389          24 :         if (enclose) {
     390          14 :                 r = strjoin("(", p, ")", NULL);
     391          14 :                 free(p);
     392          14 :                 return r;
     393             :         }
     394             : 
     395          10 :         return p;
     396             : }
     397             : 
     398           3 : char *journal_make_match_string(sd_journal *j) {
     399           3 :         assert(j);
     400             : 
     401           3 :         return match_make_string(j->level0);
     402             : }
     403             : 
     404         213 : _public_ void sd_journal_flush_matches(sd_journal *j) {
     405         213 :         if (!j)
     406           0 :                 return;
     407             : 
     408         213 :         if (j->level0)
     409           4 :                 match_free(j->level0);
     410             : 
     411         213 :         j->level0 = j->level1 = j->level2 = NULL;
     412             : 
     413         213 :         detach_location(j);
     414             : }
     415             : 
     416       51074 : _pure_ static int compare_with_location(JournalFile *f, Location *l) {
     417       51074 :         assert(f);
     418       51074 :         assert(l);
     419       51074 :         assert(f->location_type == LOCATION_SEEK);
     420       51074 :         assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
     421             : 
     422      102148 :         if (l->monotonic_set &&
     423       71464 :             sd_id128_equal(f->current_boot_id, l->boot_id) &&
     424       40780 :             l->realtime_set &&
     425       20476 :             f->current_realtime == l->realtime &&
     426         172 :             l->xor_hash_set &&
     427          86 :             f->current_xor_hash == l->xor_hash)
     428          86 :                 return 0;
     429             : 
     430      101976 :         if (l->seqnum_set &&
     431       50988 :             sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
     432             : 
     433       10335 :                 if (f->current_seqnum < l->seqnum)
     434          49 :                         return -1;
     435       10286 :                 if (f->current_seqnum > l->seqnum)
     436       10286 :                         return 1;
     437             :         }
     438             : 
     439       81306 :         if (l->monotonic_set &&
     440       40653 :             sd_id128_equal(f->current_boot_id, l->boot_id)) {
     441             : 
     442        9979 :                 if (f->current_monotonic < l->monotonic)
     443          91 :                         return -1;
     444        9888 :                 if (f->current_monotonic > l->monotonic)
     445        9888 :                         return 1;
     446             :         }
     447             : 
     448       30674 :         if (l->realtime_set) {
     449             : 
     450       30674 :                 if (f->current_realtime < l->realtime)
     451           0 :                         return -1;
     452       30674 :                 if (f->current_realtime > l->realtime)
     453       30674 :                         return 1;
     454             :         }
     455             : 
     456           0 :         if (l->xor_hash_set) {
     457             : 
     458           0 :                 if (f->current_xor_hash < l->xor_hash)
     459           0 :                         return -1;
     460           0 :                 if (f->current_xor_hash > l->xor_hash)
     461           0 :                         return 1;
     462             :         }
     463             : 
     464           0 :         return 0;
     465             : }
     466             : 
     467         832 : static int next_for_match(
     468             :                 sd_journal *j,
     469             :                 Match *m,
     470             :                 JournalFile *f,
     471             :                 uint64_t after_offset,
     472             :                 direction_t direction,
     473             :                 Object **ret,
     474             :                 uint64_t *offset) {
     475             : 
     476             :         int r;
     477         832 :         uint64_t np = 0;
     478             :         Object *n;
     479             : 
     480         832 :         assert(j);
     481         832 :         assert(m);
     482         832 :         assert(f);
     483             : 
     484         832 :         if (m->type == MATCH_DISCRETE) {
     485             :                 uint64_t dp;
     486             : 
     487         185 :                 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
     488         185 :                 if (r <= 0)
     489          10 :                         return r;
     490             : 
     491         175 :                 return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
     492             : 
     493         647 :         } else if (m->type == MATCH_OR_TERM) {
     494             :                 Match *i;
     495             : 
     496             :                 /* Find the earliest match beyond after_offset */
     497             : 
     498         666 :                 LIST_FOREACH(matches, i, m->matches) {
     499             :                         uint64_t cp;
     500             : 
     501         340 :                         r = next_for_match(j, i, f, after_offset, direction, NULL, &cp);
     502         340 :                         if (r < 0)
     503           0 :                                 return r;
     504         340 :                         else if (r > 0) {
     505         306 :                                 if (np == 0 || (direction == DIRECTION_DOWN ? cp < np : cp > np))
     506         306 :                                         np = cp;
     507             :                         }
     508             :                 }
     509             : 
     510         326 :                 if (np == 0)
     511          22 :                         return 0;
     512             : 
     513         321 :         } else if (m->type == MATCH_AND_TERM) {
     514             :                 Match *i, *last_moved;
     515             : 
     516             :                 /* Always jump to the next matching entry and repeat
     517             :                  * this until we find an offset that matches for all
     518             :                  * matches. */
     519             : 
     520         321 :                 if (!m->matches)
     521           0 :                         return 0;
     522             : 
     523         321 :                 r = next_for_match(j, m->matches, f, after_offset, direction, NULL, &np);
     524         321 :                 if (r <= 0)
     525          22 :                         return r;
     526             : 
     527         299 :                 assert(direction == DIRECTION_DOWN ? np >= after_offset : np <= after_offset);
     528         299 :                 last_moved = m->matches;
     529             : 
     530         304 :                 LIST_LOOP_BUT_ONE(matches, i, m->matches, last_moved) {
     531             :                         uint64_t cp;
     532             : 
     533           5 :                         r = next_for_match(j, i, f, np, direction, NULL, &cp);
     534           5 :                         if (r <= 0)
     535           0 :                                 return r;
     536             : 
     537           5 :                         assert(direction == DIRECTION_DOWN ? cp >= np : cp <= np);
     538           5 :                         if (direction == DIRECTION_DOWN ? cp > np : cp < np) {
     539           0 :                                 np = cp;
     540           0 :                                 last_moved = i;
     541             :                         }
     542             :                 }
     543             :         }
     544             : 
     545         603 :         assert(np > 0);
     546             : 
     547         603 :         r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
     548         603 :         if (r < 0)
     549           0 :                 return r;
     550             : 
     551         603 :         if (ret)
     552         144 :                 *ret = n;
     553         603 :         if (offset)
     554         603 :                 *offset = np;
     555             : 
     556         603 :         return 1;
     557             : }
     558             : 
     559          72 : static int find_location_for_match(
     560             :                 sd_journal *j,
     561             :                 Match *m,
     562             :                 JournalFile *f,
     563             :                 direction_t direction,
     564             :                 Object **ret,
     565             :                 uint64_t *offset) {
     566             : 
     567             :         int r;
     568             : 
     569          72 :         assert(j);
     570          72 :         assert(m);
     571          72 :         assert(f);
     572             : 
     573          72 :         if (m->type == MATCH_DISCRETE) {
     574             :                 uint64_t dp;
     575             : 
     576          21 :                 r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
     577          21 :                 if (r <= 0)
     578           6 :                         return r;
     579             : 
     580             :                 /* FIXME: missing: find by monotonic */
     581             : 
     582          15 :                 if (j->current_location.type == LOCATION_HEAD)
     583          12 :                         return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset);
     584           3 :                 if (j->current_location.type == LOCATION_TAIL)
     585           3 :                         return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset);
     586           0 :                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
     587           0 :                         return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
     588           0 :                 if (j->current_location.monotonic_set) {
     589           0 :                         r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
     590           0 :                         if (r != -ENOENT)
     591           0 :                                 return r;
     592             :                 }
     593           0 :                 if (j->current_location.realtime_set)
     594           0 :                         return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
     595             : 
     596           0 :                 return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset);
     597             : 
     598          51 :         } else if (m->type == MATCH_OR_TERM) {
     599          27 :                 uint64_t np = 0;
     600             :                 Object *n;
     601             :                 Match *i;
     602             : 
     603             :                 /* Find the earliest match */
     604             : 
     605          60 :                 LIST_FOREACH(matches, i, m->matches) {
     606             :                         uint64_t cp;
     607             : 
     608          33 :                         r = find_location_for_match(j, i, f, direction, NULL, &cp);
     609          33 :                         if (r < 0)
     610           0 :                                 return r;
     611          33 :                         else if (r > 0) {
     612          26 :                                 if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp))
     613          26 :                                         np = cp;
     614             :                         }
     615             :                 }
     616             : 
     617          27 :                 if (np == 0)
     618           2 :                         return 0;
     619             : 
     620          25 :                 r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n);
     621          25 :                 if (r < 0)
     622           0 :                         return r;
     623             : 
     624          25 :                 if (ret)
     625           0 :                         *ret = n;
     626          25 :                 if (offset)
     627          25 :                         *offset = np;
     628             : 
     629          25 :                 return 1;
     630             : 
     631             :         } else {
     632             :                 Match *i;
     633          24 :                 uint64_t np = 0;
     634             : 
     635          24 :                 assert(m->type == MATCH_AND_TERM);
     636             : 
     637             :                 /* First jump to the last match, and then find the
     638             :                  * next one where all matches match */
     639             : 
     640          24 :                 if (!m->matches)
     641           0 :                         return 0;
     642             : 
     643          49 :                 LIST_FOREACH(matches, i, m->matches) {
     644             :                         uint64_t cp;
     645             : 
     646          27 :                         r = find_location_for_match(j, i, f, direction, NULL, &cp);
     647          27 :                         if (r <= 0)
     648           2 :                                 return r;
     649             : 
     650          25 :                         if (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))
     651          23 :                                 np = cp;
     652             :                 }
     653             : 
     654          22 :                 return next_for_match(j, m, f, np, direction, ret, offset);
     655             :         }
     656             : }
     657             : 
     658          44 : static int find_location_with_matches(
     659             :                 sd_journal *j,
     660             :                 JournalFile *f,
     661             :                 direction_t direction,
     662             :                 Object **ret,
     663             :                 uint64_t *offset) {
     664             : 
     665             :         int r;
     666             : 
     667          44 :         assert(j);
     668          44 :         assert(f);
     669          44 :         assert(ret);
     670          44 :         assert(offset);
     671             : 
     672          44 :         if (!j->level0) {
     673             :                 /* No matches is simple */
     674             : 
     675          32 :                 if (j->current_location.type == LOCATION_HEAD)
     676          16 :                         return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset);
     677          16 :                 if (j->current_location.type == LOCATION_TAIL)
     678           8 :                         return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset);
     679           8 :                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
     680           4 :                         return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
     681           4 :                 if (j->current_location.monotonic_set) {
     682           4 :                         r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
     683           4 :                         if (r != -ENOENT)
     684           0 :                                 return r;
     685             :                 }
     686           4 :                 if (j->current_location.realtime_set)
     687           4 :                         return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
     688             : 
     689           0 :                 return journal_file_next_entry(f, 0, direction, ret, offset);
     690             :         } else
     691          12 :                 return find_location_for_match(j, j->level0, f, direction, ret, offset);
     692             : }
     693             : 
     694       10448 : static int next_with_matches(
     695             :                 sd_journal *j,
     696             :                 JournalFile *f,
     697             :                 direction_t direction,
     698             :                 Object **ret,
     699             :                 uint64_t *offset) {
     700             : 
     701       10448 :         assert(j);
     702       10448 :         assert(f);
     703       10448 :         assert(ret);
     704       10448 :         assert(offset);
     705             : 
     706             :         /* No matches is easy. We simple advance the file
     707             :          * pointer by one. */
     708       10448 :         if (!j->level0)
     709       10304 :                 return journal_file_next_entry(f, f->current_offset, direction, ret, offset);
     710             : 
     711             :         /* If we have a match then we look for the next matching entry
     712             :          * with an offset at least one step larger */
     713         288 :         return next_for_match(j, j->level0, f,
     714          97 :                               direction == DIRECTION_DOWN ? f->current_offset + 1
     715          47 :                                                           : f->current_offset - 1,
     716             :                               direction, ret, offset);
     717             : }
     718             : 
     719       51090 : static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
     720             :         Object *c;
     721             :         uint64_t cp, n_entries;
     722             :         int r;
     723             : 
     724       51090 :         assert(j);
     725       51090 :         assert(f);
     726             : 
     727       51090 :         n_entries = le64toh(f->header->n_entries);
     728             : 
     729             :         /* If we hit EOF before, we don't need to look into this file again
     730             :          * unless direction changed or new entries appeared. */
     731       51122 :         if (f->last_direction == direction && f->location_type == LOCATION_TAIL &&
     732          32 :             n_entries == f->last_n_entries)
     733          32 :                 return 0;
     734             : 
     735       51058 :         f->last_n_entries = n_entries;
     736             : 
     737       51058 :         if (f->last_direction == direction && f->current_offset > 0) {
     738             :                 /* LOCATION_SEEK here means we did the work in a previous
     739             :                  * iteration and the current location already points to a
     740             :                  * candidate entry. */
     741      101997 :                 if (f->location_type != LOCATION_SEEK) {
     742       10362 :                         r = next_with_matches(j, f, direction, &c, &cp);
     743       10362 :                         if (r <= 0)
     744          31 :                                 return r;
     745             : 
     746       10331 :                         journal_file_save_location(f, c, cp);
     747             :                 }
     748             :         } else {
     749          44 :                 f->last_direction = direction;
     750             : 
     751          44 :                 r = find_location_with_matches(j, f, direction, &c, &cp);
     752          44 :                 if (r <= 0)
     753           1 :                         return r;
     754             : 
     755          43 :                 journal_file_save_location(f, c, cp);
     756             :         }
     757             : 
     758             :         /* OK, we found the spot, now let's advance until an entry
     759             :          * that is actually different from what we were previously
     760             :          * looking at. This is necessary to handle entries which exist
     761             :          * in two (or more) journal files, and which shall all be
     762             :          * suppressed but one. */
     763             : 
     764             :         for (;;) {
     765             :                 bool found;
     766             : 
     767       51109 :                 if (j->current_location.type == LOCATION_DISCRETE) {
     768             :                         int k;
     769             : 
     770       51074 :                         k = compare_with_location(f, &j->current_location);
     771             : 
     772       51074 :                         found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
     773             :                 } else
     774          35 :                         found = true;
     775             : 
     776       51109 :                 if (found)
     777       51023 :                         return 1;
     778             : 
     779          86 :                 r = next_with_matches(j, f, direction, &c, &cp);
     780          86 :                 if (r <= 0)
     781           3 :                         return r;
     782             : 
     783          83 :                 journal_file_save_location(f, c, cp);
     784          83 :         }
     785             : }
     786             : 
     787       10380 : static int real_journal_next(sd_journal *j, direction_t direction) {
     788       10380 :         JournalFile *f, *new_file = NULL;
     789             :         Iterator i;
     790             :         Object *o;
     791             :         int r;
     792             : 
     793       10380 :         assert_return(j, -EINVAL);
     794       10380 :         assert_return(!journal_pid_changed(j), -ECHILD);
     795             : 
     796       71850 :         ORDERED_HASHMAP_FOREACH(f, j->files, i) {
     797             :                 bool found;
     798             : 
     799       51090 :                 r = next_beyond_location(j, f, direction);
     800       51090 :                 if (r < 0) {
     801           0 :                         log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path);
     802           0 :                         remove_file_real(j, f);
     803           0 :                         continue;
     804       51090 :                 } else if (r == 0) {
     805          67 :                         f->location_type = LOCATION_TAIL;
     806          67 :                         continue;
     807             :                 }
     808             : 
     809       51023 :                 if (!new_file)
     810       10367 :                         found = true;
     811             :                 else {
     812             :                         int k;
     813             : 
     814       40656 :                         k = journal_file_compare_locations(f, new_file);
     815             : 
     816       40656 :                         found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
     817             :                 }
     818             : 
     819       51023 :                 if (found)
     820       11667 :                         new_file = f;
     821             :         }
     822             : 
     823       10380 :         if (!new_file)
     824          13 :                 return 0;
     825             : 
     826       10367 :         r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
     827       10367 :         if (r < 0)
     828           0 :                 return r;
     829             : 
     830       10367 :         set_location(j, new_file, o);
     831             : 
     832       10367 :         return 1;
     833             : }
     834             : 
     835       10305 : _public_ int sd_journal_next(sd_journal *j) {
     836       10305 :         return real_journal_next(j, DIRECTION_DOWN);
     837             : }
     838             : 
     839          59 : _public_ int sd_journal_previous(sd_journal *j) {
     840          59 :         return real_journal_next(j, DIRECTION_UP);
     841             : }
     842             : 
     843           4 : static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t skip) {
     844           4 :         int c = 0, r;
     845             : 
     846           4 :         assert_return(j, -EINVAL);
     847           4 :         assert_return(!journal_pid_changed(j), -ECHILD);
     848             : 
     849           4 :         if (skip == 0) {
     850             :                 /* If this is not a discrete skip, then at least
     851             :                  * resolve the current location */
     852           0 :                 if (j->current_location.type != LOCATION_DISCRETE)
     853           0 :                         return real_journal_next(j, direction);
     854             : 
     855           0 :                 return 0;
     856             :         }
     857             : 
     858             :         do {
     859          16 :                 r = real_journal_next(j, direction);
     860          16 :                 if (r < 0)
     861           0 :                         return r;
     862             : 
     863          16 :                 if (r == 0)
     864           0 :                         return c;
     865             : 
     866          16 :                 skip--;
     867          16 :                 c++;
     868          16 :         } while (skip > 0);
     869             : 
     870           4 :         return c;
     871             : }
     872             : 
     873           2 : _public_ int sd_journal_next_skip(sd_journal *j, uint64_t skip) {
     874           2 :         return real_journal_next_skip(j, DIRECTION_DOWN, skip);
     875             : }
     876             : 
     877           2 : _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
     878           2 :         return real_journal_next_skip(j, DIRECTION_UP, skip);
     879             : }
     880             : 
     881         564 : _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
     882             :         Object *o;
     883             :         int r;
     884             :         char bid[33], sid[33];
     885             : 
     886         564 :         assert_return(j, -EINVAL);
     887         564 :         assert_return(!journal_pid_changed(j), -ECHILD);
     888         564 :         assert_return(cursor, -EINVAL);
     889             : 
     890         564 :         if (!j->current_file || j->current_file->current_offset <= 0)
     891           0 :                 return -EADDRNOTAVAIL;
     892             : 
     893         564 :         r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
     894         564 :         if (r < 0)
     895           0 :                 return r;
     896             : 
     897         564 :         sd_id128_to_string(j->current_file->header->seqnum_id, sid);
     898         564 :         sd_id128_to_string(o->entry.boot_id, bid);
     899             : 
     900        2256 :         if (asprintf(cursor,
     901             :                      "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
     902         564 :                      sid, le64toh(o->entry.seqnum),
     903         564 :                      bid, le64toh(o->entry.monotonic),
     904         564 :                      le64toh(o->entry.realtime),
     905         564 :                      le64toh(o->entry.xor_hash)) < 0)
     906           0 :                 return -ENOMEM;
     907             : 
     908         564 :         return 0;
     909             : }
     910             : 
     911           0 : _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
     912             :         const char *word, *state;
     913             :         size_t l;
     914             :         unsigned long long seqnum, monotonic, realtime, xor_hash;
     915             :         bool
     916           0 :                 seqnum_id_set = false,
     917           0 :                 seqnum_set = false,
     918           0 :                 boot_id_set = false,
     919           0 :                 monotonic_set = false,
     920           0 :                 realtime_set = false,
     921           0 :                 xor_hash_set = false;
     922             :         sd_id128_t seqnum_id, boot_id;
     923             : 
     924           0 :         assert_return(j, -EINVAL);
     925           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
     926           0 :         assert_return(!isempty(cursor), -EINVAL);
     927             : 
     928           0 :         FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
     929             :                 char *item;
     930           0 :                 int k = 0;
     931             : 
     932           0 :                 if (l < 2 || word[1] != '=')
     933           0 :                         return -EINVAL;
     934             : 
     935           0 :                 item = strndup(word, l);
     936           0 :                 if (!item)
     937           0 :                         return -ENOMEM;
     938             : 
     939           0 :                 switch (word[0]) {
     940             : 
     941             :                 case 's':
     942           0 :                         seqnum_id_set = true;
     943           0 :                         k = sd_id128_from_string(item+2, &seqnum_id);
     944           0 :                         break;
     945             : 
     946             :                 case 'i':
     947           0 :                         seqnum_set = true;
     948           0 :                         if (sscanf(item+2, "%llx", &seqnum) != 1)
     949           0 :                                 k = -EINVAL;
     950           0 :                         break;
     951             : 
     952             :                 case 'b':
     953           0 :                         boot_id_set = true;
     954           0 :                         k = sd_id128_from_string(item+2, &boot_id);
     955           0 :                         break;
     956             : 
     957             :                 case 'm':
     958           0 :                         monotonic_set = true;
     959           0 :                         if (sscanf(item+2, "%llx", &monotonic) != 1)
     960           0 :                                 k = -EINVAL;
     961           0 :                         break;
     962             : 
     963             :                 case 't':
     964           0 :                         realtime_set = true;
     965           0 :                         if (sscanf(item+2, "%llx", &realtime) != 1)
     966           0 :                                 k = -EINVAL;
     967           0 :                         break;
     968             : 
     969             :                 case 'x':
     970           0 :                         xor_hash_set = true;
     971           0 :                         if (sscanf(item+2, "%llx", &xor_hash) != 1)
     972           0 :                                 k = -EINVAL;
     973           0 :                         break;
     974             :                 }
     975             : 
     976           0 :                 free(item);
     977             : 
     978           0 :                 if (k < 0)
     979           0 :                         return k;
     980             :         }
     981             : 
     982           0 :         if ((!seqnum_set || !seqnum_id_set) &&
     983           0 :             (!monotonic_set || !boot_id_set) &&
     984           0 :             !realtime_set)
     985           0 :                 return -EINVAL;
     986             : 
     987           0 :         reset_location(j);
     988             : 
     989           0 :         j->current_location.type = LOCATION_SEEK;
     990             : 
     991           0 :         if (realtime_set) {
     992           0 :                 j->current_location.realtime = (uint64_t) realtime;
     993           0 :                 j->current_location.realtime_set = true;
     994             :         }
     995             : 
     996           0 :         if (seqnum_set && seqnum_id_set) {
     997           0 :                 j->current_location.seqnum = (uint64_t) seqnum;
     998           0 :                 j->current_location.seqnum_id = seqnum_id;
     999           0 :                 j->current_location.seqnum_set = true;
    1000             :         }
    1001             : 
    1002           0 :         if (monotonic_set && boot_id_set) {
    1003           0 :                 j->current_location.monotonic = (uint64_t) monotonic;
    1004           0 :                 j->current_location.boot_id = boot_id;
    1005           0 :                 j->current_location.monotonic_set = true;
    1006             :         }
    1007             : 
    1008           0 :         if (xor_hash_set) {
    1009           0 :                 j->current_location.xor_hash = (uint64_t) xor_hash;
    1010           0 :                 j->current_location.xor_hash_set = true;
    1011             :         }
    1012             : 
    1013           0 :         return 0;
    1014             : }
    1015             : 
    1016         322 : _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) {
    1017             :         int r;
    1018             :         const char *word, *state;
    1019             :         size_t l;
    1020             :         Object *o;
    1021             : 
    1022         322 :         assert_return(j, -EINVAL);
    1023         322 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1024         322 :         assert_return(!isempty(cursor), -EINVAL);
    1025             : 
    1026         322 :         if (!j->current_file || j->current_file->current_offset <= 0)
    1027           0 :                 return -EADDRNOTAVAIL;
    1028             : 
    1029         322 :         r = journal_file_move_to_object(j->current_file, OBJECT_ENTRY, j->current_file->current_offset, &o);
    1030         322 :         if (r < 0)
    1031           0 :                 return r;
    1032             : 
    1033        2254 :         FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
    1034        3864 :                 _cleanup_free_ char *item = NULL;
    1035             :                 sd_id128_t id;
    1036             :                 unsigned long long ll;
    1037        1932 :                 int k = 0;
    1038             : 
    1039        1932 :                 if (l < 2 || word[1] != '=')
    1040           0 :                         return -EINVAL;
    1041             : 
    1042        1932 :                 item = strndup(word, l);
    1043        1932 :                 if (!item)
    1044           0 :                         return -ENOMEM;
    1045             : 
    1046        1932 :                 switch (word[0]) {
    1047             : 
    1048             :                 case 's':
    1049         322 :                         k = sd_id128_from_string(item+2, &id);
    1050         322 :                         if (k < 0)
    1051           0 :                                 return k;
    1052         322 :                         if (!sd_id128_equal(id, j->current_file->header->seqnum_id))
    1053           0 :                                 return 0;
    1054         322 :                         break;
    1055             : 
    1056             :                 case 'i':
    1057         322 :                         if (sscanf(item+2, "%llx", &ll) != 1)
    1058           0 :                                 return -EINVAL;
    1059         322 :                         if (ll != le64toh(o->entry.seqnum))
    1060           0 :                                 return 0;
    1061         322 :                         break;
    1062             : 
    1063             :                 case 'b':
    1064         322 :                         k = sd_id128_from_string(item+2, &id);
    1065         322 :                         if (k < 0)
    1066           0 :                                 return k;
    1067         322 :                         if (!sd_id128_equal(id, o->entry.boot_id))
    1068           0 :                                 return 0;
    1069         322 :                         break;
    1070             : 
    1071             :                 case 'm':
    1072         322 :                         if (sscanf(item+2, "%llx", &ll) != 1)
    1073           0 :                                 return -EINVAL;
    1074         322 :                         if (ll != le64toh(o->entry.monotonic))
    1075           0 :                                 return 0;
    1076         322 :                         break;
    1077             : 
    1078             :                 case 't':
    1079         322 :                         if (sscanf(item+2, "%llx", &ll) != 1)
    1080           0 :                                 return -EINVAL;
    1081         322 :                         if (ll != le64toh(o->entry.realtime))
    1082           0 :                                 return 0;
    1083         322 :                         break;
    1084             : 
    1085             :                 case 'x':
    1086         322 :                         if (sscanf(item+2, "%llx", &ll) != 1)
    1087           0 :                                 return -EINVAL;
    1088         322 :                         if (ll != le64toh(o->entry.xor_hash))
    1089           0 :                                 return 0;
    1090         322 :                         break;
    1091             :                 }
    1092             :         }
    1093             : 
    1094         322 :         return 1;
    1095             : }
    1096             : 
    1097             : 
    1098           0 : _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t usec) {
    1099           0 :         assert_return(j, -EINVAL);
    1100           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1101             : 
    1102           0 :         reset_location(j);
    1103           0 :         j->current_location.type = LOCATION_SEEK;
    1104           0 :         j->current_location.boot_id = boot_id;
    1105           0 :         j->current_location.monotonic = usec;
    1106           0 :         j->current_location.monotonic_set = true;
    1107             : 
    1108           0 :         return 0;
    1109             : }
    1110             : 
    1111           0 : _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
    1112           0 :         assert_return(j, -EINVAL);
    1113           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1114             : 
    1115           0 :         reset_location(j);
    1116           0 :         j->current_location.type = LOCATION_SEEK;
    1117           0 :         j->current_location.realtime = usec;
    1118           0 :         j->current_location.realtime_set = true;
    1119             : 
    1120           0 :         return 0;
    1121             : }
    1122             : 
    1123           9 : _public_ int sd_journal_seek_head(sd_journal *j) {
    1124           9 :         assert_return(j, -EINVAL);
    1125           9 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1126             : 
    1127           9 :         reset_location(j);
    1128           9 :         j->current_location.type = LOCATION_HEAD;
    1129             : 
    1130           9 :         return 0;
    1131             : }
    1132             : 
    1133           5 : _public_ int sd_journal_seek_tail(sd_journal *j) {
    1134           5 :         assert_return(j, -EINVAL);
    1135           5 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1136             : 
    1137           5 :         reset_location(j);
    1138           5 :         j->current_location.type = LOCATION_TAIL;
    1139             : 
    1140           5 :         return 0;
    1141             : }
    1142             : 
    1143         842 : static void check_network(sd_journal *j, int fd) {
    1144             :         struct statfs sfs;
    1145             : 
    1146         842 :         assert(j);
    1147             : 
    1148         842 :         if (j->on_network)
    1149           0 :                 return;
    1150             : 
    1151         842 :         if (fstatfs(fd, &sfs) < 0)
    1152           0 :                 return;
    1153             : 
    1154         842 :         j->on_network =
    1155        1684 :                 F_TYPE_EQUAL(sfs.f_type, CIFS_MAGIC_NUMBER) ||
    1156        1684 :                 F_TYPE_EQUAL(sfs.f_type, CODA_SUPER_MAGIC) ||
    1157        1684 :                 F_TYPE_EQUAL(sfs.f_type, NCP_SUPER_MAGIC) ||
    1158        2526 :                 F_TYPE_EQUAL(sfs.f_type, NFS_SUPER_MAGIC) ||
    1159         842 :                 F_TYPE_EQUAL(sfs.f_type, SMB_SUPER_MAGIC);
    1160             : }
    1161             : 
    1162           0 : static bool file_has_type_prefix(const char *prefix, const char *filename) {
    1163             :         const char *full, *tilded, *atted;
    1164             : 
    1165           0 :         full = strjoina(prefix, ".journal");
    1166           0 :         tilded = strjoina(full, "~");
    1167           0 :         atted = strjoina(prefix, "@");
    1168             : 
    1169           0 :         return streq(filename, full) ||
    1170           0 :                streq(filename, tilded) ||
    1171           0 :                startswith(filename, atted);
    1172             : }
    1173             : 
    1174        1345 : static bool file_type_wanted(int flags, const char *filename) {
    1175        1345 :         if (!endswith(filename, ".journal") && !endswith(filename, ".journal~"))
    1176           0 :                 return false;
    1177             : 
    1178             :         /* no flags set → every type is OK */
    1179        1345 :         if (!(flags & (SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER)))
    1180        1345 :                 return true;
    1181             : 
    1182           0 :         if (flags & SD_JOURNAL_SYSTEM && file_has_type_prefix("system", filename))
    1183           0 :                 return true;
    1184             : 
    1185           0 :         if (flags & SD_JOURNAL_CURRENT_USER) {
    1186             :                 char prefix[5 + DECIMAL_STR_MAX(uid_t) + 1];
    1187             : 
    1188           0 :                 xsprintf(prefix, "user-"UID_FMT, getuid());
    1189             : 
    1190           0 :                 if (file_has_type_prefix(prefix, filename))
    1191           0 :                         return true;
    1192             :         }
    1193             : 
    1194           0 :         return false;
    1195             : }
    1196             : 
    1197        1345 : static int add_any_file(sd_journal *j, const char *path) {
    1198        1345 :         JournalFile *f = NULL;
    1199             :         int r;
    1200             : 
    1201        1345 :         assert(j);
    1202        1345 :         assert(path);
    1203             : 
    1204        1345 :         if (ordered_hashmap_get(j->files, path))
    1205           0 :                 return 0;
    1206             : 
    1207        1345 :         if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) {
    1208           0 :                 log_warning("Too many open journal files, not adding %s.", path);
    1209           0 :                 return set_put_error(j, -ETOOMANYREFS);
    1210             :         }
    1211             : 
    1212        1345 :         r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f);
    1213        1345 :         if (r < 0)
    1214         816 :                 return r;
    1215             : 
    1216             :         /* journal_file_dump(f); */
    1217             : 
    1218         529 :         r = ordered_hashmap_put(j->files, f->path, f);
    1219         529 :         if (r < 0) {
    1220           0 :                 journal_file_close(f);
    1221           0 :                 return r;
    1222             :         }
    1223             : 
    1224         529 :         log_debug("File %s added.", f->path);
    1225             : 
    1226         529 :         check_network(j, f->fd);
    1227             : 
    1228         529 :         j->current_invalidate_counter ++;
    1229             : 
    1230         529 :         return 0;
    1231             : }
    1232             : 
    1233        1345 : static int add_file(sd_journal *j, const char *prefix, const char *filename) {
    1234        2690 :         _cleanup_free_ char *path = NULL;
    1235             :         int r;
    1236             : 
    1237        1345 :         assert(j);
    1238        1345 :         assert(prefix);
    1239        1345 :         assert(filename);
    1240             : 
    1241        2690 :         if (j->no_new_files ||
    1242        1345 :             !file_type_wanted(j->flags, filename))
    1243           0 :                 return 0;
    1244             : 
    1245        1345 :         path = strjoin(prefix, "/", filename, NULL);
    1246        1345 :         if (!path)
    1247           0 :                 return -ENOMEM;
    1248             : 
    1249        1345 :         r = add_any_file(j, path);
    1250        1345 :         if (r == -ENOENT)
    1251           0 :                 return 0;
    1252        1345 :         return r;
    1253             : }
    1254             : 
    1255           0 : static int remove_file(sd_journal *j, const char *prefix, const char *filename) {
    1256           0 :         _cleanup_free_ char *path;
    1257             :         JournalFile *f;
    1258             : 
    1259           0 :         assert(j);
    1260           0 :         assert(prefix);
    1261           0 :         assert(filename);
    1262             : 
    1263           0 :         path = strjoin(prefix, "/", filename, NULL);
    1264           0 :         if (!path)
    1265           0 :                 return -ENOMEM;
    1266             : 
    1267           0 :         f = ordered_hashmap_get(j->files, path);
    1268           0 :         if (!f)
    1269           0 :                 return 0;
    1270             : 
    1271           0 :         remove_file_real(j, f);
    1272           0 :         return 0;
    1273             : }
    1274             : 
    1275           0 : static void remove_file_real(sd_journal *j, JournalFile *f) {
    1276           0 :         assert(j);
    1277           0 :         assert(f);
    1278             : 
    1279           0 :         ordered_hashmap_remove(j->files, f->path);
    1280             : 
    1281           0 :         log_debug("File %s removed.", f->path);
    1282             : 
    1283           0 :         if (j->current_file == f) {
    1284           0 :                 j->current_file = NULL;
    1285           0 :                 j->current_field = 0;
    1286             :         }
    1287             : 
    1288           0 :         if (j->unique_file == f) {
    1289             :                 /* Jump to the next unique_file or NULL if that one was last */
    1290           0 :                 j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
    1291           0 :                 j->unique_offset = 0;
    1292           0 :                 if (!j->unique_file)
    1293           0 :                         j->unique_file_lost = true;
    1294             :         }
    1295             : 
    1296           0 :         journal_file_close(f);
    1297             : 
    1298           0 :         j->current_invalidate_counter ++;
    1299           0 : }
    1300             : 
    1301         102 : static int add_directory(sd_journal *j, const char *prefix, const char *dirname) {
    1302         204 :         _cleanup_free_ char *path = NULL;
    1303             :         int r;
    1304         204 :         _cleanup_closedir_ DIR *d = NULL;
    1305             :         sd_id128_t id, mid;
    1306             :         Directory *m;
    1307             : 
    1308         102 :         assert(j);
    1309         102 :         assert(prefix);
    1310         102 :         assert(dirname);
    1311             : 
    1312         102 :         log_debug("Considering %s/%s.", prefix, dirname);
    1313             : 
    1314         202 :         if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
    1315         200 :             (sd_id128_from_string(dirname, &id) < 0 ||
    1316         200 :              sd_id128_get_machine(&mid) < 0 ||
    1317         100 :              !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
    1318           0 :             return 0;
    1319             : 
    1320         102 :         path = strjoin(prefix, "/", dirname, NULL);
    1321         102 :         if (!path)
    1322           0 :                 return -ENOMEM;
    1323             : 
    1324         102 :         d = opendir(path);
    1325         102 :         if (!d) {
    1326           0 :                 log_debug_errno(errno, "Failed to open %s: %m", path);
    1327           0 :                 if (errno == ENOENT)
    1328           0 :                         return 0;
    1329           0 :                 return -errno;
    1330             :         }
    1331             : 
    1332         102 :         m = hashmap_get(j->directories_by_path, path);
    1333         102 :         if (!m) {
    1334         102 :                 m = new0(Directory, 1);
    1335         102 :                 if (!m)
    1336           0 :                         return -ENOMEM;
    1337             : 
    1338         102 :                 m->is_root = false;
    1339         102 :                 m->path = path;
    1340             : 
    1341         102 :                 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
    1342           0 :                         free(m);
    1343           0 :                         return -ENOMEM;
    1344             :                 }
    1345             : 
    1346         102 :                 path = NULL; /* avoid freeing in cleanup */
    1347         102 :                 j->current_invalidate_counter ++;
    1348             : 
    1349         102 :                 log_debug("Directory %s added.", m->path);
    1350             : 
    1351           0 :         } else if (m->is_root)
    1352           0 :                 return 0;
    1353             : 
    1354         102 :         if (m->wd <= 0 && j->inotify_fd >= 0) {
    1355             : 
    1356           0 :                 m->wd = inotify_add_watch(j->inotify_fd, m->path,
    1357             :                                           IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
    1358             :                                           IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM|
    1359             :                                           IN_ONLYDIR);
    1360             : 
    1361           0 :                 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
    1362           0 :                         inotify_rm_watch(j->inotify_fd, m->wd);
    1363             :         }
    1364             : 
    1365             :         for (;;) {
    1366             :                 struct dirent *de;
    1367             : 
    1368        1632 :                 errno = 0;
    1369        1632 :                 de = readdir(d);
    1370        1632 :                 if (!de && errno != 0) {
    1371           0 :                         r = -errno;
    1372           0 :                         log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
    1373           0 :                         return r;
    1374             :                 }
    1375        1632 :                 if (!de)
    1376         102 :                         break;
    1377             : 
    1378        2346 :                 if (dirent_is_file_with_suffix(de, ".journal") ||
    1379         816 :                     dirent_is_file_with_suffix(de, ".journal~")) {
    1380        1326 :                         r = add_file(j, m->path, de->d_name);
    1381        1326 :                         if (r < 0) {
    1382         816 :                                 log_debug_errno(r, "Failed to add file %s/%s: %m",
    1383             :                                                 m->path, de->d_name);
    1384         816 :                                 r = set_put_error(j, r);
    1385         816 :                                 if (r < 0)
    1386           0 :                                         return r;
    1387             :                         }
    1388             :                 }
    1389        1530 :         }
    1390             : 
    1391         102 :         check_network(j, dirfd(d));
    1392             : 
    1393         102 :         return 0;
    1394             : }
    1395             : 
    1396         313 : static int add_root_directory(sd_journal *j, const char *p) {
    1397         626 :         _cleanup_closedir_ DIR *d = NULL;
    1398             :         Directory *m;
    1399             :         int r;
    1400             : 
    1401         313 :         assert(j);
    1402         313 :         assert(p);
    1403             : 
    1404         313 :         if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) &&
    1405           0 :             !path_startswith(p, "/run"))
    1406           0 :                 return -EINVAL;
    1407             : 
    1408         313 :         if (j->prefix)
    1409           0 :                 p = strjoina(j->prefix, p);
    1410             : 
    1411         313 :         d = opendir(p);
    1412         313 :         if (!d)
    1413         102 :                 return -errno;
    1414             : 
    1415         211 :         m = hashmap_get(j->directories_by_path, p);
    1416         211 :         if (!m) {
    1417         211 :                 m = new0(Directory, 1);
    1418         211 :                 if (!m)
    1419           0 :                         return -ENOMEM;
    1420             : 
    1421         211 :                 m->is_root = true;
    1422         211 :                 m->path = strdup(p);
    1423         211 :                 if (!m->path) {
    1424           0 :                         free(m);
    1425           0 :                         return -ENOMEM;
    1426             :                 }
    1427             : 
    1428         211 :                 if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
    1429           0 :                         free(m->path);
    1430           0 :                         free(m);
    1431           0 :                         return -ENOMEM;
    1432             :                 }
    1433             : 
    1434         211 :                 j->current_invalidate_counter ++;
    1435             : 
    1436         211 :                 log_debug("Root directory %s added.", m->path);
    1437             : 
    1438           0 :         } else if (!m->is_root)
    1439           0 :                 return 0;
    1440             : 
    1441         211 :         if (m->wd <= 0 && j->inotify_fd >= 0) {
    1442             : 
    1443           0 :                 m->wd = inotify_add_watch(j->inotify_fd, m->path,
    1444             :                                           IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE|
    1445             :                                           IN_ONLYDIR);
    1446             : 
    1447           0 :                 if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0)
    1448           0 :                         inotify_rm_watch(j->inotify_fd, m->wd);
    1449             :         }
    1450             : 
    1451         211 :         if (j->no_new_files)
    1452           0 :                 return 0;
    1453             : 
    1454             :         for (;;) {
    1455             :                 struct dirent *de;
    1456             :                 sd_id128_t id;
    1457             : 
    1458         754 :                 errno = 0;
    1459         754 :                 de = readdir(d);
    1460         754 :                 if (!de && errno != 0) {
    1461           0 :                         r = -errno;
    1462           0 :                         log_debug_errno(errno, "Failed to read directory %s: %m", m->path);
    1463           0 :                         return r;
    1464             :                 }
    1465         754 :                 if (!de)
    1466         211 :                         break;
    1467             : 
    1468        1067 :                 if (dirent_is_file_with_suffix(de, ".journal") ||
    1469         524 :                     dirent_is_file_with_suffix(de, ".journal~")) {
    1470          19 :                         r = add_file(j, m->path, de->d_name);
    1471          38 :                         if (r < 0) {
    1472           0 :                                 log_debug_errno(r, "Failed to add file %s/%s: %m",
    1473             :                                                 m->path, de->d_name);
    1474           0 :                                 r = set_put_error(j, r);
    1475           0 :                                 if (r < 0)
    1476           0 :                                         return r;
    1477             :                         }
    1478        1048 :                 } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) &&
    1479         524 :                            sd_id128_from_string(de->d_name, &id) >= 0) {
    1480             : 
    1481         102 :                         r = add_directory(j, m->path, de->d_name);
    1482         102 :                         if (r < 0)
    1483           0 :                                 log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name);
    1484             :                 }
    1485         543 :         }
    1486             : 
    1487         211 :         check_network(j, dirfd(d));
    1488             : 
    1489         211 :         return 0;
    1490             : }
    1491             : 
    1492         313 : static int remove_directory(sd_journal *j, Directory *d) {
    1493         313 :         assert(j);
    1494             : 
    1495         313 :         if (d->wd > 0) {
    1496           0 :                 hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
    1497             : 
    1498           0 :                 if (j->inotify_fd >= 0)
    1499           0 :                         inotify_rm_watch(j->inotify_fd, d->wd);
    1500             :         }
    1501             : 
    1502         313 :         hashmap_remove(j->directories_by_path, d->path);
    1503             : 
    1504         313 :         if (d->is_root)
    1505         211 :                 log_debug("Root directory %s removed.", d->path);
    1506             :         else
    1507         102 :                 log_debug("Directory %s removed.", d->path);
    1508             : 
    1509         313 :         free(d->path);
    1510         313 :         free(d);
    1511             : 
    1512         313 :         return 0;
    1513             : }
    1514             : 
    1515         102 : static int add_search_paths(sd_journal *j) {
    1516             :         int r;
    1517         102 :         const char search_paths[] =
    1518             :                 "/run/log/journal\0"
    1519             :                 "/var/log/journal\0";
    1520             :         const char *p;
    1521             : 
    1522         102 :         assert(j);
    1523             : 
    1524             :         /* We ignore most errors here, since the idea is to only open
    1525             :          * what's actually accessible, and ignore the rest. */
    1526             : 
    1527         306 :         NULSTR_FOREACH(p, search_paths) {
    1528         204 :                 r = add_root_directory(j, p);
    1529         204 :                 if (r < 0 && r != -ENOENT) {
    1530           0 :                         r = set_put_error(j, r);
    1531           0 :                         if (r < 0)
    1532           0 :                                 return r;
    1533             :                 }
    1534             :         }
    1535             : 
    1536         102 :         return 0;
    1537             : }
    1538             : 
    1539           0 : static int add_current_paths(sd_journal *j) {
    1540             :         Iterator i;
    1541             :         JournalFile *f;
    1542             : 
    1543           0 :         assert(j);
    1544           0 :         assert(j->no_new_files);
    1545             : 
    1546             :         /* Simply adds all directories for files we have open as
    1547             :          * "root" directories. We don't expect errors here, so we
    1548             :          * treat them as fatal. */
    1549             : 
    1550           0 :         ORDERED_HASHMAP_FOREACH(f, j->files, i) {
    1551           0 :                 _cleanup_free_ char *dir;
    1552             :                 int r;
    1553             : 
    1554           0 :                 dir = dirname_malloc(f->path);
    1555           0 :                 if (!dir)
    1556           0 :                         return -ENOMEM;
    1557             : 
    1558           0 :                 r = add_root_directory(j, dir);
    1559           0 :                 if (r < 0) {
    1560           0 :                         set_put_error(j, r);
    1561           0 :                         return r;
    1562             :                 }
    1563             :         }
    1564             : 
    1565           0 :         return 0;
    1566             : }
    1567             : 
    1568             : 
    1569           0 : static int allocate_inotify(sd_journal *j) {
    1570           0 :         assert(j);
    1571             : 
    1572           0 :         if (j->inotify_fd < 0) {
    1573           0 :                 j->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
    1574           0 :                 if (j->inotify_fd < 0)
    1575           0 :                         return -errno;
    1576             :         }
    1577             : 
    1578           0 :         if (!j->directories_by_wd) {
    1579           0 :                 j->directories_by_wd = hashmap_new(NULL);
    1580           0 :                 if (!j->directories_by_wd)
    1581           0 :                         return -ENOMEM;
    1582             :         }
    1583             : 
    1584           0 :         return 0;
    1585             : }
    1586             : 
    1587         211 : static sd_journal *journal_new(int flags, const char *path) {
    1588             :         sd_journal *j;
    1589             : 
    1590         211 :         j = new0(sd_journal, 1);
    1591         211 :         if (!j)
    1592           0 :                 return NULL;
    1593             : 
    1594         211 :         j->original_pid = getpid();
    1595         211 :         j->inotify_fd = -1;
    1596         211 :         j->flags = flags;
    1597         211 :         j->data_threshold = DEFAULT_DATA_THRESHOLD;
    1598             : 
    1599         211 :         if (path) {
    1600         109 :                 j->path = strdup(path);
    1601         109 :                 if (!j->path)
    1602           0 :                         goto fail;
    1603             :         }
    1604             : 
    1605         211 :         j->files = ordered_hashmap_new(&string_hash_ops);
    1606         211 :         j->directories_by_path = hashmap_new(&string_hash_ops);
    1607         211 :         j->mmap = mmap_cache_new();
    1608         211 :         if (!j->files || !j->directories_by_path || !j->mmap)
    1609             :                 goto fail;
    1610             : 
    1611         211 :         return j;
    1612             : 
    1613             : fail:
    1614           0 :         sd_journal_close(j);
    1615           0 :         return NULL;
    1616             : }
    1617             : 
    1618         102 : _public_ int sd_journal_open(sd_journal **ret, int flags) {
    1619             :         sd_journal *j;
    1620             :         int r;
    1621             : 
    1622         102 :         assert_return(ret, -EINVAL);
    1623         102 :         assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY|SD_JOURNAL_SYSTEM|SD_JOURNAL_CURRENT_USER)) == 0, -EINVAL);
    1624             : 
    1625         102 :         j = journal_new(flags, NULL);
    1626         102 :         if (!j)
    1627           0 :                 return -ENOMEM;
    1628             : 
    1629         102 :         r = add_search_paths(j);
    1630         102 :         if (r < 0)
    1631           0 :                 goto fail;
    1632             : 
    1633         102 :         *ret = j;
    1634         102 :         return 0;
    1635             : 
    1636             : fail:
    1637           0 :         sd_journal_close(j);
    1638             : 
    1639           0 :         return r;
    1640             : }
    1641             : 
    1642           0 : _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
    1643           0 :         _cleanup_free_ char *root = NULL, *class = NULL;
    1644             :         sd_journal *j;
    1645             :         char *p;
    1646             :         int r;
    1647             : 
    1648           0 :         assert_return(machine, -EINVAL);
    1649           0 :         assert_return(ret, -EINVAL);
    1650           0 :         assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL);
    1651           0 :         assert_return(machine_name_is_valid(machine), -EINVAL);
    1652             : 
    1653           0 :         p = strjoina("/run/systemd/machines/", machine);
    1654           0 :         r = parse_env_file(p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL);
    1655           0 :         if (r == -ENOENT)
    1656           0 :                 return -EHOSTDOWN;
    1657           0 :         if (r < 0)
    1658           0 :                 return r;
    1659           0 :         if (!root)
    1660           0 :                 return -ENODATA;
    1661             : 
    1662           0 :         if (!streq_ptr(class, "container"))
    1663           0 :                 return -EIO;
    1664             : 
    1665           0 :         j = journal_new(flags, NULL);
    1666           0 :         if (!j)
    1667           0 :                 return -ENOMEM;
    1668             : 
    1669           0 :         j->prefix = root;
    1670           0 :         root = NULL;
    1671             : 
    1672           0 :         r = add_search_paths(j);
    1673           0 :         if (r < 0)
    1674           0 :                 goto fail;
    1675             : 
    1676           0 :         *ret = j;
    1677           0 :         return 0;
    1678             : 
    1679             : fail:
    1680           0 :         sd_journal_close(j);
    1681           0 :         return r;
    1682             : }
    1683             : 
    1684         209 : _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
    1685             :         sd_journal *j;
    1686             :         int r;
    1687             : 
    1688         209 :         assert_return(ret, -EINVAL);
    1689         209 :         assert_return(path, -EINVAL);
    1690         209 :         assert_return(flags == 0, -EINVAL);
    1691             : 
    1692         109 :         j = journal_new(flags, path);
    1693         109 :         if (!j)
    1694           0 :                 return -ENOMEM;
    1695             : 
    1696         109 :         r = add_root_directory(j, path);
    1697         109 :         if (r < 0) {
    1698           0 :                 set_put_error(j, r);
    1699           0 :                 goto fail;
    1700             :         }
    1701             : 
    1702         109 :         *ret = j;
    1703         109 :         return 0;
    1704             : 
    1705             : fail:
    1706           0 :         sd_journal_close(j);
    1707             : 
    1708           0 :         return r;
    1709             : }
    1710             : 
    1711           0 : _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
    1712             :         sd_journal *j;
    1713             :         const char **path;
    1714             :         int r;
    1715             : 
    1716           0 :         assert_return(ret, -EINVAL);
    1717           0 :         assert_return(flags == 0, -EINVAL);
    1718             : 
    1719           0 :         j = journal_new(flags, NULL);
    1720           0 :         if (!j)
    1721           0 :                 return -ENOMEM;
    1722             : 
    1723           0 :         STRV_FOREACH(path, paths) {
    1724           0 :                 r = add_any_file(j, *path);
    1725           0 :                 if (r < 0) {
    1726           0 :                         log_error_errno(r, "Failed to open %s: %m", *path);
    1727           0 :                         goto fail;
    1728             :                 }
    1729             :         }
    1730             : 
    1731           0 :         j->no_new_files = true;
    1732             : 
    1733           0 :         *ret = j;
    1734           0 :         return 0;
    1735             : 
    1736             : fail:
    1737           0 :         sd_journal_close(j);
    1738             : 
    1739           0 :         return r;
    1740             : }
    1741             : 
    1742         211 : _public_ void sd_journal_close(sd_journal *j) {
    1743             :         Directory *d;
    1744             :         JournalFile *f;
    1745             : 
    1746         211 :         if (!j)
    1747           0 :                 return;
    1748             : 
    1749         211 :         sd_journal_flush_matches(j);
    1750             : 
    1751         951 :         while ((f = ordered_hashmap_steal_first(j->files)))
    1752         529 :                 journal_file_close(f);
    1753             : 
    1754         211 :         ordered_hashmap_free(j->files);
    1755             : 
    1756         735 :         while ((d = hashmap_first(j->directories_by_path)))
    1757         313 :                 remove_directory(j, d);
    1758             : 
    1759         422 :         while ((d = hashmap_first(j->directories_by_wd)))
    1760           0 :                 remove_directory(j, d);
    1761             : 
    1762         211 :         hashmap_free(j->directories_by_path);
    1763         211 :         hashmap_free(j->directories_by_wd);
    1764             : 
    1765         211 :         safe_close(j->inotify_fd);
    1766             : 
    1767         211 :         if (j->mmap) {
    1768         211 :                 log_debug("mmap cache statistics: %u hit, %u miss", mmap_cache_get_hit(j->mmap), mmap_cache_get_missed(j->mmap));
    1769         211 :                 mmap_cache_unref(j->mmap);
    1770             :         }
    1771             : 
    1772         211 :         free(j->path);
    1773         211 :         free(j->prefix);
    1774         211 :         free(j->unique_field);
    1775         211 :         set_free(j->errors);
    1776         211 :         free(j);
    1777             : }
    1778             : 
    1779           0 : _public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
    1780             :         Object *o;
    1781             :         JournalFile *f;
    1782             :         int r;
    1783             : 
    1784           0 :         assert_return(j, -EINVAL);
    1785           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1786           0 :         assert_return(ret, -EINVAL);
    1787             : 
    1788           0 :         f = j->current_file;
    1789           0 :         if (!f)
    1790           0 :                 return -EADDRNOTAVAIL;
    1791             : 
    1792           0 :         if (f->current_offset <= 0)
    1793           0 :                 return -EADDRNOTAVAIL;
    1794             : 
    1795           0 :         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
    1796           0 :         if (r < 0)
    1797           0 :                 return r;
    1798             : 
    1799           0 :         *ret = le64toh(o->entry.realtime);
    1800           0 :         return 0;
    1801             : }
    1802             : 
    1803           0 : _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id128_t *ret_boot_id) {
    1804             :         Object *o;
    1805             :         JournalFile *f;
    1806             :         int r;
    1807             :         sd_id128_t id;
    1808             : 
    1809           0 :         assert_return(j, -EINVAL);
    1810           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1811             : 
    1812           0 :         f = j->current_file;
    1813           0 :         if (!f)
    1814           0 :                 return -EADDRNOTAVAIL;
    1815             : 
    1816           0 :         if (f->current_offset <= 0)
    1817           0 :                 return -EADDRNOTAVAIL;
    1818             : 
    1819           0 :         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
    1820           0 :         if (r < 0)
    1821           0 :                 return r;
    1822             : 
    1823           0 :         if (ret_boot_id)
    1824           0 :                 *ret_boot_id = o->entry.boot_id;
    1825             :         else {
    1826           0 :                 r = sd_id128_get_boot(&id);
    1827           0 :                 if (r < 0)
    1828           0 :                         return r;
    1829             : 
    1830           0 :                 if (!sd_id128_equal(id, o->entry.boot_id))
    1831           0 :                         return -ESTALE;
    1832             :         }
    1833             : 
    1834           0 :         if (ret)
    1835           0 :                 *ret = le64toh(o->entry.monotonic);
    1836             : 
    1837           0 :         return 0;
    1838             : }
    1839             : 
    1840         597 : static bool field_is_valid(const char *field) {
    1841             :         const char *p;
    1842             : 
    1843         597 :         assert(field);
    1844             : 
    1845         597 :         if (isempty(field))
    1846           0 :                 return false;
    1847             : 
    1848         597 :         if (startswith(field, "__"))
    1849           0 :                 return false;
    1850             : 
    1851        3937 :         for (p = field; *p; p++) {
    1852             : 
    1853        3340 :                 if (*p == '_')
    1854           0 :                         continue;
    1855             : 
    1856        3340 :                 if (*p >= 'A' && *p <= 'Z')
    1857        3340 :                         continue;
    1858             : 
    1859           0 :                 if (*p >= '0' && *p <= '9')
    1860           0 :                         continue;
    1861             : 
    1862           0 :                 return false;
    1863             :         }
    1864             : 
    1865         597 :         return true;
    1866             : }
    1867             : 
    1868         596 : _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *size) {
    1869             :         JournalFile *f;
    1870             :         uint64_t i, n;
    1871             :         size_t field_length;
    1872             :         int r;
    1873             :         Object *o;
    1874             : 
    1875         596 :         assert_return(j, -EINVAL);
    1876         596 :         assert_return(!journal_pid_changed(j), -ECHILD);
    1877         596 :         assert_return(field, -EINVAL);
    1878         596 :         assert_return(data, -EINVAL);
    1879         596 :         assert_return(size, -EINVAL);
    1880         596 :         assert_return(field_is_valid(field), -EINVAL);
    1881             : 
    1882         596 :         f = j->current_file;
    1883         596 :         if (!f)
    1884           0 :                 return -EADDRNOTAVAIL;
    1885             : 
    1886         596 :         if (f->current_offset <= 0)
    1887           0 :                 return -EADDRNOTAVAIL;
    1888             : 
    1889         596 :         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
    1890         596 :         if (r < 0)
    1891           0 :                 return r;
    1892             : 
    1893         596 :         field_length = strlen(field);
    1894             : 
    1895         596 :         n = journal_file_entry_n_items(o);
    1896         912 :         for (i = 0; i < n; i++) {
    1897             :                 uint64_t p, l;
    1898             :                 le64_t le_hash;
    1899             :                 size_t t;
    1900             :                 int compression;
    1901             : 
    1902         912 :                 p = le64toh(o->entry.items[i].object_offset);
    1903         912 :                 le_hash = o->entry.items[i].hash;
    1904         912 :                 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
    1905         912 :                 if (r < 0)
    1906           0 :                         return r;
    1907             : 
    1908         912 :                 if (le_hash != o->data.hash)
    1909           0 :                         return -EBADMSG;
    1910             : 
    1911         912 :                 l = le64toh(o->object.size) - offsetof(Object, data.payload);
    1912             : 
    1913         912 :                 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
    1914         912 :                 if (compression) {
    1915             : #if defined(HAVE_XZ) || defined(HAVE_LZ4)
    1916           0 :                         if (decompress_startswith(compression,
    1917           0 :                                                   o->data.payload, l,
    1918             :                                                   &f->compress_buffer, &f->compress_buffer_size,
    1919             :                                                   field, field_length, '=')) {
    1920             : 
    1921             :                                 size_t rsize;
    1922             : 
    1923           0 :                                 r = decompress_blob(compression,
    1924           0 :                                                     o->data.payload, l,
    1925             :                                                     &f->compress_buffer, &f->compress_buffer_size, &rsize,
    1926             :                                                     j->data_threshold);
    1927           0 :                                 if (r < 0)
    1928           0 :                                         return r;
    1929             : 
    1930           0 :                                 *data = f->compress_buffer;
    1931           0 :                                 *size = (size_t) rsize;
    1932             : 
    1933           0 :                                 return 0;
    1934             :                         }
    1935             : #else
    1936             :                         return -EPROTONOSUPPORT;
    1937             : #endif
    1938        1824 :                 } else if (l >= field_length+1 &&
    1939        1508 :                            memcmp(o->data.payload, field, field_length) == 0 &&
    1940         596 :                            o->data.payload[field_length] == '=') {
    1941             : 
    1942         596 :                         t = (size_t) l;
    1943             : 
    1944         596 :                         if ((uint64_t) t != l)
    1945           0 :                                 return -E2BIG;
    1946             : 
    1947         596 :                         *data = o->data.payload;
    1948         596 :                         *size = t;
    1949             : 
    1950         596 :                         return 0;
    1951             :                 }
    1952             : 
    1953         316 :                 r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
    1954         316 :                 if (r < 0)
    1955           0 :                         return r;
    1956             :         }
    1957             : 
    1958           0 :         return -ENOENT;
    1959             : }
    1960             : 
    1961         460 : static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **data, size_t *size) {
    1962             :         size_t t;
    1963             :         uint64_t l;
    1964             :         int compression;
    1965             : 
    1966         460 :         l = le64toh(o->object.size) - offsetof(Object, data.payload);
    1967         460 :         t = (size_t) l;
    1968             : 
    1969             :         /* We can't read objects larger than 4G on a 32bit machine */
    1970         460 :         if ((uint64_t) t != l)
    1971           0 :                 return -E2BIG;
    1972             : 
    1973         460 :         compression = o->object.flags & OBJECT_COMPRESSION_MASK;
    1974         460 :         if (compression) {
    1975             : #if defined(HAVE_XZ) || defined(HAVE_LZ4)
    1976             :                 size_t rsize;
    1977             :                 int r;
    1978             : 
    1979           0 :                 r = decompress_blob(compression,
    1980           0 :                                     o->data.payload, l, &f->compress_buffer,
    1981             :                                     &f->compress_buffer_size, &rsize, j->data_threshold);
    1982           0 :                 if (r < 0)
    1983           0 :                         return r;
    1984             : 
    1985           0 :                 *data = f->compress_buffer;
    1986           0 :                 *size = (size_t) rsize;
    1987             : #else
    1988             :                 return -EPROTONOSUPPORT;
    1989             : #endif
    1990             :         } else {
    1991         460 :                 *data = o->data.payload;
    1992         460 :                 *size = t;
    1993             :         }
    1994             : 
    1995         460 :         return 0;
    1996             : }
    1997             : 
    1998           0 : _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) {
    1999             :         JournalFile *f;
    2000             :         uint64_t p, n;
    2001             :         le64_t le_hash;
    2002             :         int r;
    2003             :         Object *o;
    2004             : 
    2005           0 :         assert_return(j, -EINVAL);
    2006           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2007           0 :         assert_return(data, -EINVAL);
    2008           0 :         assert_return(size, -EINVAL);
    2009             : 
    2010           0 :         f = j->current_file;
    2011           0 :         if (!f)
    2012           0 :                 return -EADDRNOTAVAIL;
    2013             : 
    2014           0 :         if (f->current_offset <= 0)
    2015           0 :                 return -EADDRNOTAVAIL;
    2016             : 
    2017           0 :         r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
    2018           0 :         if (r < 0)
    2019           0 :                 return r;
    2020             : 
    2021           0 :         n = journal_file_entry_n_items(o);
    2022           0 :         if (j->current_field >= n)
    2023           0 :                 return 0;
    2024             : 
    2025           0 :         p = le64toh(o->entry.items[j->current_field].object_offset);
    2026           0 :         le_hash = o->entry.items[j->current_field].hash;
    2027           0 :         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
    2028           0 :         if (r < 0)
    2029           0 :                 return r;
    2030             : 
    2031           0 :         if (le_hash != o->data.hash)
    2032           0 :                 return -EBADMSG;
    2033             : 
    2034           0 :         r = return_data(j, f, o, data, size);
    2035           0 :         if (r < 0)
    2036           0 :                 return r;
    2037             : 
    2038           0 :         j->current_field ++;
    2039             : 
    2040           0 :         return 1;
    2041             : }
    2042             : 
    2043           0 : _public_ void sd_journal_restart_data(sd_journal *j) {
    2044           0 :         if (!j)
    2045           0 :                 return;
    2046             : 
    2047           0 :         j->current_field = 0;
    2048             : }
    2049             : 
    2050           0 : _public_ int sd_journal_get_fd(sd_journal *j) {
    2051             :         int r;
    2052             : 
    2053           0 :         assert_return(j, -EINVAL);
    2054           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2055             : 
    2056           0 :         if (j->inotify_fd >= 0)
    2057           0 :                 return j->inotify_fd;
    2058             : 
    2059           0 :         r = allocate_inotify(j);
    2060           0 :         if (r < 0)
    2061           0 :                 return r;
    2062             : 
    2063             :         /* Iterate through all dirs again, to add them to the
    2064             :          * inotify */
    2065           0 :         if (j->no_new_files)
    2066           0 :                 r = add_current_paths(j);
    2067           0 :         else if (j->path)
    2068           0 :                 r = add_root_directory(j, j->path);
    2069             :         else
    2070           0 :                 r = add_search_paths(j);
    2071           0 :         if (r < 0)
    2072           0 :                 return r;
    2073             : 
    2074           0 :         return j->inotify_fd;
    2075             : }
    2076             : 
    2077           0 : _public_ int sd_journal_get_events(sd_journal *j) {
    2078             :         int fd;
    2079             : 
    2080           0 :         assert_return(j, -EINVAL);
    2081           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2082             : 
    2083           0 :         fd = sd_journal_get_fd(j);
    2084           0 :         if (fd < 0)
    2085           0 :                 return fd;
    2086             : 
    2087           0 :         return POLLIN;
    2088             : }
    2089             : 
    2090           0 : _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
    2091             :         int fd;
    2092             : 
    2093           0 :         assert_return(j, -EINVAL);
    2094           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2095           0 :         assert_return(timeout_usec, -EINVAL);
    2096             : 
    2097           0 :         fd = sd_journal_get_fd(j);
    2098           0 :         if (fd < 0)
    2099           0 :                 return fd;
    2100             : 
    2101           0 :         if (!j->on_network) {
    2102           0 :                 *timeout_usec = (uint64_t) -1;
    2103           0 :                 return 0;
    2104             :         }
    2105             : 
    2106             :         /* If we are on the network we need to regularly check for
    2107             :          * changes manually */
    2108             : 
    2109           0 :         *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC;
    2110           0 :         return 1;
    2111             : }
    2112             : 
    2113           0 : static void process_inotify_event(sd_journal *j, struct inotify_event *e) {
    2114             :         Directory *d;
    2115             :         int r;
    2116             : 
    2117           0 :         assert(j);
    2118           0 :         assert(e);
    2119             : 
    2120             :         /* Is this a subdirectory we watch? */
    2121           0 :         d = hashmap_get(j->directories_by_wd, INT_TO_PTR(e->wd));
    2122           0 :         if (d) {
    2123             :                 sd_id128_t id;
    2124             : 
    2125           0 :                 if (!(e->mask & IN_ISDIR) && e->len > 0 &&
    2126           0 :                     (endswith(e->name, ".journal") ||
    2127           0 :                      endswith(e->name, ".journal~"))) {
    2128             : 
    2129             :                         /* Event for a journal file */
    2130             : 
    2131           0 :                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
    2132           0 :                                 r = add_file(j, d->path, e->name);
    2133           0 :                                 if (r < 0) {
    2134           0 :                                         log_debug_errno(r, "Failed to add file %s/%s: %m",
    2135             :                                                         d->path, e->name);
    2136           0 :                                         set_put_error(j, r);
    2137             :                                 }
    2138             : 
    2139           0 :                         } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) {
    2140             : 
    2141           0 :                                 r = remove_file(j, d->path, e->name);
    2142           0 :                                 if (r < 0)
    2143           0 :                                         log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name);
    2144             :                         }
    2145             : 
    2146           0 :                 } else if (!d->is_root && e->len == 0) {
    2147             : 
    2148             :                         /* Event for a subdirectory */
    2149             : 
    2150           0 :                         if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) {
    2151           0 :                                 r = remove_directory(j, d);
    2152           0 :                                 if (r < 0)
    2153           0 :                                         log_debug_errno(r, "Failed to remove directory %s: %m", d->path);
    2154             :                         }
    2155             : 
    2156             : 
    2157           0 :                 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && sd_id128_from_string(e->name, &id) >= 0) {
    2158             : 
    2159             :                         /* Event for root directory */
    2160             : 
    2161           0 :                         if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) {
    2162           0 :                                 r = add_directory(j, d->path, e->name);
    2163           0 :                                 if (r < 0)
    2164           0 :                                         log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name);
    2165             :                         }
    2166             :                 }
    2167             : 
    2168           0 :                 return;
    2169             :         }
    2170             : 
    2171           0 :         if (e->mask & IN_IGNORED)
    2172           0 :                 return;
    2173             : 
    2174           0 :         log_warning("Unknown inotify event.");
    2175             : }
    2176             : 
    2177           0 : static int determine_change(sd_journal *j) {
    2178             :         bool b;
    2179             : 
    2180           0 :         assert(j);
    2181             : 
    2182           0 :         b = j->current_invalidate_counter != j->last_invalidate_counter;
    2183           0 :         j->last_invalidate_counter = j->current_invalidate_counter;
    2184             : 
    2185           0 :         return b ? SD_JOURNAL_INVALIDATE : SD_JOURNAL_APPEND;
    2186             : }
    2187             : 
    2188           0 : _public_ int sd_journal_process(sd_journal *j) {
    2189           0 :         bool got_something = false;
    2190             : 
    2191           0 :         assert_return(j, -EINVAL);
    2192           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2193             : 
    2194           0 :         j->last_process_usec = now(CLOCK_MONOTONIC);
    2195             : 
    2196             :         for (;;) {
    2197             :                 union inotify_event_buffer buffer;
    2198             :                 struct inotify_event *e;
    2199             :                 ssize_t l;
    2200             : 
    2201           0 :                 l = read(j->inotify_fd, &buffer, sizeof(buffer));
    2202           0 :                 if (l < 0) {
    2203           0 :                         if (errno == EAGAIN || errno == EINTR)
    2204           0 :                                 return got_something ? determine_change(j) : SD_JOURNAL_NOP;
    2205             : 
    2206           0 :                         return -errno;
    2207             :                 }
    2208             : 
    2209           0 :                 got_something = true;
    2210             : 
    2211           0 :                 FOREACH_INOTIFY_EVENT(e, buffer, l)
    2212           0 :                         process_inotify_event(j, e);
    2213           0 :         }
    2214             : }
    2215             : 
    2216           0 : _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
    2217             :         int r;
    2218             :         uint64_t t;
    2219             : 
    2220           0 :         assert_return(j, -EINVAL);
    2221           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2222             : 
    2223           0 :         if (j->inotify_fd < 0) {
    2224             : 
    2225             :                 /* This is the first invocation, hence create the
    2226             :                  * inotify watch */
    2227           0 :                 r = sd_journal_get_fd(j);
    2228           0 :                 if (r < 0)
    2229           0 :                         return r;
    2230             : 
    2231             :                 /* The journal might have changed since the context
    2232             :                  * object was created and we weren't watching before,
    2233             :                  * hence don't wait for anything, and return
    2234             :                  * immediately. */
    2235           0 :                 return determine_change(j);
    2236             :         }
    2237             : 
    2238           0 :         r = sd_journal_get_timeout(j, &t);
    2239           0 :         if (r < 0)
    2240           0 :                 return r;
    2241             : 
    2242           0 :         if (t != (uint64_t) -1) {
    2243             :                 usec_t n;
    2244             : 
    2245           0 :                 n = now(CLOCK_MONOTONIC);
    2246           0 :                 t = t > n ? t - n : 0;
    2247             : 
    2248           0 :                 if (timeout_usec == (uint64_t) -1 || timeout_usec > t)
    2249           0 :                         timeout_usec = t;
    2250             :         }
    2251             : 
    2252             :         do {
    2253           0 :                 r = fd_wait_for_event(j->inotify_fd, POLLIN, timeout_usec);
    2254           0 :         } while (r == -EINTR);
    2255             : 
    2256           0 :         if (r < 0)
    2257           0 :                 return r;
    2258             : 
    2259           0 :         return sd_journal_process(j);
    2260             : }
    2261             : 
    2262           0 : _public_ int sd_journal_get_cutoff_realtime_usec(sd_journal *j, uint64_t *from, uint64_t *to) {
    2263             :         Iterator i;
    2264             :         JournalFile *f;
    2265           0 :         bool first = true;
    2266           0 :         uint64_t fmin = 0, tmax = 0;
    2267             :         int r;
    2268             : 
    2269           0 :         assert_return(j, -EINVAL);
    2270           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2271           0 :         assert_return(from || to, -EINVAL);
    2272           0 :         assert_return(from != to, -EINVAL);
    2273             : 
    2274           0 :         ORDERED_HASHMAP_FOREACH(f, j->files, i) {
    2275             :                 usec_t fr, t;
    2276             : 
    2277           0 :                 r = journal_file_get_cutoff_realtime_usec(f, &fr, &t);
    2278           0 :                 if (r == -ENOENT)
    2279           0 :                         continue;
    2280           0 :                 if (r < 0)
    2281           0 :                         return r;
    2282           0 :                 if (r == 0)
    2283           0 :                         continue;
    2284             : 
    2285           0 :                 if (first) {
    2286           0 :                         fmin = fr;
    2287           0 :                         tmax = t;
    2288           0 :                         first = false;
    2289             :                 } else {
    2290           0 :                         fmin = MIN(fr, fmin);
    2291           0 :                         tmax = MAX(t, tmax);
    2292             :                 }
    2293             :         }
    2294             : 
    2295           0 :         if (from)
    2296           0 :                 *from = fmin;
    2297           0 :         if (to)
    2298           0 :                 *to = tmax;
    2299             : 
    2300           0 :         return first ? 0 : 1;
    2301             : }
    2302             : 
    2303           0 : _public_ int sd_journal_get_cutoff_monotonic_usec(sd_journal *j, sd_id128_t boot_id, uint64_t *from, uint64_t *to) {
    2304             :         Iterator i;
    2305             :         JournalFile *f;
    2306           0 :         bool found = false;
    2307             :         int r;
    2308             : 
    2309           0 :         assert_return(j, -EINVAL);
    2310           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2311           0 :         assert_return(from || to, -EINVAL);
    2312           0 :         assert_return(from != to, -EINVAL);
    2313             : 
    2314           0 :         ORDERED_HASHMAP_FOREACH(f, j->files, i) {
    2315             :                 usec_t fr, t;
    2316             : 
    2317           0 :                 r = journal_file_get_cutoff_monotonic_usec(f, boot_id, &fr, &t);
    2318           0 :                 if (r == -ENOENT)
    2319           0 :                         continue;
    2320           0 :                 if (r < 0)
    2321           0 :                         return r;
    2322           0 :                 if (r == 0)
    2323           0 :                         continue;
    2324             : 
    2325           0 :                 if (found) {
    2326           0 :                         if (from)
    2327           0 :                                 *from = MIN(fr, *from);
    2328           0 :                         if (to)
    2329           0 :                                 *to = MAX(t, *to);
    2330             :                 } else {
    2331           0 :                         if (from)
    2332           0 :                                 *from = fr;
    2333           0 :                         if (to)
    2334           0 :                                 *to = t;
    2335           0 :                         found = true;
    2336             :                 }
    2337             :         }
    2338             : 
    2339           0 :         return found;
    2340             : }
    2341             : 
    2342           0 : void journal_print_header(sd_journal *j) {
    2343             :         Iterator i;
    2344             :         JournalFile *f;
    2345           0 :         bool newline = false;
    2346             : 
    2347           0 :         assert(j);
    2348             : 
    2349           0 :         ORDERED_HASHMAP_FOREACH(f, j->files, i) {
    2350           0 :                 if (newline)
    2351           0 :                         putchar('\n');
    2352             :                 else
    2353           0 :                         newline = true;
    2354             : 
    2355           0 :                 journal_file_print_header(f);
    2356             :         }
    2357           0 : }
    2358             : 
    2359           0 : _public_ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes) {
    2360             :         Iterator i;
    2361             :         JournalFile *f;
    2362           0 :         uint64_t sum = 0;
    2363             : 
    2364           0 :         assert_return(j, -EINVAL);
    2365           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2366           0 :         assert_return(bytes, -EINVAL);
    2367             : 
    2368           0 :         ORDERED_HASHMAP_FOREACH(f, j->files, i) {
    2369             :                 struct stat st;
    2370             : 
    2371           0 :                 if (fstat(f->fd, &st) < 0)
    2372           0 :                         return -errno;
    2373             : 
    2374           0 :                 sum += (uint64_t) st.st_blocks * 512ULL;
    2375             :         }
    2376             : 
    2377           0 :         *bytes = sum;
    2378           0 :         return 0;
    2379             : }
    2380             : 
    2381           1 : _public_ int sd_journal_query_unique(sd_journal *j, const char *field) {
    2382             :         char *f;
    2383             : 
    2384           1 :         assert_return(j, -EINVAL);
    2385           1 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2386           1 :         assert_return(!isempty(field), -EINVAL);
    2387           1 :         assert_return(field_is_valid(field), -EINVAL);
    2388             : 
    2389           1 :         f = strdup(field);
    2390           1 :         if (!f)
    2391           0 :                 return -ENOMEM;
    2392             : 
    2393           1 :         free(j->unique_field);
    2394           1 :         j->unique_field = f;
    2395           1 :         j->unique_file = NULL;
    2396           1 :         j->unique_offset = 0;
    2397           1 :         j->unique_file_lost = false;
    2398             : 
    2399           1 :         return 0;
    2400             : }
    2401             : 
    2402         201 : _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l) {
    2403             :         size_t k;
    2404             : 
    2405         201 :         assert_return(j, -EINVAL);
    2406         201 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2407         201 :         assert_return(data, -EINVAL);
    2408         201 :         assert_return(l, -EINVAL);
    2409         201 :         assert_return(j->unique_field, -EINVAL);
    2410             : 
    2411         201 :         k = strlen(j->unique_field);
    2412             : 
    2413         201 :         if (!j->unique_file) {
    2414           1 :                 if (j->unique_file_lost)
    2415           0 :                         return 0;
    2416             : 
    2417           1 :                 j->unique_file = ordered_hashmap_first(j->files);
    2418           1 :                 if (!j->unique_file)
    2419           0 :                         return 0;
    2420             : 
    2421           1 :                 j->unique_offset = 0;
    2422             :         }
    2423             : 
    2424             :         for (;;) {
    2425             :                 JournalFile *of;
    2426             :                 Iterator i;
    2427             :                 Object *o;
    2428             :                 const void *odata;
    2429             :                 size_t ol;
    2430             :                 bool found;
    2431             :                 int r;
    2432             : 
    2433             :                 /* Proceed to next data object in the field's linked list */
    2434         263 :                 if (j->unique_offset == 0) {
    2435           3 :                         r = journal_file_find_field_object(j->unique_file, j->unique_field, k, &o, NULL);
    2436           3 :                         if (r < 0)
    2437         201 :                                 return r;
    2438             : 
    2439           3 :                         j->unique_offset = r > 0 ? le64toh(o->field.head_data_offset) : 0;
    2440             :                 } else {
    2441         260 :                         r = journal_file_move_to_object(j->unique_file, OBJECT_DATA, j->unique_offset, &o);
    2442         260 :                         if (r < 0)
    2443           0 :                                 return r;
    2444             : 
    2445         260 :                         j->unique_offset = le64toh(o->data.next_field_offset);
    2446             :                 }
    2447             : 
    2448             :                 /* We reached the end of the list? Then start again, with the next file */
    2449         263 :                 if (j->unique_offset == 0) {
    2450           3 :                         j->unique_file = ordered_hashmap_next(j->files, j->unique_file->path);
    2451           3 :                         if (!j->unique_file)
    2452           1 :                                 return 0;
    2453             : 
    2454          64 :                         continue;
    2455             :                 }
    2456             : 
    2457             :                 /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
    2458             :                  * instead, so that we can look at this data object at the same
    2459             :                  * time as one on another file */
    2460         260 :                 r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
    2461         260 :                 if (r < 0)
    2462           0 :                         return r;
    2463             : 
    2464             :                 /* Let's do the type check by hand, since we used 0 context above. */
    2465         260 :                 if (o->object.type != OBJECT_DATA) {
    2466           0 :                         log_debug("%s:offset " OFSfmt ": object has type %d, expected %d",
    2467             :                                   j->unique_file->path, j->unique_offset,
    2468             :                                   o->object.type, OBJECT_DATA);
    2469           0 :                         return -EBADMSG;
    2470             :                 }
    2471             : 
    2472         260 :                 r = return_data(j, j->unique_file, o, &odata, &ol);
    2473         260 :                 if (r < 0)
    2474           0 :                         return r;
    2475             : 
    2476             :                 /* Check if we have at least the field name and "=". */
    2477         260 :                 if (ol <= k) {
    2478           0 :                         log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu",
    2479             :                                   j->unique_file->path, j->unique_offset,
    2480             :                                   ol, k + 1);
    2481           0 :                         return -EBADMSG;
    2482             :                 }
    2483             : 
    2484         260 :                 if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') {
    2485           0 :                         log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"",
    2486             :                                   j->unique_file->path, j->unique_offset,
    2487             :                                   j->unique_field);
    2488           0 :                         return -EBADMSG;
    2489             :                 }
    2490             : 
    2491             :                 /* OK, now let's see if we already returned this data
    2492             :                  * object by checking if it exists in the earlier
    2493             :                  * traversed files. */
    2494         260 :                 found = false;
    2495         940 :                 ORDERED_HASHMAP_FOREACH(of, j->files, i) {
    2496             :                         Object *oo;
    2497             :                         uint64_t op;
    2498             : 
    2499         680 :                         if (of == j->unique_file)
    2500         260 :                                 break;
    2501             : 
    2502             :                         /* Skip this file it didn't have any fields
    2503             :                          * indexed */
    2504         840 :                         if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
    2505         420 :                             le64toh(of->header->n_fields) <= 0)
    2506           0 :                                 continue;
    2507             : 
    2508         420 :                         r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
    2509         420 :                         if (r < 0)
    2510           0 :                                 return r;
    2511             : 
    2512         420 :                         if (r > 0)
    2513          60 :                                 found = true;
    2514             :                 }
    2515             : 
    2516         260 :                 if (found)
    2517          60 :                         continue;
    2518             : 
    2519         200 :                 r = return_data(j, j->unique_file, o, data, l);
    2520         200 :                 if (r < 0)
    2521           0 :                         return r;
    2522             : 
    2523         200 :                 return 1;
    2524          62 :         }
    2525             : }
    2526             : 
    2527           1 : _public_ void sd_journal_restart_unique(sd_journal *j) {
    2528           1 :         if (!j)
    2529           0 :                 return;
    2530             : 
    2531           1 :         j->unique_file = NULL;
    2532           1 :         j->unique_offset = 0;
    2533           1 :         j->unique_file_lost = false;
    2534             : }
    2535             : 
    2536           0 : _public_ int sd_journal_reliable_fd(sd_journal *j) {
    2537           0 :         assert_return(j, -EINVAL);
    2538           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2539             : 
    2540           0 :         return !j->on_network;
    2541             : }
    2542             : 
    2543           0 : static char *lookup_field(const char *field, void *userdata) {
    2544           0 :         sd_journal *j = userdata;
    2545             :         const void *data;
    2546             :         size_t size, d;
    2547             :         int r;
    2548             : 
    2549           0 :         assert(field);
    2550           0 :         assert(j);
    2551             : 
    2552           0 :         r = sd_journal_get_data(j, field, &data, &size);
    2553           0 :         if (r < 0 ||
    2554           0 :             size > REPLACE_VAR_MAX)
    2555           0 :                 return strdup(field);
    2556             : 
    2557           0 :         d = strlen(field) + 1;
    2558             : 
    2559           0 :         return strndup((const char*) data + d, size - d);
    2560             : }
    2561             : 
    2562           0 : _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) {
    2563             :         const void *data;
    2564             :         size_t size;
    2565             :         sd_id128_t id;
    2566           0 :         _cleanup_free_ char *text = NULL, *cid = NULL;
    2567             :         char *t;
    2568             :         int r;
    2569             : 
    2570           0 :         assert_return(j, -EINVAL);
    2571           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2572           0 :         assert_return(ret, -EINVAL);
    2573             : 
    2574           0 :         r = sd_journal_get_data(j, "MESSAGE_ID", &data, &size);
    2575           0 :         if (r < 0)
    2576           0 :                 return r;
    2577             : 
    2578           0 :         cid = strndup((const char*) data + 11, size - 11);
    2579           0 :         if (!cid)
    2580           0 :                 return -ENOMEM;
    2581             : 
    2582           0 :         r = sd_id128_from_string(cid, &id);
    2583           0 :         if (r < 0)
    2584           0 :                 return r;
    2585             : 
    2586           0 :         r = catalog_get(CATALOG_DATABASE, id, &text);
    2587           0 :         if (r < 0)
    2588           0 :                 return r;
    2589             : 
    2590           0 :         t = replace_var(text, lookup_field, j);
    2591           0 :         if (!t)
    2592           0 :                 return -ENOMEM;
    2593             : 
    2594           0 :         *ret = t;
    2595           0 :         return 0;
    2596             : }
    2597             : 
    2598           0 : _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) {
    2599           0 :         assert_return(ret, -EINVAL);
    2600             : 
    2601           0 :         return catalog_get(CATALOG_DATABASE, id, ret);
    2602             : }
    2603             : 
    2604           1 : _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) {
    2605           1 :         assert_return(j, -EINVAL);
    2606           1 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2607             : 
    2608           1 :         j->data_threshold = sz;
    2609           1 :         return 0;
    2610             : }
    2611             : 
    2612           0 : _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
    2613           0 :         assert_return(j, -EINVAL);
    2614           0 :         assert_return(!journal_pid_changed(j), -ECHILD);
    2615           0 :         assert_return(sz, -EINVAL);
    2616             : 
    2617           0 :         *sz = j->data_threshold;
    2618           0 :         return 0;
    2619             : }

Generated by: LCOV version 1.11