use std::collections::HashMap;
use std::ffi::CStr;
use std::fs;
use std::mem;
use std::str::FromStr;
use lazy_static::lazy_static;
use regex::{Captures, Regex};
use semver::{Version, VersionReq};
lazy_static! {
pub static ref KERNEL_VERSION: String = kernel_version();
pub static ref SEMVER_KERNEL_VERSION: &'static str = semver_kernel_version();
pub static ref HAVE_INVALIDATE: bool = have_invalidate();
pub static ref PAGE_SIZE: usize = page_size();
pub static ref UID: libc::uid_t = getuid();
pub static ref GID: libc::gid_t = getgid();
pub static ref KEY_INFO: KeyQuota = key_user_info();
}
fn kernel_version() -> String {
let mut utsname = unsafe { mem::zeroed() };
let ret = unsafe { libc::uname(&mut utsname) };
if ret < 0 {
panic!("failed to query the kernel version: {}", errno::errno());
}
let cstr = unsafe { CStr::from_ptr(utsname.release.as_ptr()) };
cstr.to_str()
.expect("kernel version should be ASCII")
.into()
}
fn semver_kernel_version() -> &'static str {
match (*KERNEL_VERSION).find('-') {
Some(pos) => &(*KERNEL_VERSION)[..pos],
None => &KERNEL_VERSION,
}
}
fn have_invalidate() -> bool {
match Version::parse(*SEMVER_KERNEL_VERSION) {
Ok(ver) => {
let minver = VersionReq::parse(">=3.5").unwrap();
minver.matches(&ver)
},
Err(err) => {
eprintln!(
"failed to parse kernel version `{}` ({}): assuming incompatibility",
*SEMVER_KERNEL_VERSION, err
);
false
},
}
}
fn page_size() -> usize {
errno::set_errno(errno::Errno(0));
let ret = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
if ret < 0 {
let err = errno::errno();
if err.0 == 0 {
panic!("page size is indeterminite?");
} else {
panic!("failed to query the page size: {}", errno::errno());
}
}
ret as usize
}
const KEY_USERS_FILE: &str = "/proc/key-users";
lazy_static! {
static ref KEY_USERS: Regex = Regex::new(
" *(?P<uid>\\d+): +\
(?P<usage>\\d+) \
(?P<nkeys>\\d+)/(?P<nikeys>\\d+) \
(?P<qnkeys>\\d+)/(?P<maxkeys>\\d+) \
(?P<qnbytes>\\d+)/(?P<maxbytes>\\d+)"
)
.unwrap();
}
fn by_name<T>(capture: &Captures, name: &str) -> T
where
T: FromStr,
T::Err: std::fmt::Display,
{
let cap = capture
.name(name)
.expect("name should be captured")
.as_str();
match cap.parse() {
Ok(v) => v,
Err(err) => panic!("failed to parse {} as an integer: {}", name, err),
}
}
#[derive(Debug, Clone, Copy)]
pub struct KeyQuota {
pub usage: usize,
pub nkeys: usize,
pub nikeys: usize,
pub qnkeys: usize,
pub maxkeys: usize,
pub qnbytes: usize,
pub maxbytes: usize,
}
fn all_key_user_info() -> HashMap<libc::uid_t, KeyQuota> {
let data = String::from_utf8(fs::read(KEY_USERS_FILE).unwrap()).unwrap();
(*KEY_USERS)
.captures_iter(&data)
.map(|capture| {
let uid = by_name(&capture, "uid");
let usage = by_name(&capture, "usage");
let nkeys = by_name(&capture, "nkeys");
let nikeys = by_name(&capture, "nikeys");
let qnkeys = by_name(&capture, "qnkeys");
let maxkeys = by_name(&capture, "maxkeys");
let qnbytes = by_name(&capture, "qnbytes");
let maxbytes = by_name(&capture, "maxbytes");
(
uid,
KeyQuota {
usage,
nkeys,
nikeys,
qnkeys,
maxkeys,
qnbytes,
maxbytes,
},
)
})
.collect()
}
fn key_user_info() -> KeyQuota {
let uid = unsafe { libc::getuid() };
*all_key_user_info()
.get(&uid)
.expect("the current user has no keys?")
}
fn getuid() -> libc::uid_t {
unsafe { libc::getuid() }
}
fn getgid() -> libc::gid_t {
unsafe { libc::getgid() }
}