Skip to main content

journal/
facade.rs

1#![allow(non_snake_case)]
2
3use crate::{
4    BootInfo, DirectoryReader, Entry, FileReader, ReaderOptions, SdkError, export_entry_bytes,
5    format_entry_text, json_entry,
6};
7use std::collections::HashSet;
8use std::fmt;
9use std::path::{Path, PathBuf};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum OutputMode {
13    Default,
14    Json,
15    Export,
16}
17
18impl Default for OutputMode {
19    fn default() -> Self {
20        Self::Default
21    }
22}
23
24#[derive(Debug, Clone)]
25pub enum Error {
26    Unsupported,
27    NoEntry,
28    InvalidCursor,
29    EndOfEntries,
30    CorruptFile,
31    Other(String),
32}
33
34impl fmt::Display for Error {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            Self::Unsupported => write!(f, "operation not supported"),
38            Self::NoEntry => write!(f, "no matching entry"),
39            Self::InvalidCursor => write!(f, "invalid cursor"),
40            Self::EndOfEntries => write!(f, "end of entries"),
41            Self::CorruptFile => write!(f, "corrupt journal file"),
42            Self::Other(msg) => write!(f, "{msg}"),
43        }
44    }
45}
46
47impl std::error::Error for Error {}
48
49pub const ERR_UNSUPPORTED: i32 = 1;
50pub const ERR_NO_ENTRY: i32 = 2;
51pub const ERR_INVALID_CURSOR: i32 = 3;
52pub const ERR_END_OF_ENTRIES: i32 = 4;
53
54enum ReaderKind {
55    File(FileReader),
56    Directory(DirectoryReader),
57}
58
59pub struct SdJournal {
60    reader: ReaderKind,
61    output_mode: OutputMode,
62    field_items: Vec<String>,
63    field_index: usize,
64    unique_field: Option<String>,
65}
66
67pub type UniqueValue = (String, Vec<u8>);
68
69pub fn SdJournalOpen(path: &str, flags: u32) -> std::result::Result<SdJournal, Error> {
70    if flags != 0 {
71        return Err(Error::Unsupported);
72    }
73
74    let path = Path::new(path);
75    let reader = if path.is_dir() {
76        ReaderKind::Directory(DirectoryReader::open(path).map_err(map_error)?)
77    } else {
78        ReaderKind::File(FileReader::open(path).map_err(map_error)?)
79    };
80
81    Ok(SdJournal::new(reader))
82}
83
84pub fn SdJournalOpenFile(path: &str, flags: u32) -> std::result::Result<SdJournal, Error> {
85    SdJournalOpenFileWithOptions(path, flags, ReaderOptions::default())
86}
87
88pub fn SdJournalOpenFileWithOptions(
89    path: &str,
90    flags: u32,
91    options: ReaderOptions,
92) -> std::result::Result<SdJournal, Error> {
93    if flags != 0 {
94        return Err(Error::Unsupported);
95    }
96    Ok(SdJournal::new(ReaderKind::File(
97        FileReader::open_with_options(path, options).map_err(map_error)?,
98    )))
99}
100
101pub fn SdJournalOpenDirectory(path: &str, flags: u32) -> std::result::Result<SdJournal, Error> {
102    SdJournalOpenDirectoryWithOptions(path, flags, ReaderOptions::default())
103}
104
105pub fn SdJournalOpenDirectoryWithOptions(
106    path: &str,
107    flags: u32,
108    options: ReaderOptions,
109) -> std::result::Result<SdJournal, Error> {
110    if flags != 0 {
111        return Err(Error::Unsupported);
112    }
113    Ok(SdJournal::new(ReaderKind::Directory(
114        DirectoryReader::open_with_options(path, options).map_err(map_error)?,
115    )))
116}
117
118pub fn SdJournalOpenFiles(paths: &[&str], flags: u32) -> std::result::Result<SdJournal, Error> {
119    SdJournalOpenFilesWithOptions(paths, flags, ReaderOptions::default())
120}
121
122pub fn SdJournalOpenFilesWithOptions(
123    paths: &[&str],
124    flags: u32,
125    options: ReaderOptions,
126) -> std::result::Result<SdJournal, Error> {
127    if flags != 0 {
128        return Err(Error::Unsupported);
129    }
130    let paths = dedupe_open_file_paths(paths);
131    if paths.len() == 1 {
132        return SdJournalOpenFileWithOptions(&paths[0], flags, options);
133    }
134    Ok(SdJournal::new(ReaderKind::Directory(
135        DirectoryReader::open_files_with_options(&paths, options).map_err(map_error)?,
136    )))
137}
138
139fn dedupe_open_file_paths(paths: &[&str]) -> Vec<String> {
140    let mut out = Vec::with_capacity(paths.len());
141    let mut seen = HashSet::with_capacity(paths.len());
142    for path in paths {
143        let key = canonical_open_file_key(Path::new(path));
144        if seen.insert(key) {
145            out.push((*path).to_string());
146        }
147    }
148    out
149}
150
151fn canonical_open_file_key(path: &Path) -> PathBuf {
152    std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
153}
154
155pub fn SdJournalClose(j: SdJournal) {
156    drop(j);
157}
158
159impl SdJournal {
160    fn new(reader: ReaderKind) -> Self {
161        Self {
162            reader,
163            output_mode: OutputMode::Default,
164            field_items: Vec::new(),
165            field_index: 0,
166            unique_field: None,
167        }
168    }
169
170    fn reset_iterators(&mut self) {
171        match &mut self.reader {
172            ReaderKind::File(reader) => {
173                reader.clear_entry_data_state();
174                reader.clear_unique_state();
175            }
176            ReaderKind::Directory(reader) => {
177                reader.clear_entry_data_state();
178                reader.clear_unique_state();
179            }
180        }
181        self.field_items.clear();
182        self.field_index = 0;
183        self.unique_field = None;
184    }
185
186    pub fn add_match(&mut self, data: &[u8]) {
187        self.reset_iterators();
188        match &mut self.reader {
189            ReaderKind::File(reader) => reader.add_match(data),
190            ReaderKind::Directory(reader) => reader.add_match(data),
191        }
192    }
193
194    pub fn add_conjunction(&mut self) -> std::result::Result<(), Error> {
195        self.reset_iterators();
196        match &mut self.reader {
197            ReaderKind::File(reader) => reader.add_conjunction(),
198            ReaderKind::Directory(reader) => reader.add_conjunction(),
199        }
200        .map_err(map_error)
201    }
202
203    pub fn add_disjunction(&mut self) -> std::result::Result<(), Error> {
204        self.reset_iterators();
205        match &mut self.reader {
206            ReaderKind::File(reader) => reader.add_disjunction(),
207            ReaderKind::Directory(reader) => reader.add_disjunction(),
208        }
209        .map_err(map_error)
210    }
211
212    pub fn flush_matches(&mut self) {
213        self.reset_iterators();
214        match &mut self.reader {
215            ReaderKind::File(reader) => reader.flush_matches(),
216            ReaderKind::Directory(reader) => reader.flush_matches(),
217        }
218    }
219
220    pub fn next(&mut self) -> std::result::Result<i32, Error> {
221        self.reset_iterators();
222        let advanced = match &mut self.reader {
223            ReaderKind::File(reader) => reader.next(),
224            ReaderKind::Directory(reader) => reader.next(),
225        }
226        .map_err(map_error)?;
227        Ok(i32::from(advanced))
228    }
229
230    pub fn previous(&mut self) -> std::result::Result<i32, Error> {
231        self.reset_iterators();
232        let advanced = match &mut self.reader {
233            ReaderKind::File(reader) => reader.previous(),
234            ReaderKind::Directory(reader) => reader.previous(),
235        }
236        .map_err(map_error)?;
237        Ok(i32::from(advanced))
238    }
239
240    pub fn seek_head(&mut self) {
241        self.reset_iterators();
242        match &mut self.reader {
243            ReaderKind::File(reader) => reader.seek_head(),
244            ReaderKind::Directory(reader) => reader.seek_head(),
245        }
246    }
247
248    pub fn seek_tail(&mut self) {
249        self.reset_iterators();
250        match &mut self.reader {
251            ReaderKind::File(reader) => reader.seek_tail(),
252            ReaderKind::Directory(reader) => reader.seek_tail(),
253        }
254    }
255
256    pub fn seek_realtime_usec(&mut self, usec: u64) {
257        self.reset_iterators();
258        match &mut self.reader {
259            ReaderKind::File(reader) => reader.seek_realtime(usec),
260            ReaderKind::Directory(reader) => reader.seek_realtime(usec),
261        }
262    }
263
264    pub fn seek_cursor(&mut self, cursor: &str) -> std::result::Result<(), Error> {
265        self.reset_iterators();
266        match &mut self.reader {
267            ReaderKind::File(reader) => reader.seek_cursor(cursor),
268            ReaderKind::Directory(reader) => reader.seek_cursor(cursor),
269        }
270        .map_err(map_error)
271    }
272
273    pub fn get_entry(&mut self) -> std::result::Result<Entry, Error> {
274        match &mut self.reader {
275            ReaderKind::File(reader) => reader.get_entry(),
276            ReaderKind::Directory(reader) => reader.get_entry(),
277        }
278        .map_err(map_error)
279    }
280
281    pub fn get_realtime_usec(&self) -> std::result::Result<u64, Error> {
282        match &self.reader {
283            ReaderKind::File(reader) => reader.get_realtime_usec(),
284            ReaderKind::Directory(reader) => reader.get_realtime_usec(),
285        }
286        .map_err(map_error)
287    }
288
289    pub fn get_cursor(&self) -> std::result::Result<String, Error> {
290        match &self.reader {
291            ReaderKind::File(reader) => reader.get_cursor(),
292            ReaderKind::Directory(reader) => reader.get_cursor(),
293        }
294        .map_err(map_error)
295    }
296
297    pub fn get_seqnum(&self) -> std::result::Result<(u64, [u8; 16]), Error> {
298        match &self.reader {
299            ReaderKind::File(reader) => reader.get_seqnum(),
300            ReaderKind::Directory(reader) => reader.get_seqnum(),
301        }
302        .map_err(map_error)
303    }
304
305    pub fn get_monotonic_usec(&self) -> std::result::Result<(u64, [u8; 16]), Error> {
306        match &self.reader {
307            ReaderKind::File(reader) => reader.get_monotonic_usec(),
308            ReaderKind::Directory(reader) => reader.get_monotonic_usec(),
309        }
310        .map_err(map_error)
311    }
312
313    pub fn test_cursor(&self, cursor: &str) -> std::result::Result<bool, Error> {
314        match &self.reader {
315            ReaderKind::File(reader) => reader.test_cursor(cursor),
316            ReaderKind::Directory(reader) => reader.test_cursor(cursor),
317        }
318        .map_err(map_error)
319    }
320
321    pub fn restart_data(&mut self) -> std::result::Result<(), Error> {
322        match &mut self.reader {
323            ReaderKind::File(reader) => reader.entry_data_restart(),
324            ReaderKind::Directory(reader) => reader.entry_data_restart(),
325        }
326        .map_err(map_error)
327    }
328
329    pub fn enumerate_available_data(&mut self) -> std::result::Result<Option<&[u8]>, Error> {
330        match &mut self.reader {
331            ReaderKind::File(reader) => reader.enumerate_entry_payload(),
332            ReaderKind::Directory(reader) => reader.enumerate_entry_payload(),
333        }
334        .map_err(map_error)
335    }
336
337    pub fn enumerate_fields(&mut self) -> std::result::Result<Vec<String>, Error> {
338        match &mut self.reader {
339            ReaderKind::File(reader) => enumerate_file_fields(reader),
340            ReaderKind::Directory(reader) => reader.enumerate_fields(),
341        }
342        .map_err(map_error)
343    }
344
345    pub fn restart_fields(&mut self) -> std::result::Result<(), Error> {
346        self.field_items = self.enumerate_fields()?;
347        self.field_index = 0;
348        Ok(())
349    }
350
351    pub fn enumerate_field(&mut self) -> std::result::Result<Option<String>, Error> {
352        if self.field_index >= self.field_items.len() {
353            return Ok(None);
354        }
355        let item = self.field_items[self.field_index].clone();
356        self.field_index += 1;
357        Ok(Some(item))
358    }
359
360    fn query_unique_values(&mut self, field: &str) -> std::result::Result<Vec<Vec<u8>>, Error> {
361        let mut values = Vec::new();
362        self.visit_unique_values(field, |value| {
363            values.push(value.to_vec());
364            Ok(())
365        })?;
366        Ok(values)
367    }
368
369    pub fn visit_unique_values<F>(
370        &mut self,
371        field: &str,
372        visitor: F,
373    ) -> std::result::Result<(), Error>
374    where
375        F: FnMut(&[u8]) -> std::result::Result<(), Error>,
376    {
377        let mut visitor = visitor;
378        let mut visitor_error = None;
379        let result = {
380            let mut sdk_visitor = |value: &[u8]| match visitor(value) {
381                Ok(()) => Ok(()),
382                Err(err) => {
383                    visitor_error = Some(err);
384                    Err(crate::SdkError::VerificationError(
385                        "unique value visitor failed".to_string(),
386                    ))
387                }
388            };
389            match &mut self.reader {
390                ReaderKind::File(reader) => reader.visit_unique_values(field, &mut sdk_visitor),
391                ReaderKind::Directory(reader) => {
392                    reader.visit_unique_values(field, &mut sdk_visitor)
393                }
394            }
395        };
396        result.map_err(|err| visitor_error.take().unwrap_or_else(|| map_error(err)))
397    }
398
399    pub fn query_unique(&mut self, field: &str) -> std::result::Result<Vec<UniqueValue>, Error> {
400        Ok(self
401            .query_unique_values(field)?
402            .into_iter()
403            .map(|value| (field.to_string(), value))
404            .collect())
405    }
406
407    pub fn query_unique_state(&mut self, field: &str) -> std::result::Result<(), Error> {
408        match &mut self.reader {
409            ReaderKind::File(reader) => reader.query_unique_state(field),
410            ReaderKind::Directory(reader) => reader.query_unique_state(field),
411        }
412        .map_err(map_error)?;
413        self.unique_field = Some(field.to_string());
414        Ok(())
415    }
416
417    pub fn restart_unique(&mut self) {
418        match &mut self.reader {
419            ReaderKind::File(reader) => reader.restart_unique_state(),
420            ReaderKind::Directory(reader) => reader.restart_unique_state(),
421        }
422    }
423
424    pub fn enumerate_available_unique(&mut self) -> std::result::Result<Option<Vec<u8>>, Error> {
425        let Some(field) = &self.unique_field else {
426            return Ok(None);
427        };
428        match &mut self.reader {
429            ReaderKind::File(reader) => reader.enumerate_unique_payload(field),
430            ReaderKind::Directory(reader) => reader.enumerate_unique_payload(),
431        }
432        .map_err(map_error)
433    }
434
435    pub fn list_boots(&self) -> Vec<BootInfo> {
436        match &self.reader {
437            ReaderKind::File(reader) => {
438                let header = reader.cached_header().header;
439                vec![BootInfo {
440                    index: 0,
441                    boot_id: hex::encode(header.tail_entry_boot_id),
442                    first_entry: header.head_entry_realtime as i64,
443                    last_entry: header.tail_entry_realtime as i64,
444                }]
445            }
446            ReaderKind::Directory(reader) => reader.list_boots(),
447        }
448    }
449
450    pub fn set_output_mode(&mut self, mode: OutputMode) {
451        self.output_mode = mode;
452    }
453
454    pub fn process_output(&self, entry: &Entry) -> std::result::Result<Vec<u8>, Error> {
455        match self.output_mode {
456            OutputMode::Default => Ok(format_entry_text(entry)),
457            OutputMode::Export => Ok(export_entry_bytes(entry)),
458            OutputMode::Json => {
459                let mut out = serde_json::to_vec(&json_entry(entry))
460                    .map_err(|err| Error::Other(err.to_string()))?;
461                out.push(b'\n');
462                Ok(out)
463            }
464        }
465    }
466}
467
468pub fn SdJournalAddMatch(j: &mut SdJournal, data: &[u8]) -> std::result::Result<(), Error> {
469    crate::parse_match_bytes(data).map_err(|_| Error::Other("EINVAL".to_string()))?;
470    j.add_match(data);
471    Ok(())
472}
473
474pub fn SdJournalAddDisjunction(j: &mut SdJournal) -> std::result::Result<(), Error> {
475    j.add_disjunction()
476}
477
478pub fn SdJournalAddConjunction(j: &mut SdJournal) -> std::result::Result<(), Error> {
479    j.add_conjunction()
480}
481
482pub fn SdJournalFlushMatches(j: &mut SdJournal) -> std::result::Result<(), Error> {
483    j.flush_matches();
484    Ok(())
485}
486
487pub fn SdJournalNext(j: &mut SdJournal) -> std::result::Result<i32, Error> {
488    j.next()
489}
490
491pub fn SdJournalNextSkip(j: &mut SdJournal, skip: u64) -> std::result::Result<i32, Error> {
492    let mut advanced = 0;
493    for _ in 0..skip {
494        if j.next()? == 0 {
495            break;
496        }
497        advanced += 1;
498    }
499    Ok(advanced)
500}
501
502pub fn SdJournalPrevious(j: &mut SdJournal) -> std::result::Result<i32, Error> {
503    j.previous()
504}
505
506pub fn SdJournalPreviousSkip(j: &mut SdJournal, skip: u64) -> std::result::Result<i32, Error> {
507    let mut advanced = 0;
508    for _ in 0..skip {
509        if j.previous()? == 0 {
510            break;
511        }
512        advanced += 1;
513    }
514    Ok(advanced)
515}
516
517pub fn SdJournalSeekHead(j: &mut SdJournal) -> std::result::Result<(), Error> {
518    j.seek_head();
519    Ok(())
520}
521
522pub fn SdJournalSeekTail(j: &mut SdJournal) -> std::result::Result<(), Error> {
523    j.seek_tail();
524    Ok(())
525}
526
527pub fn SdJournalSeekRealtimeUsec(j: &mut SdJournal, usec: u64) -> std::result::Result<(), Error> {
528    j.seek_realtime_usec(usec);
529    Ok(())
530}
531
532pub fn SdJournalSeekCursor(j: &mut SdJournal, cursor: &str) -> std::result::Result<(), Error> {
533    j.seek_cursor(cursor)
534}
535
536pub fn SdJournalGetRealtimeUsec(j: &SdJournal) -> std::result::Result<u64, Error> {
537    j.get_realtime_usec()
538}
539
540pub fn SdJournalGetSeqnum(j: &SdJournal) -> std::result::Result<(u64, [u8; 16]), Error> {
541    j.get_seqnum()
542}
543
544pub fn SdJournalGetMonotonicUsec(j: &SdJournal) -> std::result::Result<(u64, [u8; 16]), Error> {
545    j.get_monotonic_usec()
546}
547
548pub fn SdJournalGetCursor(j: &SdJournal) -> std::result::Result<String, Error> {
549    j.get_cursor()
550}
551
552pub fn SdJournalTestCursor(j: &SdJournal, cursor: &str) -> std::result::Result<bool, Error> {
553    j.test_cursor(cursor)
554}
555
556pub fn SdJournalGetEntry(j: &mut SdJournal) -> std::result::Result<Entry, Error> {
557    j.get_entry()
558}
559
560pub fn SdJournalGetData(j: &mut SdJournal, field: &str) -> std::result::Result<Vec<u8>, Error> {
561    let found = match &mut j.reader {
562        ReaderKind::File(reader) => reader.get_entry_payload(field.as_bytes()),
563        ReaderKind::Directory(reader) => reader.get_entry_payload(field.as_bytes()),
564    }
565    .map_err(map_error)?;
566    found.ok_or(Error::NoEntry)
567}
568
569pub fn SdJournalRestartData(j: &mut SdJournal) -> std::result::Result<(), Error> {
570    j.restart_data()
571}
572
573pub fn SdJournalEnumerateAvailableData(
574    j: &mut SdJournal,
575) -> std::result::Result<Option<&[u8]>, Error> {
576    j.enumerate_available_data()
577}
578
579pub fn SdJournalEnumerateFields(j: &mut SdJournal) -> std::result::Result<Vec<String>, Error> {
580    j.enumerate_fields()
581}
582
583pub fn SdJournalRestartFields(j: &mut SdJournal) -> std::result::Result<(), Error> {
584    j.restart_fields()
585}
586
587pub fn SdJournalEnumerateField(j: &mut SdJournal) -> std::result::Result<Option<String>, Error> {
588    j.enumerate_field()
589}
590
591pub fn SdJournalListBoots(j: &mut SdJournal) -> std::result::Result<Vec<BootInfo>, Error> {
592    Ok(j.list_boots())
593}
594
595pub fn SdJournalQueryUnique(
596    j: &mut SdJournal,
597    field: &str,
598) -> std::result::Result<Vec<UniqueValue>, Error> {
599    j.query_unique(field)
600}
601
602pub fn SdJournalVisitUniqueValues<F>(
603    j: &mut SdJournal,
604    field: &str,
605    visitor: F,
606) -> std::result::Result<(), Error>
607where
608    F: FnMut(&[u8]) -> std::result::Result<(), Error>,
609{
610    j.visit_unique_values(field, visitor)
611}
612
613pub fn SdJournalQueryUniqueState(j: &mut SdJournal, field: &str) -> std::result::Result<(), Error> {
614    j.query_unique_state(field)
615}
616
617pub fn SdJournalRestartUnique(j: &mut SdJournal) -> std::result::Result<(), Error> {
618    j.restart_unique();
619    Ok(())
620}
621
622pub fn SdJournalEnumerateAvailableUnique(
623    j: &mut SdJournal,
624) -> std::result::Result<Option<Vec<u8>>, Error> {
625    j.enumerate_available_unique()
626}
627
628pub fn SdJournalSetOutputMode(j: &mut SdJournal, mode: OutputMode) {
629    j.set_output_mode(mode);
630}
631
632pub fn SdJournalProcessOutput(j: &SdJournal, entry: &Entry) -> std::result::Result<Vec<u8>, Error> {
633    j.process_output(entry)
634}
635
636fn enumerate_file_fields(reader: &mut FileReader) -> crate::Result<Vec<String>> {
637    reader.enumerate_fields()
638}
639
640fn map_error(err: SdkError) -> Error {
641    match err {
642        SdkError::NoEntry => Error::NoEntry,
643        SdkError::InvalidCursor(_) => Error::InvalidCursor,
644        SdkError::Unsupported(_) => Error::Unsupported,
645        SdkError::DecompressionFailed(msg) => Error::Other(msg),
646        SdkError::InvalidPath(msg) => Error::Other(msg),
647        SdkError::Journal(err) => Error::Other(err.to_string()),
648        SdkError::VerificationError(msg) => {
649            Error::Other(format!("journal verification failed: corrupt file: {msg}"))
650        }
651    }
652}