acct/
lib.rs

1//! This is documentation for the `acct` crate.
2//!
3//! The acct crate is meant to be used for handling and
4//! processing the acct(5) file generated by UNIX process
5//! accounting.
6
7#[macro_use]
8extern crate serde_derive;
9extern crate bincode;
10extern crate libc;
11
12use bincode::{deserialize, serialize};
13use libc::{getpwuid, passwd};
14use std::ffi::CStr;
15use std::io::Read;
16use std::string::String;
17use std::time::{Duration, SystemTime, UNIX_EPOCH};
18use std::{fmt, mem, result};
19use std::ops::Deref;
20
21const AFORK: u8 = 0x01;
22const ASU: u8 = 0x02;
23const ACORE: u8 = 0x08;
24const AXSIG: u8 = 0x10;
25
26pub type Result<T> = result::Result<T, Error>;
27
28#[derive(Debug)]
29pub enum Error {
30    InvalidFile,
31    BadReader,
32    Er,
33}
34
35impl From<std::string::FromUtf8Error> for Error {
36    fn from(_: std::string::FromUtf8Error) -> Error {
37        Error::Er
38    }
39}
40
41impl From<std::ffi::OsString> for Error {
42    fn from(_: std::ffi::OsString) -> Error {
43        Error::Er
44    }
45}
46
47impl From<std::io::Error> for Error {
48    fn from(_: std::io::Error) -> Error {
49        Error::BadReader
50    }
51}
52
53impl From<std::boxed::Box<bincode::ErrorKind>> for Error {
54    fn from(_: std::boxed::Box<bincode::ErrorKind>) -> Error {
55        Error::BadReader
56    }
57}
58
59impl fmt::Display for Error {
60    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        match *self {
62            Error::InvalidFile => write!(f, "Invalid file"),
63            Error::BadReader => write!(f, "Invalid reader"),
64            Error::Er => write!(f, "Invalid data"),
65        }
66    }
67}
68
69#[derive(Serialize, Deserialize, PartialEq, Debug)]
70struct AcctV3Inner {
71    ac_flag: u8,
72    ac_version: u8,
73    ac_tty: u16,
74    ac_exitcode: u32,
75    ac_uid: u32,
76    ac_gid: u32,
77    ac_pid: u32,
78    ac_ppid: u32,
79    ac_btime: u32,
80    ac_etime: f32,
81    ac_utime: u16,
82    ac_stime: u16,
83    ac_mem: u16,
84    ac_io: u16,
85    ac_rw: u16,
86    ac_minflt: u16,
87    ac_majflt: u16,
88    ac_swaps: u16,
89    ac_comm: [u8; 16],
90}
91
92impl AcctV3Inner {
93    fn load_from_slice(buf: &[u8]) -> Result<AcctV3Inner> {
94        let acct: AcctV3Inner = deserialize(buf)?;
95
96        Ok(acct)
97    }
98
99    fn command(&self) -> Result<String> {
100        let res = String::from_utf8(self.ac_comm.to_vec())?;
101        Ok(res)
102    }
103
104    fn is_valid(&self) -> bool {
105        self.ac_version == 3
106    }
107}
108
109/// Represents a acct(5) v3 record structure
110///
111/// see https://linux.die.net/man/5/acct
112#[derive(Debug)]
113pub struct AcctV3 {
114    inner: AcctV3Inner,
115    /// The accounting username
116    pub username: String,
117    /// The command name of executed command
118    pub command: String,
119    /// The time the command was created
120    pub creation_time: SystemTime,
121}
122
123impl AcctV3 {
124    /// Constructs a AcctV3 object from a byte slice
125    pub fn from_slice(buf: &[u8]) -> Result<AcctV3> {
126        let inner = AcctV3Inner::load_from_slice(buf)?;
127        let command = inner.command()?;
128        // Safe because AcctV3Inner::load_from_slice() guarantees valid string
129        let pw: passwd = unsafe { *getpwuid(inner.ac_uid) };
130        let username = unsafe { CStr::from_ptr(pw.pw_name) };
131        let username = username.to_str().unwrap().to_string();
132
133        let ctime = inner.ac_btime as u64;
134        let creation_time = UNIX_EPOCH + Duration::from_secs(ctime);
135
136        Ok(AcctV3 {
137            inner: inner,
138            command: command,
139            username: username,
140            creation_time: creation_time,
141        })
142    }
143
144    fn is_valid(&self) -> bool {
145        self.inner.is_valid()
146    }
147
148    /// Shows if command was forked
149    pub fn was_forked(&self) -> bool {
150        self.inner.ac_flag & AFORK == AFORK
151    }
152
153    /// Shows if the command's user was root
154    pub fn was_super_user(&self) -> bool {
155        self.inner.ac_flag & ASU == ASU
156    }
157
158    /// Shows if the command produced a core dump
159    pub fn was_core_dumped(&self) -> bool {
160        self.inner.ac_flag & ACORE == ACORE
161    }
162
163    /// Shows if the command was killed via a signal
164    pub fn was_killed(&self) -> bool {
165        self.inner.ac_flag & AXSIG == AXSIG
166    }
167}
168
169/// Represents an acct(5) log file.
170pub struct AcctFile {
171    /// Vector of acct records
172    records: Vec<AcctV3>,
173}
174
175impl AcctFile {
176    fn is_valid(buf: &[u8]) -> bool {
177        buf.len() % mem::size_of::<AcctV3Inner>() == 0
178    }
179
180    /// Construct a new AcctFile struct from a Reader
181    pub fn new<R: Read + ?Sized>(reader: &mut R) -> Result<AcctFile> {
182        let size = mem::size_of::<AcctV3Inner>();
183        let mut all: Vec<AcctV3> = Vec::new();
184        let mut buf: Vec<u8> = Vec::new();
185        reader.read_to_end(&mut buf)?;
186
187        if !AcctFile::is_valid(&buf) {
188            return Err(Error::Er);
189        }
190
191        for chunk in (0..buf.len()).step_by(size) {
192            let acct = AcctV3::from_slice(&buf[chunk..chunk + size])?;
193            if acct.is_valid() {
194                all.push(acct);
195            }
196        }
197
198        Ok(AcctFile { records: all })
199    }
200
201    /// Convert the AcctFile object into bytes for writing back into file.
202    /// Consumes the object.
203    pub fn into_bytes(self) -> Result<Vec<u8>> {
204        let mut all_bytes: Vec<u8> = Vec::new();
205        for acct in self.records {
206            let mut buf = serialize(&acct.inner)?;
207            all_bytes.append(&mut buf);
208        }
209
210        Ok(all_bytes)
211    }
212}
213
214impl Deref for AcctFile {
215    type Target = Vec<AcctV3>;
216
217    fn deref(&self) -> &Self::Target {
218        &self.records
219    }
220}
221
222pub fn expand_time(time: u16) -> u16 {
223    let ret: u16 = (time & 0x1fff) << (((time >> 13) & 0x7) * 3);
224
225    ret
226}