use libc::{c_int, size_t};
use std::ptr;
use std::collections::BTreeMap;
use ffi::journal as ffi;
use super::{Result, JournalEntry};
pub struct JournalReader {
j: *mut ffi::sd_journal,
}
pub struct JournalReaderConfig {
pub files: JournalFiles,
pub only_volatile: bool,
pub only_local: bool,
}
#[derive(Clone, Debug)]
pub enum JournalFiles {
System,
CurrentUser,
All,
}
pub enum JournalSeek {
Head,
Tail,
}
impl JournalReaderConfig {
pub fn default() -> JournalReaderConfig {
return JournalReaderConfig {
files: JournalFiles::All,
only_volatile: false,
only_local: false,
};
}
}
impl JournalReader {
pub fn open(config: &JournalReaderConfig) -> Result<JournalReader> {
let mut flags: c_int = 0;
if config.only_volatile {
flags |= ffi::SD_JOURNAL_RUNTIME_ONLY;
}
if config.only_local {
flags |= ffi::SD_JOURNAL_LOCAL_ONLY;
}
flags |= match config.files {
JournalFiles::System => ffi::SD_JOURNAL_SYSTEM,
JournalFiles::CurrentUser => ffi::SD_JOURNAL_CURRENT_USER,
JournalFiles::All => 0,
};
let mut journal = JournalReader { j: ptr::null_mut() };
sd_try!(ffi::sd_journal_open(&mut journal.j, flags));
Ok(journal)
}
fn current_entry(&mut self) -> Result<Option<JournalEntry>> {
unsafe { ffi::sd_journal_restart_data(self.j) }
let mut fields = BTreeMap::new();
let mut sz: size_t = 0;
let data: *mut u8 = ptr::null_mut();
while sd_try!(ffi::sd_journal_enumerate_data(self.j, &data, &mut sz)) > 0 {
unsafe {
let b = ::std::slice::from_raw_parts_mut(data, sz as usize);
let field = String::from_utf8_lossy(b);
let mut name_value = field.splitn(2, '=');
let name = name_value.next().unwrap();
let value = name_value.next().unwrap();
fields.insert(From::from(name), From::from(value));
}
}
let mut timestamp_realtime_us: u64 = 0;
unsafe {
ffi::sd_journal_get_realtime_usec(
self.j,
&mut timestamp_realtime_us);
}
fields.insert(
"__REALTIME_TIMESTAMP".to_string(),
timestamp_realtime_us.to_string());
let mut timestamp_monotonic_us: u64 = 0;
unsafe {
ffi::sd_journal_get_monotonic_usec(
self.j,
&mut timestamp_monotonic_us,
ptr::null());
}
fields.insert(
"__MONOTONIC_TIMESTAMP".to_string(),
timestamp_monotonic_us.to_string());
let entry = JournalEntry::from_fields(&fields);
Ok(Some(entry))
}
pub fn next_entry(&mut self) -> Result<Option<JournalEntry>> {
if sd_try!(ffi::sd_journal_next(self.j)) == 0 {
return Ok(None);
}
return self.current_entry();
}
pub fn previous_entry(&mut self) -> Result<Option<JournalEntry>> {
if sd_try!(ffi::sd_journal_previous(self.j)) == 0 {
return Ok(None);
}
return self.current_entry();
}
pub fn seek(&mut self, seek: JournalSeek) -> Result<()> {
match seek {
JournalSeek::Head => sd_try!(ffi::sd_journal_seek_head(self.j)),
JournalSeek::Tail => sd_try!(ffi::sd_journal_seek_tail(self.j)),
};
return Ok(());
}
}
impl Drop for JournalReader {
fn drop(&mut self) {
if !self.j.is_null() {
unsafe {
ffi::sd_journal_close(self.j);
}
}
}
}