1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
use dlog::{BufferId, Priority};
use dlog_sys::{log_id_t, log_priority};
use dlogutil_sys::*;
use libc::timespec;
use std::ffi::CStr;
use std::io::{Error, ErrorKind, Result};
use std::ptr::{null, null_mut};
use std::time::Duration;

/// Enumeration for timestamp-based log sorting orderings.
///
/// For more detailed information on the timestamp documentation,
/// refer to the C library documentation.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum SortingOrder {
    /// Monotonic timestamp applied by the sender.
    SentMono,
    /// Real-time timestamp applied by the sender.
    SentReal,
    /// Monotonic timestamp applied by the receiver.
    RecvMono,
    /// Real-time timestamp applied by the receiver.
    RecvReal,
    /// The default timestamp of the buffer. See [`buffer_get_default_ts_type`].
    Default,
}

impl From<dlogutil_sorting_order> for SortingOrder {
    fn from(c_enum: dlogutil_sorting_order) -> Self {
        match c_enum {
            dlogutil_sorting_order::DLOGUTIL_SORT_SENT_MONO => SortingOrder::SentMono,
            dlogutil_sorting_order::DLOGUTIL_SORT_SENT_REAL => SortingOrder::SentReal,
            dlogutil_sorting_order::DLOGUTIL_SORT_RECV_MONO => SortingOrder::RecvMono,
            dlogutil_sorting_order::DLOGUTIL_SORT_RECV_REAL => SortingOrder::RecvReal,
            dlogutil_sorting_order::DLOGUTIL_SORT_DEFAULT => SortingOrder::Default,
        }
    }
}

impl From<SortingOrder> for dlogutil_sorting_order {
    fn from(rust_enum: SortingOrder) -> Self {
        match rust_enum {
            SortingOrder::SentMono => dlogutil_sorting_order::DLOGUTIL_SORT_SENT_MONO,
            SortingOrder::SentReal => dlogutil_sorting_order::DLOGUTIL_SORT_SENT_REAL,
            SortingOrder::RecvMono => dlogutil_sorting_order::DLOGUTIL_SORT_RECV_MONO,
            SortingOrder::RecvReal => dlogutil_sorting_order::DLOGUTIL_SORT_RECV_REAL,
            SortingOrder::Default => dlogutil_sorting_order::DLOGUTIL_SORT_DEFAULT,
        }
    }
}

/// A struct containing libdlogutil initialisation configuration.
pub struct DlogutilConfig {
    config: *mut dlogutil_config,
}

// SAFETY: it is ok to use the this object from different threads if only one thread
// is using the memory at the time.
unsafe impl Send for DlogutilConfig {}

// SAFETY: the operations that do not modify the memory at all are ok to be used from
// different threads at once, and those who do are marked as &mut.
unsafe impl Sync for DlogutilConfig {}

/// A struct containing the state of a log handling request.
pub struct DlogutilState {
    state: *mut dlogutil_state,
}

// SAFETY: it is ok to use the this object from different threads if only one thread
// is using the memory at the time.
unsafe impl Send for DlogutilState {}

// SAFETY: the operations that do not modify the memory at all are ok to be used from
// different threads at once, and those who do are marked as &mut.
unsafe impl Sync for DlogutilState {}

/// A struct containing the metadata and contents for a single dlog entry.
pub struct DlogutilEntry {
    entry: *mut dlogutil_entry,
}

// SAFETY: it is ok to use the this object from different threads if only one thread
// is using the memory at the time.
unsafe impl Send for DlogutilEntry {}

// SAFETY: the operations that do not modify the memory at all are ok to be used from
// different threads at once, and those who do are marked as &mut.
unsafe impl Sync for DlogutilEntry {}

impl Drop for DlogutilConfig {
    fn drop(&mut self) {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        unsafe {
            dlogutil_config_destroy(self.config);
        }
    }
}

impl Drop for DlogutilState {
    fn drop(&mut self) {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        unsafe {
            dlogutil_state_destroy(self.state);
        }
    }
}

impl Drop for DlogutilEntry {
    fn drop(&mut self) {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        unsafe {
            dlogutil_entry_destroy(self.entry);
        }
    }
}

impl DlogutilConfig {
    /// Creates a new [`DlogutilConfig`] struct to be filled with configuration.
    ///
    /// Useful for dumping the logs of a specific thread in a multithreaded process.
    ///
    /// # Returns
    ///
    /// * `Ok(Self)` - A handle to the config struct.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::OutOfMemory`] - Out of memory.
    pub fn create() -> Result<Self> {
        // SAFETY: the function is safe by itself. It returns either a valid pointer,
        // in which case we return Ok, or NULL, in which case we return Err, which
        // means that if Self is created, the inner pointer is guaranteed to be correct.
        let config = unsafe { dlogutil_config_create() };
        if config.is_null() {
            return Err(Error::from(ErrorKind::OutOfMemory));
        }
        Ok(DlogutilConfig { config })
    }

    /// Enables retrieving only those logs that are logged by the thread with
    /// the given TID.
    ///
    /// Useful for dumping the logs of a specific thread in a multithreaded process.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    /// * [`std::io::ErrorKind::OutOfMemory`] - Out of memory.
    pub fn filter_tid(&mut self, tid: i32) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_filter_tid(self.config, tid) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Enables retrieving only those logs that are logged by the process with
    /// the given PID.
    ///
    /// Useful for dumping the logs of a specific process.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    /// * [`std::io::ErrorKind::OutOfMemory`] - Out of memory.
    pub fn filter_pid(&mut self, pid: i32) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_filter_pid(self.config, pid) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Enables retrieving only those logs that match a given filter.
    ///
    /// # Arguments
    ///
    /// * `query` - The filter query. For syntax, see dlogutil's --help.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - Invalid syntax of the filterspec.
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    /// * [`std::io::ErrorKind::OutOfMemory`] - Out of memory.
    pub fn filter_filterspec(&mut self, query: &str) -> Result<()> {
        let query = format!("{}\0", query);
        let query_c =
            CStr::from_bytes_with_nul(query.as_bytes()).map_err(|_| ErrorKind::InvalidInput)?;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // The string is defined in a verified way above.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_filter_filterspec(self.config, query_c.as_ptr()) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Disables log sorting for given log retrieval request.
    ///
    /// Logs are still received in some order that is usually largely sorted,
    /// but if sorting is disabled logutil-side there may be cases where a log
    /// with a later timestamp is in front of a log with an earlier one.
    /// The use case here is performance, since the failure case above is rare.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn sorting_disable(&mut self) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_sorting_disable(self.config) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Enables log sorting for given log retrieval request.
    ///
    /// This is the default and generally makes sure that logs are in order,
    /// but has a modest performance cost.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn sorting_enable(&mut self) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_sorting_enable(self.config) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Enables sorting, choosing the sort buffer size manually.
    ///
    /// The count parameter influences the quality of sorting, but also memory usage.
    /// This version is a somewhat lower level version of [`sorting_enable`].
    /// For more information on sorting quality, refer to the C library documentation.
    ///
    /// [`sorting_enable`]: DlogutilConfig::sorting_enable
    ///
    /// # Arguments
    ///
    /// * `entry_count` - How many logs to keep at a given time. At least 1.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - Zero size.
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn sorting_enable_with_size(&mut self, entry_count: u32) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_sorting_enable_with_size(self.config, entry_count) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Chooses a timestamp type by which returned logs are sorted by.
    ///
    /// If the chosen timestamp is missing in the logs, currently they will not
    /// be sorted at all. This should, however, still become a reasonable order,
    /// since logs are usually stored sorted by one of timestamps. If only some
    /// logs are missing the chosen timestamp, ordering of the logs is undefined.
    /// See [`buffer_get_default_ts_type`] for the default.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn order_set(&mut self, sort_by: SortingOrder) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_order_set(self.config, sort_by.into()) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Adds a buffer whence logs will be taken to a request.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn buffer_add(&mut self, buf: BufferId) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_buffer_add(self.config, buf.into()) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Set log retrieval mode to retrieving all the logs since the start of
    /// the system without an end.
    ///
    /// This is similar to `dlogutil` in a default mode.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn mode_set_continuous(&mut self) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_mode_set_continuous(self.config) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Set log retrieval mode to retrieving all the logs since the call
    /// without an end.
    ///
    /// This is similar to `dlogutil -m`.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn mode_set_monitor(&mut self) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_mode_set_monitor(self.config) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Set log retrieval mode to dumping all the logs since the start of the
    /// system until the call (possibly a specified amount of the most recent
    /// of them instead).
    ///
    /// This is similar to `dlogutil -d`. After dumping all the logs,
    /// [`get_log`] will signal this by returning `TIZEN_ERROR_NO_DATA`.
    ///
    /// [`get_log`]: DlogutilState::get_log
    ///
    /// # Arguments
    ///
    /// * `entry_count` - Number of logs to be returned. It can be
    ///   `DLOGUTIL_MAX_DUMP_SIZE`, in which case all the logs will be dumped.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn mode_set_dump(&mut self, entry_count: u32) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_config_mode_set_dump(self.config, entry_count) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Set log retrieval mode to dumping compressed historical logs.
    ///
    /// This is similar to `cat /var/log/dlog/xyz`. After dumping all the logs,
    /// [`get_log`] will signal this by returning `TIZEN_ERROR_NO_DATA`.
    ///
    /// [`get_log`]: DlogutilState::get_log
    ///
    /// # Arguments
    ///
    /// * `compress_buffer` - The name of the compression storage entry.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::OutOfMemory`] - Not enough memory. Parameters left unchanged.
    /// * [`std::io::ErrorKind::InvalidInput`] - The pointer was NULL.
    pub fn mode_set_compressed_memory_dump(&mut self, compress_buffer: &str) -> Result<()> {
        let compress_buffer = format!("{}\0", compress_buffer);
        let compress_buffer_c = CStr::from_bytes_with_nul(compress_buffer.as_bytes())
            .map_err(|_| ErrorKind::InvalidInput)?;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // The string is defined in a verified way above.
        // Since the function takes &mut, no other references can exist.
        match unsafe {
            dlogutil_config_mode_set_compressed_memory_dump(self.config, compress_buffer_c.as_ptr())
        } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }
}

impl DlogutilState {
    /// Finalizes the config into a state struct by connecting to buffers.
    ///
    /// An application having platform privilege level can read platform log
    /// data by declaring <http://tizen.org/privilege/log>.
    ///
    /// # Returns
    ///
    /// * `Ok(Self)` - A handle to the state struct.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::InvalidInput`] - No buffers selected.
    /// * [`std::io::ErrorKind::Uncategorized`] - Unsupported buffer set (KMSG + non-KMSG).
    /// * [`std::io::ErrorKind::Uncategorized`] - Unsupported backend (zero-copy).
    /// * [`std::io::ErrorKind::Uncategorized`] - No buffers were opened (incl. due to null backend).
    /// * [`std::io::ErrorKind::Uncategorized`] - Couldn't read config file.
    /// * [`std::io::ErrorKind::Uncategorized`] - Couldn't contact log backend.
    /// * [`std::io::ErrorKind::OutOfMemory`] - There's not enough memory.
    pub fn create(config: DlogutilConfig) -> Result<Self> {
        let mut state = DlogutilState { state: null_mut() };
        // SAFETY: the config pointer is guaranteed to be correct by the way it's created.
        // Otherwise, the function is safe by itself. It returns either a valid pointer,
        // in which case we return Ok, or NULL, in which case we return Err, which
        // means that if Self is created, the inner pointer is guaranteed to be correct.
        match unsafe { dlogutil_config_connect(config.config, &mut state.state) } {
            0 => Ok(state),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Retrieves a single log according to a dump request.
    ///
    /// Returns a returned log as a [`DlogutilEntry`] struct.
    ///
    /// If the calling process doesn't have `CAP_SYSLOG` and is not in the
    /// log group, you will only get some of the logs.
    /// Also, you must set the mode (`DlogutilConfig::mode_set_*`).
    ///
    /// [`DlogutilEntry`]: DlogutilEntry
    ///
    /// # Arguments
    ///
    /// * `timeout` - How many miliseconds to wait for the log.
    ///   The actual runtime of the call can obviously be slightly longer than
    ///   this argument. 0 means don't wait, None means wait indefinitely.
    ///
    /// # Returns
    ///
    /// * `Ok(DlogutilEntry)` - A returned log.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::Uncategorized`] - Timeout exceeded.
    /// * [`std::io::ErrorKind::Uncategorized`] - In dump mode, no more logs remaining.
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::InvalidInput`] - State not in log-getting mode.
    /// * [`std::io::ErrorKind::OutOfMemory`] - There's not enough memory.
    /// * [`std::io::ErrorKind::Uncategorized`] - Couldn't fulfill request.
    pub fn get_log(&mut self, timeout: Option<Duration>) -> Result<DlogutilEntry> {
        let mut entry_out = DlogutilEntry { entry: null_mut() };
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // The C function passes the ownership of the entry to the pointer iff the
        // return value is nonzero, which is exactly the case in which we return
        // the struct in an Ok. Since the function takes &mut, no other references can exist.
        match unsafe {
            dlogutil_get_log(
                self.state,
                timeout.map_or(-1, |t| t.as_millis() as i32),
                &mut entry_out.entry,
            )
        } {
            0 => Ok(entry_out),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Irreversibly clears a log buffer from any logs inside.
    ///
    /// Either `CAP_SYSLOG` or being in the log group is required.
    /// Also, you can't use one of the log-getting modes
    /// (`DlogutilConfig::mode_set_*`).
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - Couldn't fulfill request.
    pub fn buffer_clear(&mut self, buffer: BufferId) -> Result<()> {
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Since the function takes &mut, no other references can exist.
        match unsafe { dlogutil_buffer_clear(self.state, buffer.into()) } {
            0 => Ok(()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Gets the data storage capacity of a log buffer in bytes.
    ///
    /// Either `CAP_SYSLOG` or being in the log group is required.
    /// Also, you can't use one of the log-getting modes
    /// (`DlogutilConfig::mode_set_*`).
    ///
    /// # Returns
    ///
    /// * `Ok(u32)` - The buffer's maximum capacity in bytes.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - State in log-getting mode.
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - Couldn't fulfill request.
    pub fn buffer_get_capacity(&mut self, buffer: BufferId) -> Result<u32> {
        let mut capacity = 0;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Note that it is not correct for this function to take a shared reference, as in case of
        // the pipe backend, the function will write to a pipe and expect to read an answer
        // immediately.
        match unsafe { dlogutil_buffer_get_capacity(self.state, buffer.into(), &mut capacity) } {
            0 => Ok(capacity),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Gets the storage data usage of a log buffer in bytes.
    ///
    /// Either `CAP_SYSLOG` or being in the log group is required.
    /// Also, you can't use one of the log-getting modes
    /// (`DlogutilConfig::mode_set_*`).
    ///
    /// # Returns
    ///
    /// * `Ok(u32)` - Buffer's current usage in bytes.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - State in log-getting mode.
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - Couldn't fulfill request.
    pub fn buffer_get_usage(&mut self, buffer: BufferId) -> Result<u32> {
        let mut usage = 0;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // Note that it is not correct for this function to take a shared reference, as in case of
        // the pipe backend, the function will write to a pipe and expect to read an answer
        // immediately.
        match unsafe { dlogutil_buffer_get_usage(self.state, buffer.into(), &mut usage) } {
            0 => Ok(usage),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Gets the buffer aliasing (same storage) information.
    ///
    /// Sometimes, multiple buffers will be backed by a single log storage
    /// (for example, by the same kernel device). In such cases, the storage
    /// will only be opened once.
    /// This function allows you to see whether this is the case.
    ///
    /// # Returns
    ///
    /// * `Ok(BufferId)` - Buffer aliasing information.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    pub fn buffer_get_alias(&self, buffer: BufferId) -> Result<BufferId> {
        let mut real_buffer = log_id_t::LOG_ID_MAIN;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // This method only reads from the memory of the object, so it is ok for it to take
        // a shared reference.
        match unsafe { dlogutil_buffer_get_alias(self.state, buffer.into(), &mut real_buffer) } {
            0 => Ok(real_buffer.into()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }
}

impl DlogutilEntry {
    /// Retrieves the timestamp of given type from the log entry.
    ///
    /// The information about timestamp availability can be retrieved using the
    /// [`buffer_check_ts_type_available`] function.
    ///
    /// # Returns
    ///
    /// * `Ok(Duration)` - Timestamp of the entry as a [`Duration`] struct.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// [`Duration`]: std::time::Duration
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - Invalid value of order.
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - The timestamp is missing.
    pub fn get_timestamp(&self, stamp_type: SortingOrder) -> Result<Duration> {
        let mut ts = timespec {
            tv_sec: 0,
            tv_nsec: 0,
        };
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // This method only reads from the memory of the object, so it is ok for it to take
        // a shared reference.
        match unsafe { dlogutil_entry_get_timestamp(self.entry, stamp_type.into(), &mut ts) } {
            0 => Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Retrieves the TID (thread identificator) of the log sender.
    ///
    /// If [`LogIdKmsg`] is used as the buffer, this function will always return
    /// `TIZEN_ERROR_NO_DATA`.
    /// This is because the KMSG buffer contains no TID information.
    ///
    /// [`LogIdKmsg`]: BufferId::LogIdKmsg
    ///
    /// # Returns
    ///
    /// * `Ok(i32)` - TID of the log sender.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - TID is missing or not applicable.
    pub fn get_tid(&self) -> Result<i32> {
        let mut tid = 0;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // This method only reads from the memory of the object, so it is ok for it to take
        // a shared reference.
        match unsafe { dlogutil_entry_get_tid(self.entry, &mut tid) } {
            0 => Ok(tid),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Retrieves the PID (process identificator) of the log sender.
    ///
    /// If [`LogIdKmsg`] is used as the buffer, this function will always return
    /// `TIZEN_ERROR_NO_DATA`.
    /// This is because the KMSG buffer contains no PID information.
    ///
    /// [`LogIdKmsg`]: BufferId::LogIdKmsg
    ///
    /// # Returns
    ///
    /// * `Ok(i32)` - PID of the log sender.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - PID is missing or not applicable.
    pub fn get_pid(&self) -> Result<i32> {
        let mut pid = 0;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // This method only reads from the memory of the object, so it is ok for it to take
        // a shared reference.
        match unsafe { dlogutil_entry_get_pid(self.entry, &mut pid) } {
            0 => Ok(pid),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Retrieves the priority level metadata of the log entry.
    ///
    /// # Returns
    ///
    /// * `Ok(Priority)` - Log priority.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - The priority is missing.
    pub fn get_priority(&self) -> Result<Priority> {
        let mut prio = log_priority::DLOG_UNKNOWN;
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // This method only reads from the memory of the object, so it is ok for it to take
        // a shared reference.
        match unsafe { dlogutil_entry_get_priority(self.entry, &mut prio) } {
            0 => Ok(prio.into()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }

    /// Retrieves the tag (arbitrary label) of the log entry.
    ///
    /// In some rare cases the entry may be malformed and the tag
    /// may turn out to be unavailable.
    ///
    /// In such cases, an empty string may be returned instead.
    ///
    /// # Returns
    ///
    /// * `Ok(&str)` - Log tag.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - The tag is missing.
    pub fn get_tag(&self) -> Result<&str> {
        let mut tag_ptr = null();
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // The function returns either NULL, in which case we return Err,
        // or a string with same lifetime as self.entry, in which case we can safely
        // call CStr::from_ptr and return it in an Ok.
        // This method only reads from the memory of the object, so it is ok for it to take
        // a shared reference.
        unsafe {
            match dlogutil_entry_get_tag(self.entry, &mut tag_ptr) {
                0 => Ok(CStr::from_ptr(tag_ptr).to_str().unwrap()),
                e => Err(Error::from_raw_os_error(-e)),
            }
        }
    }

    /// Retrieves the message (without any metadata) of the log entry.
    ///
    /// In some rare cases the entry may be malformed and the message
    /// may turn out to be unavailable.
    ///
    /// In such cases, an empty string may be returned instead.
    ///
    /// # Returns
    ///
    /// * `Ok(&str)` - Log message.
    /// * `Err(std::io::Error)` - An error.
    ///
    /// # Errors
    ///
    /// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
    /// * [`std::io::ErrorKind::Uncategorized`] - The message is missing.
    pub fn get_message(&self) -> Result<&str> {
        let mut msg_ptr = null();
        // SAFETY: the pointer is guaranteed to be correct by the way it's created.
        // The function returns either NULL, in which case we return Err,
        // or a string with same lifetime as self.entry, in which case we can safely
        // call CStr::from_ptr and return it in an Ok.
        // This method only reads from the memory of the object, so it is ok for it to take
        // a shared reference.
        unsafe {
            match dlogutil_entry_get_message(self.entry, &mut msg_ptr) {
                0 => Ok(CStr::from_ptr(msg_ptr).to_str().unwrap()),
                e => Err(Error::from_raw_os_error(-e)),
            }
        }
    }
}

/// Gets the human-readable, constant name of a buffer.
///
/// # Returns
///
/// * `Ok(&str)` - The name of the passed buffer.
/// * `Err(std::io::Error)` - An error.
///
/// # Errors
///
/// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
pub fn buffer_get_name(buffer: BufferId) -> Result<&'static str> {
    let mut name_ptr = null();
    // SAFETY: the function is safe by itself provided the target is a valid pointer.
    // The function returns either NULL, in which case we return Err,
    // or a string with static lifetime, in which case we can safely call
    // CStr::from_ptr and return it in an Ok.
    unsafe {
        match dlogutil_buffer_get_name(buffer.into(), &mut name_ptr) {
            0 => Ok(CStr::from_ptr(name_ptr).to_str().unwrap()),
            e => Err(Error::from_raw_os_error(-e)),
        }
    }
}

/// Gets the default sorting timestamp type of a buffer.
///
/// This is the timestamp type that will be used for sorting by default.
/// We assume that it is always available and the input is sorted by it.
///
/// # Returns
///
/// * `Ok(SortingOrder)` - The default timestamp type of the passed buffer.
/// * `Err(std::io::Error)` - An error.
///
/// # Errors
///
/// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
/// * [`std::io::ErrorKind::Uncategorized`] - Couldn't read config file.
pub fn buffer_get_default_ts_type(buffer: BufferId) -> Result<SortingOrder> {
    let mut typ = dlogutil_sorting_order::DLOGUTIL_SORT_SENT_MONO;
    // SAFETY: the function is safe by itself provided the target is a valid pointer.
    match unsafe { dlogutil_buffer_get_default_ts_type(buffer.into(), &mut typ) } {
        0 => Ok(typ.into()),
        e => Err(Error::from_raw_os_error(-e)),
    }
}

/// Checks if a buffer contains timestamps of a given type.
///
/// If false is returned, the timestamp may still be available in some of the logs.
/// However, if true is returned, the timestamp will always be available.
/// You can check the timestamp availability per log using the [`get_timestamp`]
/// function.
///
/// [`get_timestamp`]: DlogutilEntry::get_timestamp
///
/// # Returns
///
/// * `Ok(bool)` - Whether the given timestamp type is guaranteed to be available.
/// * `Err(std::io::Error)` - An error.
///
/// # Errors
///
/// * [`std::io::ErrorKind::InvalidInput`] - More than one buffer.
/// * [`std::io::ErrorKind::InvalidInput`] - One of the pointers was NULL.
/// * [`std::io::ErrorKind::Uncategorized`] - Couldn't read config file.
pub fn buffer_check_ts_type_available(buffer: BufferId, stamp_type: SortingOrder) -> Result<bool> {
    let mut available = false;
    // SAFETY: the function is safe by itself provided the target is a valid pointer.
    match unsafe {
        dlogutil_buffer_check_ts_type_available(buffer.into(), stamp_type.into(), &mut available)
    } {
        0 => Ok(available),
        e => Err(Error::from_raw_os_error(-e)),
    }
}