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
#![allow(non_camel_case_types)]
use std::ffi::CStr;
use std::fmt;
use std::os::raw::c_short;
use zerocopy::FromBytes;
pub const EMPTY: c_short = 0;
pub const RUN_LVL: c_short = 1;
pub const BOOT_TIME: c_short = 2;
pub const NEW_TIME: c_short = 3;
pub const OLD_TIME: c_short = 4;
pub const INIT_PROCESS: c_short = 5;
pub const LOGIN_PROCESS: c_short = 6;
pub const USER_PROCESS: c_short = 7;
pub const DEAD_PROCESS: c_short = 8;
pub const ACCOUNTING: c_short = 9;
pub const UT_LINESIZE: usize = 32;
pub const UT_NAMESIZE: usize = 32;
pub const UT_HOSTSIZE: usize = 256;
#[repr(C)]
#[derive(Clone, Copy, Debug, FromBytes)]
pub struct exit_status {
pub e_termination: c_short,
pub e_exit: c_short,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, FromBytes)]
pub struct timeval {
pub tv_sec: i32,
pub tv_usec: i32,
}
#[repr(C)]
#[derive(Clone, Copy, FromBytes)]
pub struct utmp {
pub ut_type: c_short,
pub ut_pid: libc::pid_t,
pub ut_line: [u8; UT_LINESIZE],
pub ut_id: [u8; 4],
pub ut_user: [u8; UT_NAMESIZE],
pub ut_host: [u8; UT_HOSTSIZE],
pub ut_exit: exit_status,
pub ut_session: i32,
pub ut_tv: timeval,
pub ut_addr_v6: [i32; 4],
pub __unused: [u8; 20],
}
impl fmt::Debug for utmp {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("utmp")
.field("ut_type", &self.ut_type)
.field("ut_pid", &self.ut_pid)
.field("ut_line", &cstr_from_bytes(&self.ut_line))
.field("ut_id", &self.ut_id)
.field("ut_user", &cstr_from_bytes(&self.ut_user))
.field("ut_host", &cstr_from_bytes(&self.ut_host))
.field("ut_exit", &self.ut_exit)
.field("ut_session", &self.ut_session)
.field("ut_tv", &self.ut_tv)
.field("ut_addr_v6", &self.ut_addr_v6)
.field("__unused", &self.__unused)
.finish()
}
}
fn cstr_from_bytes(bytes: &[u8]) -> &CStr {
match bytes.iter().position(|b| *b == 0) {
Some(pos) => unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[..=pos]) },
None => unsafe { CStr::from_bytes_with_nul_unchecked("???\0".as_bytes()) },
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
#[test]
fn test_size_of() {
assert_eq!(mem::size_of::<utmp>(), 384);
}
}