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