sd_journal/
lib.rs

1// sd-journal: rust wrapper on sd-journal implemented in libsystemd
2// Copyright (C) 2020 Christian Klaue ente@ck76.de
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! sd-journal is a rust wrapper for sd-journal in the systemd API of [libsystemd](https://www.freedesktop.org/software/systemd/man/sd-id128.html).  sd-journal is part of the [systemd.rs](https://gitlab.com/systemd.rs) project.
18//!
19//! gitlab.com | crates.io | docs.rs
20//! -----------|-----------|--------
21//! [sd-sys](https://gitlab.com/systemd.rs/sd-sys) | [![Crates.io](https://img.shields.io/crates/v/sd-sys)](https://crates.io/crates/sd-sys) | [![docs.rs](https://docs.rs/sd-sys/badge.svg)](https://docs.rs/sd-sys/)
22//! [sd-id128](https://gitlab.com/systemd.rs/sd-id128) | [![Crates.io](https://img.shields.io/crates/v/sd-id128)](https://crates.io/crates/sd-id128) | [![docs.rs](https://docs.rs/sd-id128/badge.svg)](https://docs.rs/sd-id128/)
23//! [sd-journal](https://gitlab.com/systemd.rs/sd-journal) | [![Crates.io](https://img.shields.io/crates/v/sd-journal)](https://crates.io/crates/sd-journal) | [![docs.rs](https://docs.rs/sd-journal/badge.svg)](https://docs.rs/sd-journal)
24//!
25//! systemd.rs is an alternative to the [systemd-rust](https://github.com/jmesmon/rust-systemd) project.
26//!
27//! - systemd.rs is published under the AGPL-3.0 license. Individual/commercial
28//!   licenses are available upon request.
29//! - focused coverage of sd-id128 & sd-journal only (currently there are no
30//!   plans to extend this coverage)
31//! - good documentation with links to the libsystemd documentation
32//! - 100% coverage of libsystemd within the area of focus
33//! - good test coverage
34//! - focus on usability
35//!
36//! ## Structure
37//!
38//! libsystemd is developed in C around a single struct "journal" with no
39//! differentiation whether a function refers to the journal in total or whether
40//! the fuction relates to a single record within the journal. This library also
41//! offers all the wrapped functions on the main struct `Journal`. Additionally
42//! two iterators are implemented for Journal: `CursorIterator` and
43//! `CursorReverseIterator` which both return a `Result<Cursor, Error>`. All
44//! methods implemented on Cursor do call a method implemented on Journal. For
45//! that reason, documentation of Cursor is always referring back to the
46//! documentation for Journal. libsystemd implements some additional
47//! enumerations. For each of those, an iterator has been implemented as well.
48//!
49//! ## Status & Stability
50//!
51//! This library is still under development. There are various methods marked
52//! with the feature "experimental". These methods are not considered finalized
53//! yet. The documentation of each of these methods contains further
54//! information. Additionally the library structure is currently under
55//! investigation. Currently all methods are implemented for struct Journal.
56//! This may change soon: methods that refer to a single record may be moved to
57//! struct Cursor and methods performing cursor movements (next(), previous()
58//! and similar ones) will return a Cursor.
59//!
60//! ### Planned Development
61//!
62//! - [ ] further rustification
63//!   - [ ] remove Cursor methods from Journal
64//!   - [ ] CursorMovement return Cursor instead of just a Done
65//! - [ ] additional trait implementation
66//! - [ ] Logger implementation
67//! - [ ] encoding support
68//!
69//! ### Encoding
70//!
71//! Journald stores data as "FIELDNAME=field value". While field names are
72//! strict UTF-8 encoded and field value are usually encoded in UTF-8, field
73//! values may as well be in any encoding including binary data.
74//! This library allows logging to the journal in any encoding although using
75//! UTF-8 only is highly recommended. While reading from the journal this
76//! library will strictly raise an error whenever non-UTF-8 data is encountered.
77//! In future releases decoding support and a lossy decoding may be added.
78//!
79//! ## Examples
80//!
81//! ### cargo.toml
82//!
83//! ```toml
84//! [dependencies]
85//! sdJournal = "0.1"
86//! ```
87//!
88//! ### Logging
89//!
90//! ```rust
91//! use sd_journal::*;
92//! Journal::log_message(Level::Info, "Hello World!").unwrap();
93//! Journal::log_raw_record(&["MESSAGE=Hello World!",
94//!                           &format!("PRIORITY={}", Level::Info),
95//!                           &format!("CODE_FILE={}", file!()),
96//!                           &format!("CODE_LINE={}", line!()),
97//!                           "CUSTOM_FIELD=42"]).unwrap();
98//! ```
99//!
100//! ### Read Access
101//!
102//! ```rust
103//! use sd_journal::*;
104//! use std::path::PathBuf;
105//!
106//! // load local test data
107//! let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
108//! test_data.push("test-data/");
109//! println!("looking for test data in folder {}", test_data.display());
110//! let journal =
111//!     Journal::open_directory(&test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
112//!
113//! // loop over journal records
114//! while let Ok(CursorMovement::Done) = journal.next() {
115//!     // do something on each cursor, e.g. print the MESSAGE
116//!     println!("{}", journal.get_data("MESSAGE").unwrap());
117//! }
118//! ```
119//!
120//! ## License
121//!
122//! sd-journal: a wrapper for sd-journal of libsystemd
123//!
124//! Copyright (C) 2020 Christian Klaue [mail@ck76.de]
125//!
126//! This program is free software: you can redistribute it and/or modify
127//! it under the terms of the GNU Affero General Public License as published by
128//! the Free Software Foundation, either version 3 of the License, or
129//! (at your option) any later version.
130//!
131//! This program is distributed in the hope that it will be useful,
132//! but WITHOUT ANY WARRANTY; without even the implied warranty of
133//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
134//! GNU Affero General Public License for more details.
135//!
136//! You should have received a copy of the GNU Affero General Public License
137//! along with this program.  If not, see <https://www.gnu.org/licenses/>.
138//!
139//! Individual licenses may be granted upon request.
140mod enums;
141pub mod iterators;
142
143use chrono::{Duration, NaiveDateTime};
144pub use enums::{CursorMovement, Enumeration, Error, Event, FileFlags, Level, NamespaceFlags,
145                PathFlags, UserFlags};
146use iterators::{CursorIterator, CursorReverseIterator, FieldNames, Fields, UniqueValues};
147use libc::{c_char, c_int, c_uchar, c_void, iovec, size_t};
148use sd_id128::ID128;
149use sd_sys::journal as ffi;
150use std::{ffi::{CStr, CString},
151          fmt::Debug,
152          os::unix::io::RawFd,
153          path::PathBuf,
154          ptr};
155
156/// A wrapper for sd-journal as offered by libsystemd based on FFI bindings
157/// offered in crate [sd-sys](https://gitlab.com/systemd.rs/sd-sys).
158///
159/// Journal is a fully implemented, wrapper for submitting and querying log
160/// entries from the systemd journal.
161#[derive(Debug)]
162pub struct Journal {
163    ffi: *mut ffi::sd_journal
164}
165
166/// A journal entry record
167#[derive(Debug)]
168pub struct Cursor<'a> {
169    pub(crate) journal: &'a Journal
170}
171
172impl Journal {
173    /// Submits a simple, plain text log message with a chosen syslog level to
174    /// the journal (implements
175    /// [`sd_journal_print()`](<https://www.freedesktop.org/software/systemd/man/sd_journal_print.html#>)).
176    ///
177    /// The message submitted to `log_message()` may be anything that can be
178    /// turned into a vector of bytes. Journald considers non-UTF-8 values as
179    /// valid message although 0-bytes within the message cause an error.
180    ///
181    /// # Examples
182    /// ```
183    /// use sd_journal::*;
184    /// use std::ffi::CString;
185    /// // the following lines are all synonyms
186    /// Journal::log_message(Level::Info, "Hello World!").unwrap();
187    /// Journal::log_message(Level::Info, String::from("Hello World!").as_str()).unwrap();
188    /// Journal::log_message(Level::Info, String::from("Hello World!")).unwrap();
189    /// Journal::log_message(Level::Info, CString::new("Hello World!").unwrap()).unwrap();
190    /// ```
191    ///
192    /// # Return Values
193    /// - Ok(): success
194    /// - Err(Error::SDError): sd-journal returned an error code
195    /// - Err(Error::NullError): the message contained a 0-byte
196    pub fn log_message<T: Into<Vec<u8>>>(level: Level, message: T) -> Result<(), Error> {
197        let c_message = CString::new(message).map_err(Error::NullError)?;
198        let result = unsafe { ffi::sd_journal_print(level as c_int, c_message.as_ptr()) };
199        if result < 0 {
200            return Err(Error::SDError(result));
201        }
202        Ok(())
203    }
204
205    /// Send a raw log record to the journal (implements
206    /// [`sd_journal_sendv()`](<https://www.freedesktop.org/software/systemd/man/sd_journal_print.html#>))
207    ///
208    /// This method may be used to submit structured log entries to the system
209    /// journal. It takes any slice of byte-slices (e.g. &[String] or &[&str]).
210    ///
211    /// For libsystemd a single log record consists of multiple tuples each in
212    /// the format "FIELDNAME=fieldvalue". The field name must be in uppercase
213    /// and consist only of characters, numbers and underscores, and may not
214    /// begin with an underscore. All assignments that do not follow this
215    /// syntax will silently be ignored. A variable may be assigned more than
216    /// one value per entry. Well known field names are defined in enum
217    /// [`Field`](Field) or may be [looked up](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html#).
218    ///
219    /// The value can be of any size and format, i.e. the data encoding may
220    /// differ from UTF8 and may contain binary coding.
221    ///
222    /// # Examples
223    /// ```
224    /// use sd_journal::*;
225    /// // the first two lines are synonyms
226    /// Journal::log_message(Level::Info, "Hello World!").unwrap();
227    /// Journal::log_raw_record(&["PRIORITY=6", "MESSAGE=Hello World!"]).unwrap();
228    /// // data: &Vec<String>
229    /// Journal::log_raw_record(&vec![format!("PRIORITY={}", Level::Info),
230    ///                               "MESSAGE=Hello World!".to_string(),
231    ///                               format!("CODE_LINE={}", line!()),
232    ///                               format!("CODE_FILE={}", file!()),
233    ///                               "CUSTOM_FIELD=42".to_string()]).unwrap();
234    /// // data: &[&str]
235    /// Journal::log_raw_record(&["MESSAGE=Hello World!",
236    ///                           &format!("PRIORITY={}", Level::Info),
237    ///                           &format!("CODE_FILE={}", file!()),
238    ///                           &format!("CODE_LINE={}", line!()),
239    ///                           "CUSTOM_FIELD=42"]).unwrap();
240    /// ```
241    ///
242    /// # Return Values
243    /// - Ok(): success
244    /// - Err(Error::SDError): sd-journal returned an error code
245    pub fn log_raw_record<T: AsRef<[u8]>>(data: &[T]) -> Result<(), Error> {
246        let mut iovec_vec: Vec<iovec> = Vec::new();
247        for field in data {
248            let field = field.as_ref();
249            iovec_vec.push(iovec { iov_base: field.as_ptr() as *mut c_void,
250                                   iov_len:  field.len() });
251        }
252        let result = unsafe { ffi::sd_journal_sendv(iovec_vec.as_ptr(), iovec_vec.len() as c_int) };
253        if result < 0 {
254            return Err(Error::SDError(result));
255        }
256        Ok(())
257    }
258
259    /// Determine the message cataloge entry for a message id (implements
260    /// [`sd_journal_get_catalog_for_message_id()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_catalog.html#)).
261    ///
262    /// # Return Values
263    /// - Ok(String): message catalogue
264    /// - Err(Error::UTF8Error): UTF-8 decoding error occured
265    /// - Err(Error::SDError): sd-journal returned an error code
266    pub fn get_catalog_for_message_id(id: ID128) -> Result<String, Error> {
267        let mut data: *mut c_char = ptr::null_mut();
268        let result =
269            unsafe { ffi::sd_journal_get_catalog_for_message_id(id.into_ffi(), &mut data) };
270        if result < 0 {
271            return Err(Error::SDError(result));
272        }
273        let catalog = unsafe { CStr::from_ptr(data) };
274        let catalog = match catalog.to_str() {
275            Err(error) => {
276                unsafe { libc::free(data as *mut c_void) };
277                Err(Error::UTF8Error(error))?
278            },
279            Ok(value) => value.to_owned()
280        };
281        unsafe { libc::free(data as *mut c_void) };
282        Ok(catalog)
283    }
284
285    /// Open a journal for read access (implements
286    /// [`sd_journal_open()`](https://www.freedesktop.org/software/systemd/man/sd_journal_open.html#)).
287    ///
288    /// Opens the log journal for reading. It will find all journal files
289    /// and interleave them automatically when reading.
290    ///
291    /// # Examples
292    /// ```
293    /// use sd_journal::*;
294    /// Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
295    /// Journal::open(FileFlags::LocalOnly, UserFlags::CurrentUserOnly).unwrap();
296    ///  ```
297    ///
298    /// # Return values
299    /// - Ok(Journal): initialized journal
300    /// - Err(Error::SDError): sd-journal returned an error code
301    pub fn open(file_flags: FileFlags, user_flags: UserFlags) -> Result<Journal, Error> {
302        let mut pointer = ptr::null_mut() as *mut sd_sys::journal::sd_journal;
303        let flags = file_flags as c_int | user_flags as c_int;
304        let result = unsafe { ffi::sd_journal_open(&mut pointer, flags) };
305        if result < 0 {
306            return Err(Error::SDError(result));
307        }
308        Ok(Journal { ffi: pointer })
309    }
310
311    /// Open the journal for reading records in a specific namespace (implements
312    /// [`sd_journal_open_namespace()`](https://www.freedesktop.org/software/systemd/man/sd_journal_open.html#)).
313    ///
314    /// Opens the log journal for reading on a selected
315    /// [namespace](https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html#Journal%20Namespaces)
316    /// only. It will find all journal files and interleave them automatically
317    /// when reading. This method does not support the
318    /// `SD_JOURNAL_ALL_NAMESPACES` flag. If you want to open all namespaces,
319    /// see `open_all_namespaces()`.
320    ///
321    /// # Return values
322    /// - Ok(Journal): initialized journal
323    /// - Err(Error::SDError): sd-journal returned an error code
324    /// - Err(Error::NullError): the namespace contained a 0-byte
325    pub fn open_namespace<T: Into<Vec<u8>>>(namespace: T,
326                                            namespace_flags: NamespaceFlags,
327                                            file_flags: FileFlags,
328                                            user_flags: UserFlags)
329                                            -> Result<Journal, Error> {
330        let c_namespace = CString::new(namespace).map_err(Error::NullError)?;
331        let mut pointer = ptr::null_mut() as *mut ffi::sd_journal;
332        let flags = file_flags as c_int | user_flags as c_int | namespace_flags as c_int;
333        let result =
334            unsafe { ffi::sd_journal_open_namespace(&mut pointer, c_namespace.as_ptr(), flags) };
335        if result < 0 {
336            return Err(Error::SDError(result));
337        }
338        let journal = Journal { ffi: pointer };
339        Ok(journal)
340    }
341
342    /// Open the journal for read access including all available namespaces
343    /// (implements
344    /// [`sd_journal_open_namespace()`](https://www.freedesktop.org/software/systemd/man/sd_journal_open.html#)
345    /// with flag `SD_JOURNAL_ALL_NAMESPACES` set).
346    ///
347    /// Opens the log journal for reading for all
348    /// [namespaces](https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html#Journal%20Namespaces).
349    /// It will find all journal files automatically and interleave
350    /// them automatically when reading.
351    ///
352    /// # Return values
353    /// - Ok(Journal): initialized journal
354    /// - Err(Error::SDError): sd-journal returned an error code
355    pub fn open_all_namespaces(file_flags: FileFlags,
356                               user_flags: UserFlags)
357                               -> Result<Journal, Error> {
358        let mut pointer = ptr::null_mut() as *mut ffi::sd_journal;
359        let flags = file_flags as c_int | user_flags as c_int | ffi::SD_JOURNAL_ALL_NAMESPACES;
360        let result =
361            unsafe { ffi::sd_journal_open_namespace(&mut pointer, std::ptr::null(), flags) };
362        if result < 0 {
363            return Err(Error::SDError(result));
364        }
365        let journal = Journal { ffi: pointer };
366        Ok(journal)
367    }
368
369    /// Open the journal located at a specific path (implements
370    /// [`sd_journal_open_directory()`](https://www.freedesktop.org/software/systemd/man/sd_journal_open.html#)).
371    ///
372    /// Open the journal located at a specific path: takes an *absolute*
373    /// directory path as argument. All journal files in this directory
374    /// will be opened and interleaved automatically.
375    ///
376    /// # Examples
377    /// ```
378    /// use sd_journal::*;
379    /// use std::path::{Path, PathBuf};
380    /// // open the system journal by pointing to root with path flags set to
381    /// // PathToOSRoot
382    /// Journal::open_directory("/", PathFlags::PathToOSRoot, UserFlags::AllUsers).unwrap();
383    /// Journal::open_directory(Path::new("/"), PathFlags::PathToOSRoot, UserFlags::AllUsers).unwrap();
384    /// Journal::open_directory(PathBuf::from("/"),
385    ///                         PathFlags::PathToOSRoot,
386    ///                         UserFlags::AllUsers).unwrap();
387    /// // open test data included in a project located in a folder "test-data" in the
388    /// // project root
389    /// let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
390    /// test_data.push("test-data/");
391    /// println!("looking for test data in folder {}", test_data.display());
392    /// Journal::open_directory(test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
393    /// ```
394    /// # Return values
395    /// - Ok(Journal): initialized journal
396    /// - Err(Error::SDError): sd-journal returned an error code
397    /// - Err(Error::NullError): the path contains a 0-byte
398    pub fn open_directory<P: Into<PathBuf>>(path: P,
399                                            path_flags: PathFlags,
400                                            user_flags: UserFlags)
401                                            -> Result<Journal, Error> {
402        #[cfg(unix)]
403        use std::os::unix::ffi::OsStringExt;
404        let c_path =
405            CString::new(path.into().into_os_string().into_vec()).map_err(Error::NullError)?;
406        let mut pointer = ptr::null_mut() as *mut ffi::sd_journal;
407        let flags = path_flags as c_int | user_flags as c_int;
408        let result =
409            unsafe { ffi::sd_journal_open_directory(&mut pointer, c_path.as_ptr(), flags) };
410        if result < 0 {
411            return Err(Error::SDError(result));
412        }
413        let journal = Journal { ffi: pointer };
414        Ok(journal)
415    }
416
417    /// Open the journal stored in a list of files (implements
418    /// [`sd_journal_open_files()`](https://www.freedesktop.org/software/systemd/man/sd_journal_open.html#)).
419    ///
420    /// # Examples
421    /// ```
422    /// use sd_journal::*;
423    /// use std::path::PathBuf;
424    /// // to open the curreńt system.journal file in the default location for
425    /// // journals: /var/log/journal/<MACHINE-ID>/system.journal
426    /// let machine_id = sd_id128::ID128::machine_id().unwrap()
427    ///                                               .to_string_sd()
428    ///                                               .unwrap();
429    /// let mut sdjournal_path = PathBuf::from("/var/log/journal/");
430    /// sdjournal_path.push(&machine_id);
431    /// sdjournal_path.push("system.journal");
432    /// println!("looking for sd-journal in {}", sdjournal_path.display());
433    /// Journal::open_files([sdjournal_path]).unwrap();
434    /// // to open test data included in a project located in a folder
435    /// // "test-data" in the project root
436    /// let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
437    /// test_data.push("test-data/test.journal");
438    /// println!("looking for test data in folder {}", test_data.display());
439    /// Journal::open_files([test_data]).unwrap();
440    /// ```
441    ///
442    /// # Return values
443    /// - Ok(Journal): initialized journal
444    /// - Err(Error::SDError): sd-journal returned an error code
445    /// - Err(Error::NullError): a file path contains a 0-byte
446    pub fn open_files<A: Into<Vec<P>>, P: Into<PathBuf>>(files: A) -> Result<Journal, Error> {
447        #[cfg(unix)]
448        use std::os::unix::ffi::OsStringExt;
449        let files: Vec<P> = files.into();
450        let mut c_files_vec: Vec<CString> = Vec::with_capacity(files.len());
451        // convert Vec<PathBuf> to Vec<CString>
452        for file in files {
453            let pb_file: PathBuf = file.into();
454            let os_file = pb_file.into_os_string();
455            c_files_vec.push(CString::new(os_file.into_vec()).map_err(Error::NullError)?);
456        }
457        // convert Vec<CString> to Vec<*const c_char>
458        let mut ptr_vec: Vec<*const c_char> =
459            c_files_vec.iter().map(|file| file.as_ptr()).collect();
460        ptr_vec.push(0 as *const c_char);
461        let mut pointer = std::ptr::null_mut() as *mut ffi::sd_journal;
462        let flags: c_int = 0;
463        let result = unsafe { ffi::sd_journal_open_files(&mut pointer, ptr_vec.as_ptr(), flags) };
464        if result < 0 {
465            return Err(Error::SDError(result));
466        }
467        let journal = Journal { ffi: pointer };
468        Ok(journal)
469    }
470
471    /// Advance the read pointer of the journal by one entry (implements
472    /// [`sd_journal_next()`](https://www.freedesktop.org/software/systemd/man/sd_journal_next.html#)).
473    ///
474    /// This method wraps the sd-journal native function. There is also a
475    /// rustified iterator [`CursorIterator`](CursorIterator) avalaible via
476    /// the `iter()` method or the `IntoIterator` trait implemented for
477    /// `&Journal`.
478    /// Although the official documentation doesn't mention any error handling,
479    /// libsystemd may return an error on performing next().
480    ///
481    /// # Examples
482    /// ```
483    /// # use sd_journal::*;
484    /// # use std::path::PathBuf;
485    /// # let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
486    /// # test_data.push("test-data/");
487    /// # println!("looking for test data in folder {}", test_data.display());
488    /// # let journal = Journal::open_directory(&test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
489    /// // loop over a journal & print it's messages
490    /// while let Ok(CursorMovement::Done) = journal.next() {
491    ///     // do something on each cursor, e.g. print the MESSAGE
492    ///     println!("{}", journal.get_data("MESSAGE").unwrap());
493    /// }
494    /// ```
495    ///
496    /// # Return values
497    /// - Ok(CursorMovement::Done): full success
498    /// - Ok(CursorMovement::EoF): no movement was executed, since the cursor is
499    ///   already placed at EoF.
500    /// - Err(Error::SDError): sd-journal returned an error code
501    pub fn next(&self) -> Result<CursorMovement, Error> {
502        let result = unsafe { ffi::sd_journal_next(self.ffi) };
503        if result < 0 {
504            return Err(Error::SDError(result));
505        }
506        if result == 0 {
507            return Ok(CursorMovement::EoF);
508        }
509        Ok(CursorMovement::Done)
510    }
511
512    /// Returns an iterator on the journal.
513    ///
514    /// [CursorIterator](CursorIterator) is the rustified version of the
515    /// `next()` method. Since `next()` may fail, the advanced cursor may be
516    /// invalid. For such reason, the iterator returns a Result<Cursor, _> on
517    /// each `next()` and thus the cursor must be unwrapped first.
518    ///
519    /// # Examples
520    /// ```
521    /// # use sd_journal::*;
522    /// # use std::path::PathBuf;
523    /// # let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
524    /// # test_data.push("test-data/");
525    /// # println!("looking for test data in folder {}", test_data.display());
526    /// # let journal = Journal::open_directory(&test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
527    /// // loop over a journal & print it's messages
528    /// for cursor in journal.iter() {
529    ///     match cursor {
530    ///         Err(_) => break,
531    ///         Ok(cursor) => println!("{}", cursor.get_data("MESSAGE").unwrap())
532    ///     }
533    /// }
534    /// // ...
535    /// # journal.seek_head().unwrap();
536    /// let cursor = journal.iter_reverse().next().unwrap().unwrap();
537    /// // the following two lines are actually return the same value
538    /// let m1 = cursor.get_data("MESSAGE").unwrap();
539    /// let m2 = journal.get_data("MESSAGE").unwrap();
540    /// assert_eq!(m1, m2);
541    /// ```
542    pub fn iter(&self) -> CursorIterator {
543        CursorIterator { journal: &self }
544    }
545
546    /// Set back the read pointer of the journal by one entry (implements
547    /// [`sd_journal_previous()`](https://www.freedesktop.org/software/systemd/man/sd_journal_next.html#)).
548    ///
549    /// This method wraps the sd-journal native function. There is also a
550    /// rustified iterator [`CursorReverseIterator`](CursorReverseIterator)
551    /// avalaible via the [`iter_reverse()`](Journal::iter_reverse) method.
552    ///
553    /// # Examples
554    /// ```
555    /// # use sd_journal::*;
556    /// # use std::path::PathBuf;
557    /// # let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
558    /// # test_data.push("test-data/");
559    /// # println!("looking for test data in folder {}", test_data.display());
560    /// # let journal = Journal::open_directory(&test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
561    /// journal.seek_tail().unwrap();
562    /// // loop over a journal in reverse order & print it's messages
563    /// while let Ok(CursorMovement::Done) = journal.previous() {
564    ///     // do something on each cursor, e.g. print the MESSAGE
565    ///     println!("{}", journal.get_data("MESSAGE").unwrap());
566    /// }
567    /// ```
568    ///
569    /// # Return values
570    /// - Ok(CursorMovement::Done): full success
571    /// - Ok(CursorMovement::EoF): no movement was executed, since the cursor is
572    ///   already placed at EoF.
573    /// - Err(Error::SDError): sd-journal returned an error code
574    pub fn previous(&self) -> Result<CursorMovement, Error> {
575        let result = unsafe { ffi::sd_journal_previous(self.ffi) };
576        if result < 0 {
577            return Err(Error::SDError(result));
578        }
579        if result == 0 {
580            return Ok(CursorMovement::EoF);
581        }
582        Ok(CursorMovement::Done)
583    }
584
585    /// Returns an iterator on the journal that runs in reverse order.
586    ///
587    /// [CursorReverseIterator](CursorReverseIterator) is the rustified version
588    /// of the `previous()` method. Since `previous()` may fail, the advanced
589    /// cursor may be invalid. For such reason, the iterator returns a
590    /// Result<Cursor, _> on each `next()` and thus the cursor must be
591    /// unwrapped first.
592    ///
593    /// # Examples
594    /// ```
595    /// # use sd_journal::*;
596    /// # use std::path::PathBuf;
597    /// # let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
598    /// # test_data.push("test-data/");
599    /// # println!("looking for test data in folder {}", test_data.display());
600    /// # let journal = Journal::open_directory(&test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
601    /// journal.seek_tail().unwrap();
602    /// // loop over a journal & print it's messages
603    /// for cursor in journal.iter_reverse() {
604    ///     match cursor {
605    ///         Err(_) => break,
606    ///         Ok(cursor) => println!("{}", cursor.get_data("MESSAGE").unwrap())
607    ///     }
608    /// }
609    /// // ...
610    /// # journal.seek_tail().unwrap();
611    /// let cursor = journal.iter_reverse().next().unwrap().unwrap();
612    /// // the following two lines are actually return the same value
613    /// let m1 = cursor.get_data("MESSAGE").unwrap();
614    /// let m2 = journal.get_data("MESSAGE").unwrap();
615    /// assert_eq!(m1, m2);
616    /// ```
617    pub fn iter_reverse(&self) -> CursorReverseIterator {
618        CursorReverseIterator { journal: &self }
619    }
620
621    /// Advance the read pointer of the journal by multiple entries (implements
622    /// [`sd_journal_next_skip()`](https://www.freedesktop.org/software/systemd/man/sd_journal_next.html#)).
623    ///
624    /// # Return values
625    /// - Ok(CursorMovement::Done): full success
626    /// - Ok(CursorMovement::Limited(actual)): the movement was executed but
627    ///   limited by the EoF of the journal. The actual movement is given in the
628    ///   parameter.
629    /// - Ok(CursorMovement::EoF): no movement was executed, since the cursor is
630    ///   already placed at EoF.
631    /// - Err(Error::SDError): sd-journal returned an error code
632    pub fn next_skip(&self, skip: c_int) -> Result<CursorMovement, Error> {
633        if skip < 0 {
634            return Err(Error::RangeError);
635        }
636        let result = unsafe { ffi::sd_journal_next_skip(self.ffi, skip as u64) };
637        if result < 0 {
638            return Err(Error::SDError(result));
639        }
640        if result == 0 {
641            return Ok(CursorMovement::EoF);
642        }
643        if result < skip {
644            return Ok(CursorMovement::Limited(result));
645        }
646        Ok(CursorMovement::Done)
647    }
648
649    /// Set back the read pointer by multiple entries at once (implements
650    /// [`sd_journal_previous_skip()`](https://www.freedesktop.org/software/systemd/man/sd_journal_next.html#)).
651    ///
652    /// - Ok(CursorMovement::Done): full success
653    /// - Ok(CursorMovement::Limited(actual)): the movement was executed but
654    ///   limited by the EoF of the journal. The actual movement is given in the
655    ///   parameter.
656    /// - Ok(CursorMovement::EoF): no movement was executed, since the cursor is
657    ///   already placed at EoF.
658    /// - Err(Error::SDError): sd-journal returned an error code
659    pub fn previous_skip(&self, skip: c_int) -> Result<CursorMovement, Error> {
660        if skip < 0 {
661            return Err(Error::RangeError);
662        }
663        let result = unsafe { ffi::sd_journal_previous_skip(self.ffi, skip as u64) };
664        if result < 0 {
665            return Err(Error::SDError(result));
666        }
667        if result == 0 {
668            return Ok(CursorMovement::EoF);
669        }
670        if result < skip {
671            return Ok(CursorMovement::Limited(result));
672        }
673        Ok(CursorMovement::Done)
674    }
675
676    /// **UNSTABLE API** Seek to the head of the journal (implements
677    /// [`sd_journal_seek_head`](https://www.freedesktop.org/software/systemd/man/sd_journal_seek_head.html#)).
678    ///
679    /// Seek to the beginning of the journal, i.e. to the position **before**
680    /// the oldest available entry. Be aware that after a seek_head() the
681    /// journal cursor does not point to a valid entry. One must perform a
682    /// cursor movement before being able to retrieve data.
683    ///
684    /// # Examples
685    /// ```
686    /// # use sd_journal::*;
687    /// # let journal = Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
688    /// journal.seek_head().unwrap();
689    /// // seek_head() should be followed by a next() before any previous() --> issues
690    /// journal.next().unwrap();
691    /// // previous() should hit EoF
692    /// assert_eq!(journal.previous(), Ok(CursorMovement::EoF));
693    /// ```
694    ///
695    /// # libsystemd Issues
696    /// While `seek_head()` is supposed to move the cursor before the first
697    /// available journal entry, libsystemd may still successfully perform
698    /// `previous()` cursor movements for multiple times. All these unexpected
699    /// entries will report a full set of data and may appear fully valid
700    /// although be assured they are not. An
701    /// [error](https://github.com/systemd/systemd/issues/17662) has been
702    /// reported to the systemd project. The issue can be avoided if a `next()`
703    /// operation is executed immediately after `seek_head()` before issuing any
704    /// `previous()`. If done, `previous()`thereafter will correctly report EoF.
705    ///
706    /// # Stability
707    /// Due to the issue described above, this method may be changed in future
708    /// to include a `next()` in future releases and thus position the cursor
709    /// rather **on** the first available entry rather than positioning the
710    /// cursor **before** the oldest available entry. Such change is likely and
711    /// will be based on the issue handling of the systemd team as well as on
712    /// user feedback.
713    ///
714    /// # Return values
715    /// - Ok(()): success
716    /// - Err(Error::SDError): sd-journal returned an error code
717    #[cfg(feature = "experimental")]
718    pub fn seek_head(&self) -> Result<(), Error> {
719        let result = unsafe { ffi::sd_journal_seek_head(self.ffi) };
720        if result < 0 {
721            return Err(Error::SDError(result));
722        }
723        Ok(())
724    }
725
726    /// **UNSTABLE API** Seek to the tail of the journal (implements
727    /// [`sd_journal_seek_tail`](https://www.freedesktop.org/software/systemd/man/sd_journal_seek_head.html#)).
728    ///
729    /// Seek to the end of the journal, i.e. the position after the most recent
730    /// available entry. Be aware that after a seek_head() the
731    /// journal cursor does not point to a valid entry. One must perform a
732    /// cursor movement before being able to retrieve data.
733    ///
734    /// # Examples
735    /// ```
736    /// # use sd_journal::*;
737    /// # let journal = Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
738    /// journal.seek_tail().unwrap();
739    /// // seek_head() should be followed by a previous() before any next() --> issues
740    /// journal.previous().unwrap();
741    /// // next() should hit EoF
742    /// assert_eq!(journal.next(), Ok(CursorMovement::EoF));
743    /// ```
744    ///
745    /// # libsystemd Issues
746    /// While `seek_head()` is supposed to move the cursor before the first
747    /// available journal entry, libsystemd may still successfully perform
748    /// `previous()` cursor movements for multiple times. All these unexpected
749    /// entries will report a full set of data and may appear fully valid
750    /// although be assured they are not. An
751    /// [error](https://github.com/systemd/systemd/issues/17662) has been
752    /// reported to the systemd project. The issue can be avoided if a `next()`
753    /// operation is executed immediately after `seek_head()` before issuing any
754    /// `previous()`. If done, `previous()`thereafter will correctly report EoF.
755    ///
756    /// # Stability
757    /// Due to the issue described above, this method may be changed in future
758    /// to include a `next()` in future releases and thus position the cursor
759    /// rather **on** the first available entry rather than positioning the
760    /// cursor **before** the oldest available entry. Such change is likely and
761    /// will be based on the issue handling of the systemd team as well as on
762    /// user feedback.
763    ///
764    /// # Return values
765    /// - Ok(()): success
766    /// - Err(Error::SDError): sd-journal returned an error code
767    #[cfg(feature = "experimental")]
768    pub fn seek_tail(&self) -> Result<(), Error> {
769        let result = unsafe { ffi::sd_journal_seek_tail(self.ffi) };
770        if result < 0 {
771            return Err(Error::SDError(result));
772        }
773        Ok(())
774    }
775
776    /// **UNSTABLE API** Seek to a monotonic timestamp of a certain boot id
777    /// (implements [`sd_journal_seek_monotonic_usec()`](https://www.freedesktop.org/software/systemd/man/sd_journal_seek_head.html#)).
778    ///
779    /// Seek to a position with the specified monotonic timestamp, i.e.
780    /// `clockMonotonic'. Since monotonic time restarts on every reboot a
781    /// boot ID needs to be specified as well.
782    ///
783    /// # Examples
784    /// ```
785    /// use sd_id128::*;
786    /// use sd_journal::*;
787    /// let journal = Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
788    /// // get the current system boot id
789    /// let boot_id = ID128::boot_id().unwrap();
790    /// // get the monotonic clock range of today
791    /// let (from, _) = journal.get_monotonic_cutoff(boot_id.clone()).unwrap();
792    /// // seek to the start of journal for the current boot
793    /// journal.seek_monotonic(boot_id.clone(), from).unwrap();
794    /// journal.previous().unwrap();
795    /// // do something with the first cursor of today...
796    /// ```
797    ///
798    /// # libsystemd Issues
799    /// According to the specification of `sd_journal_seek_monotonic_usec()`:
800    ///
801    /// > If no entry exists that matches exactly the specified seek address,
802    /// the next closest is sought to.
803    ///
804    /// Unfortunately libsystemd fails to comply if the monotonic timestamp
805    /// provided points to a position outside the journal range. Lets assume the
806    /// first valid log entry for a certain boot id exists at timestamp 500usec.
807    /// Seeking to anything beyond 500usec will work as expected while seeking
808    /// to anything before 500usec followed by a next() won't position the
809    /// cursor at the first entry of that boot id but rather position the cursor
810    /// at some random position. An
811    /// [issue](https://github.com/systemd/systemd/issues/17763) has been
812    /// reported to the systemd project.
813    ///
814    /// # Stability
815    /// This method expects `chrono::Duration` in the same way as
816    /// `get_monotonic()` for the same reasons: `get_realtime()` refers to
817    /// `chrono::NaiveDateTime`. In future releases this method may be changed
818    /// to microsenconds (u128) or std::time::Duration. Such change is
819    /// reasonable likely and will be made based on user feedback.
820    ///
821    /// # Return values
822    /// - Ok(())
823    /// - Err(Error::SDError): sd-journal returned an error code
824    /// - Err(Error::TimeStampOutOfRange): the `clock_monotonic` time stamp
825    ///   either reflects a negative duration or the duration exceeds i64
826    ///   microseconds
827    #[cfg(feature = "td_chrono")]
828    #[cfg(feature = "experimental")]
829    pub fn seek_monotonic(&self, boot_id: ID128, clock_monotonic: Duration) -> Result<(), Error> {
830        // let usec = clock_monotonic.to_std()
831        //                           .map_err(|_| Error::TimeStampOutOfRange)?
832        //                           .as_micros();
833        // let usec = u64::try_from(usec).map_err(|_| Error::TimeStampOutOfRange);
834        let usec: u64 = match clock_monotonic.num_microseconds() {
835            None => Err(Error::TimeStampOutOfRange)?,
836            Some(t) if t < 0 => Err(Error::TimeStampOutOfRange)?,
837            Some(t) => t as u64
838        };
839
840        let ffi_boot_id = boot_id.into_ffi();
841        let result = unsafe { ffi::sd_journal_seek_monotonic_usec(self.ffi, ffi_boot_id, usec) };
842        if result < 0 {
843            return Err(Error::SDError(result));
844        }
845        Ok(())
846    }
847
848    /// **UNSTABLE API** Seek to realtime timestamp (implements
849    /// [`sd_journal_seek_realtime_usec()`](https://www.freedesktop.org/software/systemd/man/sd_journal_seek_head.html#)).
850    ///
851    /// Seeks to a position with the specified realtime (wallclock) timestamp,
852    /// i.e. 'clockRealtime'. Note that the realtime clock is not necessarily
853    /// monotonic. If a realtime timestamp is ambiguous, it is not defined which
854    /// position is sought to.
855    ///
856    /// # Stability
857    /// Currently the function expects a chrono::NaiveDateTime. In future
858    /// releases this method may be changed to expect microseconds (u128) or
859    /// std::time::Duration although this is very unlikely. Changes will be
860    /// made based on user feedback.
861    ///
862    /// # Return values
863    /// - Ok(())
864    /// - Err(Error::SDError): sd-journal returned an error code
865    #[cfg(feature = "td_chrono")]
866    #[cfg(feature = "experimental")]
867    pub fn seek_realtime(&self, clock_realtime: NaiveDateTime) -> Result<(), Error> {
868        let usec = clock_realtime.timestamp_subsec_micros() as u64
869                   + clock_realtime.timestamp() as u64 * 1_000_000;
870        let result = unsafe { ffi::sd_journal_seek_realtime_usec(self.ffi, usec) };
871        if result < 0 {
872            return Err(Error::SDError(result));
873        }
874        Ok(())
875    }
876
877    /// **UNSTABLE API** Seeks the journal to the position of the cursor
878    /// provided (implements [`sd_journal_seek_cursor()`](https://www.freedesktop.org/software/systemd/man/sd_journal_seek_head.html#)).
879    ///
880    /// # Stability
881    /// See [`get_cursor_id()`](get_cursor_id) for reasons why there is a small
882    /// chance this method may be adjusted in future releases.
883    ///
884    /// # Return Values
885    /// - Ok(())
886    /// - Err(Error::SDError): sd-journal returned an error code
887    /// - Err(Error::NullError): a file path contains a 0-byte
888    #[cfg(feature = "experimental")]
889    pub fn seek_cursor_id(&self, cursor_id: String) -> Result<(), Error> {
890        let c_cursor = CString::new(cursor_id).map_err(Error::NullError)?;
891        let result = unsafe { ffi::sd_journal_seek_cursor(self.ffi, c_cursor.as_ptr()) };
892        if result < 0 {
893            return Err(Error::SDError(result));
894        }
895        Ok(())
896    }
897
898    /// Adds a match to filter journal entries (implements
899    /// [`sd_journal_add_match()`](https://www.freedesktop.org/software/systemd/man/sd_journal_add_match.html#)).
900    ///
901    /// Adds a filter on a field that will be applied on cursor movement
902    /// thereafter. The filter must follow the format "FIELDNAME=fieldvalue".
903    ///
904    /// # Examples
905    /// ```
906    /// # use sd_journal::*;
907    /// # let journal = Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
908    /// journal.add_match("MESSAGE=Hello World!").unwrap();
909    /// # assert_eq!(journal.next().unwrap(), CursorMovement::Done);
910    /// while let Ok(CursorMovement::Done) = journal.next() {
911    ///     // do something on the journal entries
912    /// }
913    /// ```
914    ///
915    /// # Return Values
916    /// - Ok(()): done
917    /// - Err(Error::SDError): sd-journal returned an error code
918    pub fn add_match<T: AsRef<[c_uchar]>>(&self, filter: T) -> Result<(), Error> {
919        let filter = filter.as_ref();
920        let result = unsafe {
921            ffi::sd_journal_add_match(self.ffi, filter.as_ptr() as *const c_void, filter.len())
922        };
923        if result < 0 {
924            return Err(Error::SDError(result));
925        }
926        Ok(())
927    }
928
929    /// Adds a disjuntion marker to match definitions (implements
930    /// [`sd_jounal_add_disjunction()`](https://www.freedesktop.org/software/systemd/man/sd_journal_add_match.html#).)
931    ///
932    /// # Return Values
933    /// - Ok(()): done
934    /// - Err(Error::SDError): sd-journal returned an error code
935    pub fn add_disjunction(&self) -> Result<(), Error> {
936        let result = unsafe { ffi::sd_journal_add_disjunction(self.ffi) };
937        if result < 0 {
938            return Err(Error::SDError(result));
939        }
940        Ok(())
941    }
942
943    /// Adds a conjuntion marker to match definitions (implements
944    /// [`sd_jounal_add_conjunction()`](https://www.freedesktop.org/software/systemd/man/sd_journal_add_match.html#).)
945    ///
946    /// # Return Values
947    /// - Ok(()): done
948    /// - Err(Error::SDError): sd-journal returned an error code
949    pub fn add_conjunction(&self) -> Result<(), Error> {
950        let result = unsafe { ffi::sd_journal_add_conjunction(self.ffi) };
951        if result < 0 {
952            return Err(Error::SDError(result));
953        }
954        Ok(())
955    }
956
957    /// Flushes the match definition (implements
958    /// [`sd_journal_flush_matches()`](https://www.freedesktop.org/software/systemd/man/sd_journal_add_match.html#))
959    pub fn flush_matches(&self) {
960        unsafe { ffi::sd_journal_flush_matches(self.ffi) }
961    }
962
963    /// **UNSTABLE API** Determines the timestamps of the first and last entry
964    /// in journal (implements [`sd_journal_get_cutoff_realtime_usec`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_cutoff_realtime_usec.html#)).
965    ///
966    /// # Stability
967    /// Currently the function returns a chrono::NaiveDateTime calculated from
968    /// the microseconds since EPOCH returned by the wrapped libsystemd
969    /// function. In future releases this method may be changed to return
970    /// microseconds (u128) or std::time::Duration although this is very
971    /// unlikely. Changes will be made based on user feedback.
972    ///
973    /// # Return Values:
974    /// - Ok((NaiveDateTime, NaiveDateTime)): (from, to) timestamps of the
975    ///   journal
976    /// - Err(Error::SDError): sd-journal returned an error code
977    #[cfg(feature = "td_chrono")]
978    #[cfg(feature = "experimental")]
979    pub fn get_realtime_cutoff(&self) -> Result<(NaiveDateTime, NaiveDateTime), Error> {
980        let mut from_usec: u64 = 0;
981        let mut to_usec: u64 = 0;
982        let result = unsafe {
983            ffi::sd_journal_get_cutoff_realtime_usec(self.ffi, &mut from_usec, &mut to_usec)
984        };
985        if result < 0 {
986            return Err(Error::SDError(result));
987        }
988        let from = NaiveDateTime::from_timestamp((from_usec / 1_000_000) as i64,
989                                                 ((from_usec % 1_000_000) * 1_000) as u32);
990        let to = NaiveDateTime::from_timestamp((to_usec / 1_000_000) as i64,
991                                               ((to_usec % 1_000_000) * 1_000) as u32);
992        Ok((from, to))
993    }
994
995    /// **UNSTABLE API** Determines the duration since boot of the first and
996    /// last entry in journal for a specific boot id (implements
997    /// [`sd_journal_get_cutoff_realtime_usec()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_cutoff_monotonic_usec.html#)).
998    ///
999    /// # Stability
1000    /// Currently the function returns a chrono::Duration calculated from the
1001    /// microseconds since boot returned by the wrapped libsystemd function. The
1002    /// choice for chrono::Duration has been made based on the return value for
1003    /// `get_realtime()`. In future releases this method may be changed to
1004    /// microsenconds (u128) or std::time::Duration. Such change is reasonable
1005    /// likely and will be made based on user feedback.
1006    ///
1007    /// # Return Values
1008    /// - Ok((Duration, Duration)): (from, to) respective duration since boot
1009    /// - Err(Error::SDError): sd-journal returned an error code
1010    #[cfg(feature = "td_chrono")]
1011    #[cfg(feature = "experimental")]
1012    pub fn get_monotonic_cutoff(&self, boot_id: ID128) -> Result<(Duration, Duration), Error> {
1013        let mut from_usec: u64 = 0;
1014        let mut to_usec: u64 = 0;
1015        let result = unsafe {
1016            ffi::sd_journal_get_cutoff_monotonic_usec(self.ffi,
1017                                                      boot_id.into_ffi(),
1018                                                      &mut from_usec,
1019                                                      &mut to_usec)
1020        };
1021        if result < 0 {
1022            return Err(Error::SDError(result));
1023        }
1024        let from = Duration::seconds((from_usec / 1_000_000) as i64)
1025                   + Duration::microseconds((from_usec % 1_000_000) as i64);
1026        let to = Duration::seconds((to_usec / 1_000_000) as i64)
1027                 + Duration::microseconds((to_usec % 1_000_000) as i64);
1028        Ok((from, to))
1029    }
1030
1031    /// Sets the data treshold limit for certain methods returning data fields
1032    /// (implements [`sd_journal_set_data_threshold()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_data.html#)).
1033    ///
1034    /// # Return Values
1035    /// - Ok(())
1036    /// - Err(Error::SDError): sd-journal returned an error code
1037    pub fn set_data_treshold(&self, size: size_t) -> Result<(), Error> {
1038        let result = unsafe { ffi::sd_journal_set_data_threshold(self.ffi, size) };
1039        if result < 0 {
1040            return Err(Error::SDError(result));
1041        }
1042        Ok(())
1043    }
1044
1045    /// Gets the currently applied data treshold (implements
1046    /// [`sd_journal_get_data_threshold()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_data.html#)).
1047    ///
1048    /// # Return Values
1049    /// - Ok(size_t)
1050    /// - Err(Error::SDError): sd-journal returned an error code
1051    pub fn get_data_treshold(&self) -> Result<size_t, Error> {
1052        let mut size: size_t = 0;
1053        let result = unsafe { ffi::sd_journal_get_data_threshold(self.ffi, &mut size) };
1054        if result < 0 {
1055            return Err(Error::SDError(result));
1056        }
1057        Ok(size)
1058    }
1059
1060    /// Enumerate the field names of the journal (implements
1061    /// [`sd_journal_enumerate_fields()`](https://www.freedesktop.org/software/systemd/man/sd_journal_enumerate_fields.html#)).
1062    ///
1063    /// This method follows the principle of libsystemd to call this method
1064    /// repeatedly until you reach EoF. See [`iter_fields`](iter_fields)
1065    /// for a rustified iterator over fields.
1066    ///
1067    /// # Return Values
1068    /// - Ok(Enumeration::EoF): no more fields
1069    /// - Ok(Enumeration::Value(String)): field name
1070    /// - Err(Error::SDError): sd-journal returned an error code
1071    pub fn enumerate_field_names(&self) -> Result<Enumeration<String>, Error> {
1072        let mut field: *const c_char = ptr::null();
1073        let result = unsafe { ffi::sd_journal_enumerate_fields(self.ffi, &mut field) };
1074        if result < 0 {
1075            return Err(Error::SDError(result));
1076        }
1077        if result == 0 {
1078            return Ok(Enumeration::EoF);
1079        }
1080        Ok(Enumeration::Value(unsafe {
1081            CStr::from_ptr(field).to_str()
1082                                 .map_err(Error::UTF8Error)?
1083                                 .to_owned()
1084        }))
1085    }
1086
1087    /// Restart field enumeration (implements
1088    /// [`sd_journal_restart_fields()`](https://www.freedesktop.org/software/systemd/man/sd_journal_enumerate_fields.html#)).
1089    pub fn restart_field_name_enumeration(&self) {
1090        unsafe { ffi::sd_journal_restart_fields(self.ffi) }
1091    }
1092
1093    /// Get an iterator of the field names of the journal.
1094    ///
1095    /// This is the rustified version of `enumerate_field_names()`.
1096    ///
1097    /// # Examples
1098    /// ```
1099    /// # use sd_journal::*;
1100    /// # let journal = Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
1101    /// // loop over field names and print them
1102    /// for fieldname in journal.iter_field_names() {
1103    ///     println!("{}", fieldname.unwrap());
1104    /// }
1105    /// ```
1106    pub fn iter_field_names<'a>(&'a self) -> FieldNames<'a> {
1107        FieldNames { journal: self }
1108    }
1109
1110    /// Returns a read only file descriptor to be used in polling the journal
1111    /// (implements [`sd_journal_get_fd()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_fd.html#)).
1112    ///
1113    /// # Return Values
1114    /// - Ok(RawFd)
1115    /// - Err(Error::SDError): sd-journal returned an error code
1116    pub fn get_fd(&self) -> Result<RawFd, Error> {
1117        let result = unsafe { ffi::sd_journal_get_fd(self.ffi) };
1118        if result < 0 {
1119            return Err(Error::SDError(result));
1120        }
1121        Ok(result)
1122    }
1123
1124    /// Returns events to be used in polling the journal on the file descriptor
1125    /// (implements [`sd_journal_get_events()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_fd.html#)).
1126    ///
1127    /// # Return Values
1128    /// - Ok(c_int): events to be used in polling the file descriptor
1129    /// - Err(Error::SDError): sd-journal returned an error code
1130    pub fn get_events(&self) -> Result<c_int, Error> {
1131        let result = unsafe { ffi::sd_journal_get_fd(self.ffi) };
1132        if result < 0 {
1133            return Err(Error::SDError(result));
1134        }
1135        Ok(result)
1136    }
1137
1138    /// Returns the timeout to be used in polling the journal on the file
1139    /// descriptor (implements
1140    /// [`sd_journal_get_timeout()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_fd.html#)).
1141    ///
1142    /// # Return Values
1143    /// - Ok(u64): timeout
1144    /// - Err([Error::SDError](Error)): sd-journal returned an error code
1145    pub fn get_timeout(&self) -> Result<u64, Error> {
1146        let mut timeout: u64 = 0;
1147        let result = unsafe { ffi::sd_journal_get_timeout(self.ffi, &mut timeout) };
1148        if result < 0 {
1149            return Err(Error::SDError(result));
1150        }
1151        Ok(timeout)
1152    }
1153
1154    /// Processes events after each wake-up and returns the type of events
1155    /// (implements [`sd_journal_process()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_fd.html#)).
1156    ///
1157    /// # Return Values
1158    /// - Ok(Event): journal wake event
1159    /// - Err(Error::SDError): sd-journal returned an error code
1160    pub fn process(&self) -> Result<Event, Error> {
1161        let result = unsafe { ffi::sd_journal_process(self.ffi) };
1162        match result {
1163            ffi::SD_JOURNAL_NOP => Ok(Event::NOOP),
1164            ffi::SD_JOURNAL_APPEND => Ok(Event::Append),
1165            ffi::SD_JOURNAL_INVALIDATE => Ok(Event::Invalidate),
1166            _ => Err(Error::SDError(result))
1167        }
1168    }
1169
1170    /// Wait for changes in the journal for a maximum period defined in timeout
1171    /// (implements [`sd_journal_wait()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_fd.html#)).
1172    ///
1173    /// Use uint64_t-1 for timeout to wait indefinitely.
1174    ///
1175    /// # Return Values
1176    /// - Ok(Event): journal wake event
1177    /// - Err(Error::SDError): sd-journal returned an error code
1178    pub fn wait(&self, timeout: u64) -> Result<Event, Error> {
1179        let result = unsafe { ffi::sd_journal_wait(self.ffi, timeout) };
1180        match result {
1181            ffi::SD_JOURNAL_NOP => Ok(Event::NOOP),
1182            ffi::SD_JOURNAL_APPEND => Ok(Event::Append),
1183            ffi::SD_JOURNAL_INVALIDATE => Ok(Event::Invalidate),
1184            _ => Err(Error::SDError(result))
1185        }
1186    }
1187
1188    /// Checks whether the journal owns runtime files (implements
1189    /// [`sd_journal_has_runtime_files()`](https://www.freedesktop.org/software/systemd/man/sd_journal_has_runtime_files.html#)).
1190    ///
1191    /// # Return Values
1192    /// - Ok(bool)
1193    /// - Err(Error::SDError): sd-journal returned an error code
1194    pub fn has_runtime_files(&self) -> Result<bool, Error> {
1195        let result = unsafe { ffi::sd_journal_has_runtime_files(self.ffi) };
1196        if result < 0 {
1197            return Err(Error::SDError(result));
1198        }
1199        Ok(result > 0)
1200    }
1201
1202    /// Checks whether the journal owns persistent files (implements
1203    /// [`sd_journal_has_persistent_files()`](https://www.freedesktop.org/software/systemd/man/sd_journal_has_persistent_files.html#)).
1204    ///
1205    /// # Return Values
1206    /// - Ok(bool)
1207    /// - Err(Error::SDError): sd-journal returned an error code
1208    pub fn has_persistent_files(&self) -> Result<bool, Error> {
1209        let result = unsafe { ffi::sd_journal_has_persistent_files(self.ffi) };
1210        if result < 0 {
1211            return Err(Error::SDError(result));
1212        }
1213        Ok(result > 0)
1214    }
1215
1216    /// **UNSTABLE API** Determines the disk space used by the journal in Bytes
1217    /// (implements [`sd_journal_get_usage()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_usage.html#)).
1218    ///
1219    /// # Stability
1220    /// Currently a plain u64 is returned. There is a reasonable chance for a
1221    /// change in future releases towards a SI unit type.
1222    ///
1223    /// # Return Values
1224    /// - Ok(u64): space required in Bytes
1225    /// - Err(Error::SDError): sd-journal returned an error code
1226    #[cfg(feature = "experimental")]
1227    pub fn get_usage(&self) -> Result<u64, Error> {
1228        let mut usage: u64 = 0;
1229        let result = unsafe { ffi::sd_journal_get_usage(self.ffi, &mut usage) };
1230        if result < 0 {
1231            return Err(Error::SDError(result));
1232        }
1233        Ok(usage)
1234    }
1235
1236    /// **UNSTABLE API** Retrieves the realtime timestamp as
1237    /// chrono::NaiveDateTime of the current record (implements
1238    /// [`sd_journal_get_realtime_usec()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_realtime_usec.html#)).
1239    ///
1240    /// # Stability
1241    /// Currently the function returns a chrono::NaiveDateTime calculated from
1242    /// the microseconds since EPOCH returned by the wrapped libsystemd
1243    /// function. In future releases this method may be changed to return
1244    /// microseconds (u128) or std::time::Duration although this is very
1245    /// unlikely. Changes will be made based on user feedback.
1246    ///
1247    /// # Return Values:
1248    /// - Ok(NaiveDateTime): realtime timestamp of current record
1249    /// - Err(Error::SDError): sd-journal returned an error code
1250    #[cfg(feature = "td_chrono")]
1251    #[cfg(feature = "experimental")]
1252    pub fn get_realtime(&self) -> Result<NaiveDateTime, Error> {
1253        let mut usec: u64 = 0;
1254        let result = unsafe { ffi::sd_journal_get_realtime_usec(self.ffi, &mut usec) };
1255        if result < 0 {
1256            return Err(Error::SDError(result));
1257        }
1258        let dt = NaiveDateTime::from_timestamp((usec / 1_000_000) as i64,
1259                                               ((usec % 1_000_000) * 1_000) as u32);
1260        Ok(dt)
1261    }
1262
1263    /// **UNSTABLE API** Retrieves the monotonic timestamp of the current record
1264    /// altogether with it's boot id (implements [`sd_journal_get_monotonic_usec()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_realtime_usec.html#)).
1265    ///
1266    /// # Stability
1267    /// Currently the function returns a chrono::Duration calculated from the
1268    /// microseconds since boot returned by the wrapped libsystemd function. The
1269    /// choice for chrono::Duration has been made based on the return value for
1270    /// `get_realtime()`. In future releases this method may be changed to
1271    /// microsenconds (u128) or std::time::Duration. Such change is reasonable
1272    /// likely and will be made based on user feedback.
1273    ///
1274    /// # Return Values
1275    /// - Ok(chrono::Duration, ID128): tuple of a monotonic timestamp since boot
1276    ///   and boot id
1277    /// - Err(Error::SDError): sd-journal returned an error code
1278    #[cfg(feature = "td_chrono")]
1279    #[cfg(feature = "experimental")]
1280    pub fn get_monotonic(&self) -> Result<(Duration, sd_id128::ID128), Error> {
1281        let mut usec: u64 = 0;
1282        let mut boot_id = ID128::default().into_ffi();
1283        let result =
1284            unsafe { ffi::sd_journal_get_monotonic_usec(self.ffi, &mut usec, &mut boot_id) };
1285        if result < 0 {
1286            return Err(Error::SDError(result));
1287        }
1288        let duration = Duration::seconds((usec / 1_000_000) as i64)
1289                       + Duration::microseconds((usec % 1_000_000) as i64);
1290        Ok((duration, ID128::from_ffi(boot_id)))
1291    }
1292
1293    /// **UNSTABLE API** Retrieve a text representation of the cursor
1294    /// (implements [`sd_journal_get_cursor()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_cursor.html#)).
1295    ///
1296    /// # Stability
1297    /// `sd_journal_get_cursor()` returns a ownership of a memory location.
1298    /// Currently the content is copied into a rustified String and the memory
1299    /// freed immediately. In future releases a new data type could be defined
1300    /// which avoids the immediate conversion into a String. The new data type
1301    /// could be handed over into `seek_cursor()`. The chance for such change is
1302    /// low. The decission will be taken based on typical usage scenarios and
1303    /// user feedback.
1304    ///
1305    /// # Return values
1306    /// - Ok(String): cursor representation of sd-journal
1307    /// - Err(Error::SDError): sd-journal returned an error code
1308    /// - Err(Error::UTF8Error): UTF-8 decoding error occured; although this
1309    ///   should never happen since the journal internal cursor id is stored in
1310    ///   valid UTF-8
1311    #[cfg(feature = "experimental")]
1312    pub fn get_cursor_id(&self) -> Result<String, Error> {
1313        let mut ptr: *mut c_char = ptr::null_mut();
1314        let result = unsafe { ffi::sd_journal_get_cursor(self.ffi, &mut ptr) };
1315        if result < 0 {
1316            return Err(Error::SDError(result));
1317        }
1318        let cursor_id = unsafe { CStr::from_ptr(ptr) };
1319        let cursor_id = match cursor_id.to_str() {
1320            Err(error) => {
1321                unsafe { libc::free(ptr as *mut c_void) };
1322                Err(Error::UTF8Error(error))?
1323            },
1324            Ok(value) => value.to_owned()
1325        };
1326        unsafe { libc::free(ptr as *mut c_void) };
1327        Ok(cursor_id)
1328    }
1329
1330    /// **UNSTABLE API** Checks whether the current journal position matches a
1331    /// cursor id (implements [`sd_journal_get_cursor`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_cursor.html#)).
1332    ///
1333    /// # Stability
1334    /// See [`get_cursor_id()`](get_cursor_id) for reasons why there is a small
1335    /// chance this method may be adjusted in future releases.
1336    ///
1337    /// # Return Values
1338    /// - Ok(bool)
1339    /// - Err(Error::SDError): sd-journal returned an error code
1340    #[cfg(feature = "experimental")]
1341    pub fn cursor_id_matches<S: Into<Vec<u8>>>(&self, cursor_id: S) -> Result<bool, Error> {
1342        let c_cursor = CString::new(cursor_id).map_err(Error::NullError)?;
1343        let result = unsafe { ffi::sd_journal_test_cursor(self.ffi, c_cursor.as_ptr()) };
1344        if result < 0 {
1345            return Err(Error::SDError(result));
1346        }
1347        Ok(result > 0)
1348    }
1349
1350    /// Determine the message cataloge entry for the current record (implements
1351    /// [`sd_journal_get_catalog()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_catalog.html#)).
1352    ///
1353    /// # Return Values
1354    /// - Ok(String): message catalogue
1355    /// - Err(Error::SDError): sd-journal returned an error code
1356    /// - Err(Error::UTF8Error): UTF-8 decoding error occured
1357    pub fn get_catalog(&self) -> Result<String, Error> {
1358        let mut data: *mut c_char = ptr::null_mut();
1359        let result = unsafe { ffi::sd_journal_get_catalog(self.ffi, &mut data) };
1360        if result < 0 {
1361            return Err(Error::SDError(result));
1362        }
1363        let catalog = unsafe { CStr::from_ptr(data) };
1364        let catalog = match catalog.to_str() {
1365            Err(error) => {
1366                unsafe { libc::free(data as *mut c_void) };
1367                Err(Error::UTF8Error(error))?
1368            },
1369            Ok(value) => value.to_owned()
1370        };
1371        unsafe { libc::free(data as *mut c_void) };
1372        Ok(catalog)
1373    }
1374
1375    /// Retrieve the data of a specific field (implements
1376    /// [`sd_journal_get_data()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_data.html#)).
1377    ///
1378    /// Retrieve the data of a specific field. The fieldname must be provided in
1379    /// all upper case letters. See the documentation of well-known
1380    /// [field names](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html#).
1381    /// Field names may not contain 0x00 bytes (would raise a NullError). If the
1382    /// current entry does not contain the field, an SDError(-2) is returned.
1383    ///
1384    /// # Examples
1385    /// ```
1386    /// # use sd_journal::*;
1387    /// # use std::path::PathBuf;
1388    /// # let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1389    /// # test_data.push("test-data/");
1390    /// # println!("looking for test data in folder {}", test_data.display());
1391    /// # let journal = Journal::open_directory(&test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
1392    /// // loop over the journal and print the timestamp and message of each record
1393    /// for cursor in &journal {
1394    ///     let cursor = cursor.unwrap();
1395    ///     let message = cursor.get_data("MESSAGE")
1396    ///                         .unwrap_or("[no message available]".to_string());
1397    ///     let datetime = cursor.get_realtime().unwrap();
1398    ///     println!("{} - {}", datetime, message);
1399    /// }
1400    /// ```
1401    ///
1402    /// # Return values
1403    /// - Ok(String): value in the format FIELDNAME=FIELDVALUE
1404    /// - Err(Error::NullError): the requested field name contains 0-bytes
1405    /// - Err(Error::SDError): sd-journal returned an error code
1406    /// - Err(Error::UTF8Error): UTF-8 decoding error occured
1407    /// - Err(Error::UnexpectedDataFormat): libsystemd is expected to return
1408    ///   data in the format `FIELDNAME=field value`. Before returning that
1409    ///   data, `FIELDNAME=` are stripped of. If that operation fails, this
1410    ///   error is raised.
1411    /// - Err(Error::StringError): if this error is raised, please report an
1412    ///   issue against sd_journal
1413    pub fn get_data<F: Into<Vec<u8>>>(&self, field: F) -> Result<String, Error> {
1414        let c_field = CString::new(field).map_err(Error::NullError)?;
1415        let mut data: *const c_void = std::ptr::null_mut();
1416        let mut length: size_t = 0;
1417        let result =
1418            unsafe { ffi::sd_journal_get_data(self.ffi, c_field.as_ptr(), &mut data, &mut length) };
1419        if result < 0 {
1420            return Err(Error::SDError(result));
1421        }
1422        let result = unsafe {
1423            CStr::from_ptr(data as *mut c_char).to_str()
1424                                               .map_err(Error::UTF8Error)?
1425        };
1426
1427        let field = c_field.into_string().map_err(Error::StringError)?;
1428        let result = match result.strip_prefix(&field) {
1429            None => Err(Error::UnexpectedDataFormat)?,
1430            Some(value) => match value.strip_prefix('=') {
1431                None => Err(Error::UnexpectedDataFormat)?,
1432                Some(value) => value
1433            }
1434        };
1435        Ok(result.to_string())
1436    }
1437
1438    /// Enumerate the fields of the current record (implements
1439    /// [`sd_journal_enumerate_data()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_data.html#)).
1440    ///
1441    /// This is the libsystemd way of iterating over fields. There is also a
1442    /// rustified alternative method [`iter_fields()`](Journal::iter_fields).
1443    ///
1444    /// # Examples
1445    /// ```
1446    /// # use sd_journal::*;
1447    /// # let journal = Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
1448    /// # journal.next().unwrap();
1449    /// // loop over all fields of the current record and print FIELDNAME: field value
1450    /// while let Ok(Enumeration::Value((field, value))) = journal.enumerate_fields() {
1451    ///     println!("{}: {}", field, value)
1452    /// }
1453    /// ```
1454    ///
1455    /// # Return values
1456    /// - Ok(Enumeration::Value(String, String)): field name and value
1457    /// - Ok(Enumeration::EoF): no more fields to enumerate
1458    /// - Err(Error::SDError): sd-journal returned an error code
1459    /// - Err(Error::UTF8Error): UTF-8 decoding error occured
1460    /// - Err(Error::UnexpectedDataFormat): libsystemd is expected to return
1461    ///   data in the format `FIELDNAME=field value`. Field name and value are
1462    ///   separated at the `=`. If the format does not match, this error is
1463    ///   raised.
1464    pub fn enumerate_fields(&self) -> Result<Enumeration<(String, String)>, Error> {
1465        let mut data: *const c_void = ptr::null_mut();
1466        let mut length: size_t = 0;
1467        let result = unsafe { ffi::sd_journal_enumerate_data(self.ffi, &mut data, &mut length) };
1468        if result < 0 {
1469            return Err(Error::SDError(result));
1470        }
1471        if result == 0 {
1472            return Ok(Enumeration::EoF);
1473        }
1474        let result = unsafe {
1475            CStr::from_ptr(data as *const c_char).to_str()
1476                                                 .map_err(Error::UTF8Error)?
1477                                                 .to_owned()
1478        };
1479        let (field, value) = match result.find('=') {
1480            None => Err(Error::UnexpectedDataFormat)?,
1481            Some(index) => result.split_at(index)
1482        };
1483        let value = match value.strip_prefix('=') {
1484            None => Err(Error::UnexpectedDataFormat)?,
1485            Some(value) => value
1486        };
1487        Ok(Enumeration::Value((field.to_owned(), value.to_owned())))
1488    }
1489
1490    /// Enumerate the available & supported fields of the current record
1491    /// (implements [`sd_journal_enumerate_available_data()`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_data.html#)).
1492    ///
1493    /// # Return values
1494    /// - Ok(Enumeration::Value(String, String)): field name and value
1495    /// - Ok(Enumeration::EoF): no more fields to enumerate
1496    /// - Err(Error::SDError): sd-journal returned an error code
1497    /// - Err(Error::UTF8Error): UTF-8 decoding error occured
1498    /// - Err(Error::UnexpectedDataFormat): libsystemd is expected to return
1499    ///   data in the format `FIELDNAME=field value`. Field name and value are
1500    ///   separated at the `=`. If the format does not match, this error is
1501    ///   raised.
1502    pub fn enumerate_available_fields(&self) -> Result<Enumeration<(String, String)>, Error> {
1503        let mut data: *const c_void = ptr::null_mut();
1504        let mut length: size_t = 0;
1505        let result =
1506            unsafe { ffi::sd_journal_enumerate_available_data(self.ffi, &mut data, &mut length) };
1507        if result < 0 {
1508            return Err(Error::SDError(result));
1509        }
1510        if result == 0 {
1511            return Ok(Enumeration::EoF);
1512        }
1513        if result == 0 {
1514            return Ok(Enumeration::EoF);
1515        }
1516        let result = unsafe {
1517            CStr::from_ptr(data as *const c_char).to_str()
1518                                                 .map_err(Error::UTF8Error)?
1519                                                 .to_owned()
1520        };
1521        let (field, value) = match result.find('=') {
1522            None => Err(Error::UnexpectedDataFormat)?,
1523            Some(index) => result.split_at(index)
1524        };
1525        let value = match value.strip_prefix('=') {
1526            None => Err(Error::UnexpectedDataFormat)?,
1527            Some(value) => value
1528        };
1529        Ok(Enumeration::Value((field.to_owned(), value.to_owned())))
1530    }
1531
1532    /// Restart enumeration of fields (implements
1533    /// [`sd_journal_restart_data`](https://www.freedesktop.org/software/systemd/man/sd_journal_get_data.html#)).
1534    pub fn restart_fields_enumeration(&self) {
1535        unsafe {
1536            ffi::sd_journal_restart_data(self.ffi);
1537        }
1538    }
1539
1540    /// Returns an iterator over the fields of the current records.
1541    ///
1542    /// # Examples
1543    /// ```
1544    /// # use sd_journal::*;
1545    /// let journal = Journal::open(FileFlags::AllFiles, UserFlags::AllUsers).unwrap();
1546    /// # journal.next().unwrap();
1547    /// // The following 2 loops are synonyms
1548    /// while let Ok(Enumeration::Value((field, value))) = journal.enumerate_fields() {
1549    ///     println!("{}: {}", field, value);
1550    /// }
1551    /// for field in journal.iter_fields() {
1552    ///     let (field, value) = field.unwrap();
1553    ///     println!("{}: {}", field, value);
1554    /// }
1555    /// ```
1556    pub fn iter_fields<'a>(&'a self) -> Fields<'a> {
1557        Fields { journal: self }
1558    }
1559
1560    /// Query the journal for unique field values of a certain field (implements
1561    /// [`sd_journal_query_unique()`](https://www.freedesktop.org/software/systemd/man/sd_journal_query_unique.html#)).
1562    ///
1563    /// # libsystemd Issues
1564    /// `sd_journal_query_unique()` and the related functions do not always
1565    /// succeed to return **unique** values, i.e. a value may be returned
1566    /// repeatedly. An [issue](https://github.com/systemd/systemd/issues/18075)
1567    /// has been reported.
1568    ///
1569    /// # Return Values
1570    /// - Ok(())
1571    /// - Err(Error::SDError): sd-journal returned an error code
1572    pub fn query_unique_values<S: Into<Vec<u8>>>(&self, field: S) -> Result<(), Error> {
1573        let c_field = CString::new(field).map_err(Error::NullError)?;
1574        let result = unsafe { ffi::sd_journal_query_unique(self.ffi, c_field.as_ptr()) };
1575        if result < 0 {
1576            return Err(Error::SDError(result));
1577        }
1578        Ok(())
1579    }
1580
1581    /// Enumerate all unique values for the field requested (implements
1582    /// [`sd_journal_enumerate_unique`](https://www.freedesktop.org/software/systemd/man/sd_journal_query_unique.html#)).
1583    ///
1584    /// Return Values
1585    /// - Ok(Enumeration::Value(String)): value
1586    /// - Ok(Enumeration::EoF): no more unique values to enumerate
1587    /// - Err(Error::UTF8Error): UTF-8 decoding error occured
1588    /// - Err(Error::SDError): sd-journal returned an error code
1589    pub fn enumerate_unique_values(&self) -> Result<Enumeration<String>, Error> {
1590        let mut data: *const c_void = ptr::null_mut();
1591        let mut length: size_t = 0;
1592        let result = unsafe { ffi::sd_journal_enumerate_unique(self.ffi, &mut data, &mut length) };
1593        if result < 0 {
1594            return Err(Error::SDError(result));
1595        }
1596        if result == 0 {
1597            return Ok(Enumeration::EoF);
1598        }
1599        let result = unsafe {
1600            CStr::from_ptr(data as *const c_char).to_str()
1601                                                 .map_err(Error::UTF8Error)?
1602        };
1603        let index = match result.find('=') {
1604            None => Err(Error::UnexpectedDataFormat)?,
1605            Some(index) => index
1606        };
1607        let (_, result) = result.split_at(index + 1);
1608        Ok(Enumeration::Value(result.to_owned()))
1609    }
1610
1611    /// Enumerate available unique values for the field requested (implements
1612    /// [`sd_journal_enumerate_available_unique`](https://www.freedesktop.org/software/systemd/man/sd_journal_query_unique.html#)).
1613    ///
1614    /// Return Values:
1615    /// - Ok(Enumeration::Value(String)): value
1616    /// - Ok(Enumeration::EoF): no more unique values to enumerate
1617    /// - Err(Error::UTF8Error): UTF-8 decoding error occured
1618    /// - Err(Error::SDError): sd-journal returned an error code
1619    pub fn enumerate_available_unique_values(&self) -> Result<Enumeration<String>, Error> {
1620        let mut data: *const c_void = ptr::null_mut();
1621        let mut length: size_t = 0;
1622        let result =
1623            unsafe { ffi::sd_journal_enumerate_available_unique(self.ffi, &mut data, &mut length) };
1624        if result < 0 {
1625            return Err(Error::SDError(result));
1626        }
1627        if result == 0 {
1628            return Ok(Enumeration::EoF);
1629        }
1630        Ok(Enumeration::Value(unsafe {
1631            CStr::from_ptr(data as *const c_char).to_str()
1632                                                 .map_err(Error::UTF8Error)?
1633                                                 .to_owned()
1634        }))
1635    }
1636
1637    /// Restart enumeration of unique values (implements
1638    /// [`sd_journal_restart_unique`](https://www.freedesktop.org/software/systemd/man/sd_journal_query_unique.html#)).
1639    pub fn restart_unique_value_enumeration(&self) {
1640        unsafe { ffi::sd_journal_restart_unique(self.ffi) }
1641    }
1642
1643    /// Returns an iterator over unique values of a field.
1644    ///
1645    /// # Examples
1646    /// ```
1647    /// # use sd_journal::*;
1648    /// # use std::path::PathBuf;
1649    /// # let mut test_data = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1650    /// # test_data.push("test-data/");
1651    /// # println!("looking for test data in folder {}", test_data.display());
1652    /// # let journal = Journal::open_directory(&test_data, PathFlags::FullPath, UserFlags::AllUsers).unwrap();
1653    /// for value in journal.iter_unique_values("MESSAGE").unwrap() {
1654    ///     let value = value.unwrap();
1655    ///     println!("{}", value);
1656    /// }
1657    /// ```
1658    ///
1659    /// # Return Values
1660    /// - Ok(UniqueValues)
1661    /// - Err(Error::SDError): sd-journal returned an error code
1662    pub fn iter_unique_values<'a, S: Into<Vec<u8>>>(&'a self,
1663                                                    field: S)
1664                                                    -> Result<UniqueValues<'a>, Error> {
1665        self.query_unique_values(field)?;
1666        Ok(UniqueValues { journal: &self })
1667    }
1668}
1669
1670impl<'a> Cursor<'a> {
1671    /// see [Journal::get_realtime](Journal::get_realtime)
1672    pub fn get_realtime(&self) -> Result<NaiveDateTime, Error> {
1673        self.journal.get_realtime()
1674    }
1675
1676    /// see [Journal::get_monotonic](Journal::get_monotonic)
1677    pub fn get_monotonic(&self) -> Result<(Duration, sd_id128::ID128), Error> {
1678        self.journal.get_monotonic()
1679    }
1680
1681    /// see [Journal::get_cursor_id](Journal::get_cursor_id)
1682    pub fn get_id(&self) -> Result<String, Error> {
1683        self.journal.get_cursor_id()
1684    }
1685
1686    /// see [Journal::cursor_id_matches](Journal::cursor_id_matches)
1687    pub fn id_matches<S: Into<Vec<u8>>>(&self, cursor_id: S) -> Result<bool, Error> {
1688        self.journal.cursor_id_matches(cursor_id)
1689    }
1690
1691    /// see [Journal::get_catalog](Journal::get_catalog)
1692    pub fn get_catalog(&self) -> Result<String, Error> {
1693        self.journal.get_catalog()
1694    }
1695
1696    /// see [Journal::get_data](Journal::get_data)
1697    pub fn get_data<F: Into<Vec<u8>>>(&self, field: F) -> Result<String, Error> {
1698        self.journal.get_data(field)
1699    }
1700
1701    /// see [Journal::enumerate_fields](Journal::enumerate_fields)
1702    pub fn enumerate_fields(&self) -> Result<Enumeration<(String, String)>, Error> {
1703        self.journal.enumerate_fields()
1704    }
1705
1706    /// see [Journal::enumerate_available_fields](Journal::
1707    /// enumerate_available_fields)
1708    pub fn enumerate_available_fields(&self) -> Result<Enumeration<(String, String)>, Error> {
1709        self.journal.enumerate_available_fields()
1710    }
1711
1712    /// see [Journal::restart_fields_enumeration](Journal::
1713    /// restart_fields_enumeration)
1714    pub fn restart_fields_enumeration(&self) {
1715        self.journal.restart_fields_enumeration()
1716    }
1717
1718    /// see [Journal::iter_fields](Journal::iter_fields)
1719    pub fn iter_fields(&self) -> Fields<'a> {
1720        self.journal.iter_fields()
1721    }
1722}