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