#[macro_use]
extern crate serde_derive;
extern crate bincode;
extern crate libc;
use bincode::{deserialize, serialize};
use libc::{getpwuid, passwd};
use std::ffi::CStr;
use std::io::Read;
use std::string::String;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{fmt, mem, result};
use std::ops::Deref;
const AFORK: u8 = 0x01;
const ASU: u8 = 0x02;
const ACORE: u8 = 0x08;
const AXSIG: u8 = 0x10;
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
InvalidFile,
BadReader,
Er,
}
impl From<std::string::FromUtf8Error> for Error {
fn from(_: std::string::FromUtf8Error) -> Error {
Error::Er
}
}
impl From<std::ffi::OsString> for Error {
fn from(_: std::ffi::OsString) -> Error {
Error::Er
}
}
impl From<std::io::Error> for Error {
fn from(_: std::io::Error) -> Error {
Error::BadReader
}
}
impl From<std::boxed::Box<bincode::ErrorKind>> for Error {
fn from(_: std::boxed::Box<bincode::ErrorKind>) -> Error {
Error::BadReader
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::InvalidFile => write!(f, "Invalid file"),
Error::BadReader => write!(f, "Invalid reader"),
Error::Er => write!(f, "Invalid data"),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct AcctV3Inner {
ac_flag: u8,
ac_version: u8,
ac_tty: u16,
ac_exitcode: u32,
ac_uid: u32,
ac_gid: u32,
ac_pid: u32,
ac_ppid: u32,
ac_btime: u32,
ac_etime: f32,
ac_utime: u16,
ac_stime: u16,
ac_mem: u16,
ac_io: u16,
ac_rw: u16,
ac_minflt: u16,
ac_majflt: u16,
ac_swaps: u16,
ac_comm: [u8; 16],
}
impl AcctV3Inner {
fn load_from_slice(buf: &[u8]) -> Result<AcctV3Inner> {
let acct: AcctV3Inner = deserialize(buf)?;
Ok(acct)
}
fn command(&self) -> Result<String> {
let res = String::from_utf8(self.ac_comm.to_vec())?;
Ok(res)
}
fn is_valid(&self) -> bool {
self.ac_version == 3
}
}
#[derive(Debug)]
pub struct AcctV3 {
inner: AcctV3Inner,
pub username: String,
pub command: String,
pub creation_time: SystemTime,
}
impl AcctV3 {
pub fn from_slice(buf: &[u8]) -> Result<AcctV3> {
let inner = AcctV3Inner::load_from_slice(buf)?;
let command = inner.command()?;
let pw: passwd = unsafe { *getpwuid(inner.ac_uid) };
let username = unsafe { CStr::from_ptr(pw.pw_name) };
let username = username.to_str().unwrap().to_string();
let ctime = inner.ac_btime as u64;
let creation_time = UNIX_EPOCH + Duration::from_secs(ctime);
Ok(AcctV3 {
inner: inner,
command: command,
username: username,
creation_time: creation_time,
})
}
fn is_valid(&self) -> bool {
self.inner.is_valid()
}
pub fn was_forked(&self) -> bool {
self.inner.ac_flag & AFORK == AFORK
}
pub fn was_super_user(&self) -> bool {
self.inner.ac_flag & ASU == ASU
}
pub fn was_core_dumped(&self) -> bool {
self.inner.ac_flag & ACORE == ACORE
}
pub fn was_killed(&self) -> bool {
self.inner.ac_flag & AXSIG == AXSIG
}
}
pub struct AcctFile {
records: Vec<AcctV3>,
}
impl AcctFile {
fn is_valid(buf: &[u8]) -> bool {
buf.len() % mem::size_of::<AcctV3Inner>() == 0
}
pub fn new<R: Read + ?Sized>(reader: &mut R) -> Result<AcctFile> {
let size = mem::size_of::<AcctV3Inner>();
let mut all: Vec<AcctV3> = Vec::new();
let mut buf: Vec<u8> = Vec::new();
reader.read_to_end(&mut buf)?;
if !AcctFile::is_valid(&buf) {
return Err(Error::Er);
}
for chunk in (0..buf.len()).step_by(size) {
let acct = AcctV3::from_slice(&buf[chunk..chunk + size])?;
if acct.is_valid() {
all.push(acct);
}
}
Ok(AcctFile { records: all })
}
pub fn into_bytes(self) -> Result<Vec<u8>> {
let mut all_bytes: Vec<u8> = Vec::new();
for acct in self.records {
let mut buf = serialize(&acct.inner)?;
all_bytes.append(&mut buf);
}
Ok(all_bytes)
}
}
impl Deref for AcctFile {
type Target = Vec<AcctV3>;
fn deref(&self) -> &Self::Target {
&self.records
}
}
pub fn expand_time(time: u16) -> u16 {
let ret: u16 = (time & 0x1fff) << (((time >> 13) & 0x7) * 3);
ret
}