diff --git a/src/server/upid.rs b/src/server/upid.rs index c27ccdf5..969ea371 100644 --- a/src/server/upid.rs +++ b/src/server/upid.rs @@ -62,7 +62,7 @@ impl UPID { Ok(UPID { pid, - pstart: tools::procfs::read_proc_starttime(pid)?, + pstart: proxmox::sys::linux::procfs::read_proc_starttime(pid)?, starttime: Local::now().timestamp(), task_id, worker_type: worker_type.to_owned(), diff --git a/src/server/worker_task.rs b/src/server/worker_task.rs index df1e71e0..a31b0f34 100644 --- a/src/server/worker_task.rs +++ b/src/server/worker_task.rs @@ -36,7 +36,7 @@ lazy_static! { static ref WORKER_TASK_LIST: Mutex>> = Mutex::new(HashMap::new()); static ref MY_PID: i32 = unsafe { libc::getpid() }; - static ref MY_PID_PSTART: u64 = crate::tools::procfs::read_proc_pid_stat(*MY_PID).unwrap().starttime; + static ref MY_PID_PSTART: u64 = proxmox::sys::linux::procfs::read_proc_pid_stat(*MY_PID).unwrap().starttime; } /// Test if the task is still running @@ -49,7 +49,7 @@ pub fn worker_is_active(upid: &UPID) -> bool { false } } else { - match crate::tools::procfs::check_process_running_pstart(upid.pid, upid.pstart) { + match proxmox::sys::linux::procfs::check_process_running_pstart(upid.pid, upid.pstart) { Some(_) => true, _ => false, } diff --git a/src/tools.rs b/src/tools.rs index 520d0d19..fcd11e31 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -30,7 +30,6 @@ pub mod fs; pub mod tty; pub mod signalfd; pub mod daemon; -pub mod procfs; pub mod acl; pub mod xattr; pub mod futures; diff --git a/src/tools/procfs.rs b/src/tools/procfs.rs deleted file mode 100644 index 655a4897..00000000 --- a/src/tools/procfs.rs +++ /dev/null @@ -1,422 +0,0 @@ -use failure::*; - -use std::u32; -use std::fs::OpenOptions; -use std::io::{BufRead, BufReader}; -use std::collections::HashSet; -use std::process; -use std::net::{Ipv4Addr, Ipv6Addr}; -use proxmox::tools::fs::file_read_firstline; -use lazy_static::lazy_static; -use regex::Regex; -use libc; - -/// POSIX sysconf call -pub fn sysconf(name: i32) -> i64 { - extern { fn sysconf(name: i32) -> i64; } - unsafe { sysconf(name) } -} - -lazy_static! { - static ref CLOCK_TICKS: f64 = sysconf(libc::_SC_CLK_TCK) as f64; -} - -pub struct ProcFsPidStat { - pub status: u8, - pub utime: u64, - pub stime: u64, - pub starttime: u64, - pub vsize: u64, - pub rss: i64, -} - -pub fn read_proc_pid_stat(pid: libc::pid_t) -> Result { - - let statstr = file_read_firstline(format!("/proc/{}/stat", pid))?; - - lazy_static! { - static ref REGEX: Regex = Regex::new(concat!( - r"^(?P\d+) \(.*\) (?P\S) -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ \d+ \d+ \d+ \d+ \d+ ", - r"(?P\d+) (?P\d+) -?\d+ -?\d+ -?\d+ -?\d+ -?\d+ 0 ", - r"(?P\d+) (?P\d+) (?P-?\d+) ", - r"\d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ -?\d+ -?\d+ \d+ \d+ \d+" - )).unwrap(); - } - - if let Some(cap) = REGEX.captures(&statstr) { - if pid != cap["pid"].parse::().unwrap() { - bail!("unable to read pid stat for process '{}' - got wrong pid", pid); - } - - return Ok(ProcFsPidStat { - status: cap["status"].as_bytes()[0], - utime: cap["utime"].parse::().unwrap(), - stime: cap["stime"].parse::().unwrap(), - starttime: cap["starttime"].parse::().unwrap(), - vsize: cap["vsize"].parse::().unwrap(), - rss: cap["rss"].parse::().unwrap() * 4096, - }); - - } - - bail!("unable to read pid stat for process '{}'", pid); -} - -pub fn read_proc_starttime(pid: libc::pid_t) -> Result { - - let info = read_proc_pid_stat(pid)?; - - Ok(info.starttime) -} - -pub fn check_process_running(pid: libc::pid_t) -> Option { - if let Ok(info) = read_proc_pid_stat(pid) { - if info.status != 'Z' as u8 { - return Some(info); - } - } - None -} - -pub fn check_process_running_pstart(pid: libc::pid_t, pstart: u64) -> Option { - if let Some(info) = check_process_running(pid) { - if info.starttime == pstart { - return Some(info); - } - } - None -} - -pub fn read_proc_uptime() -> Result<(f64, f64), Error> { - let path = "/proc/uptime"; - let line = file_read_firstline(&path)?; - let mut values = line.split_whitespace().map(|v| v.parse::()); - - match (values.next(), values.next()) { - (Some(Ok(up)), Some(Ok(idle))) => return Ok((up, idle)), - _ => bail!("Error while parsing '{}'", path), - } -} - -pub fn read_proc_uptime_ticks() -> Result<(u64, u64), Error> { - let (mut up, mut idle) = read_proc_uptime()?; - up *= *CLOCK_TICKS; - idle *= *CLOCK_TICKS; - Ok((up as u64, idle as u64)) -} - -#[derive(Debug)] -pub struct ProcFsMemInfo { - pub memtotal: u64, - pub memfree: u64, - pub memused: u64, - pub memshared: u64, - pub swaptotal: u64, - pub swapfree: u64, - pub swapused: u64, -} - -pub fn read_meminfo() -> Result { - let path = "/proc/meminfo"; - let file = OpenOptions::new().read(true).open(&path)?; - - let mut meminfo = ProcFsMemInfo { - memtotal: 0, - memfree: 0, - memused: 0, - memshared: 0, - swaptotal: 0, - swapfree: 0, - swapused: 0, - }; - - let (mut buffers, mut cached) = (0, 0); - for line in BufReader::new(&file).lines() { - let content = line?; - let mut content_iter = content.split_whitespace(); - if let (Some(key), Some(value)) = (content_iter.next(), content_iter.next()) { - match key { - "MemTotal:" => meminfo.memtotal = value.parse::()? * 1024, - "MemFree:" => meminfo.memfree = value.parse::()? * 1024, - "SwapTotal:" => meminfo.swaptotal = value.parse::()? * 1024, - "SwapFree:" => meminfo.swapfree = value.parse::()? * 1024, - "Buffers:" => buffers = value.parse::()? * 1024, - "Cached:" => cached = value.parse::()? * 1024, - _ => continue, - } - } - } - - meminfo.memfree += buffers + cached; - meminfo.memused = meminfo.memtotal - meminfo.memfree; - - meminfo.swapused = meminfo.swaptotal - meminfo.swapfree; - - let spages_line = file_read_firstline("/sys/kernel/mm/ksm/pages_sharing")?; - meminfo.memshared = spages_line.trim_end().parse::()? * 4096; - - Ok(meminfo) -} - -#[derive(Clone, Debug)] -pub struct ProcFsCPUInfo { - pub user_hz: f64, - pub mhz: f64, - pub model: String, - pub hvm: bool, - pub sockets: usize, - pub cpus: usize, -} - -static CPU_INFO: Option = None; - -pub fn read_cpuinfo() -> Result { - if let Some(cpu_info) = &CPU_INFO { return Ok(cpu_info.clone()); } - - let path = "/proc/cpuinfo"; - let file = OpenOptions::new().read(true).open(&path)?; - - let mut cpuinfo = ProcFsCPUInfo { - user_hz: *CLOCK_TICKS, - mhz: 0.0, - model: String::new(), - hvm: false, - sockets: 0, - cpus: 0, - }; - - let mut socket_ids = HashSet::new(); - for line in BufReader::new(&file).lines() { - let content = line?; - if content.is_empty() { continue; } - let mut content_iter = content.split(":"); - match (content_iter.next(), content_iter.next()) { - (Some(key), Some(value)) => { - match key.trim_end() { - "processor" => cpuinfo.cpus += 1, - "model name" => cpuinfo.model = value.trim().to_string(), - "cpu MHz" => cpuinfo.mhz = value.trim().parse::()?, - "flags" => cpuinfo.hvm = value.contains(" vmx ") || value.contains(" svm "), - "physical id" => { - let id = value.trim().parse::()?; - socket_ids.insert(id); - }, - _ => continue, - } - }, - _ => bail!("Error while parsing '{}'", path), - } - } - cpuinfo.sockets = socket_ids.len(); - - Ok(cpuinfo) -} - -#[derive(Debug)] -pub struct ProcFsMemUsage { - pub size: u64, - pub resident: u64, - pub shared: u64, -} - -pub fn read_memory_usage() -> Result { - let path = format!("/proc/{}/statm", process::id()); - let line = file_read_firstline(&path)?; - let mut values = line.split_whitespace().map(|v| v.parse::()); - - let ps = 4096; - match (values.next(), values.next(), values.next()) { - (Some(Ok(size)), Some(Ok(resident)), Some(Ok(shared))) => - Ok(ProcFsMemUsage { - size: size * ps, - resident: resident * ps, - shared: shared * ps, - }), - _ => bail!("Error while parsing '{}'", path), - } -} - -#[derive(Debug)] -pub struct ProcFsNetDev { - pub device: String, - pub receive: u64, - pub send: u64, -} - -pub fn read_proc_net_dev() -> Result, Error> { - let path = "/proc/net/dev"; - let file = OpenOptions::new().read(true).open(&path)?; - - let mut result = Vec::new(); - for line in BufReader::new(&file).lines().skip(2) { - let content = line?; - let mut iter = content.split_whitespace(); - match (iter.next(), iter.next(), iter.skip(7).next()) { - (Some(device), Some(receive), Some(send)) => { - result.push(ProcFsNetDev { - device: device[..device.len()-1].to_string(), - receive: receive.parse::()?, - send: send.parse::()?, - }); - }, - _ => bail!("Error while parsing '{}'", path), - } - } - - Ok(result) -} - -fn hex_nibble(c: u8) -> Result { - Ok(match c { - b'0'..=b'9' => c - b'0', - b'a'..=b'f' => c - b'a' + 0xa, - b'A'..=b'F' => c - b'A' + 0xa, - _ => bail!("not a hex digit: {}", c as char), - }) -} - -fn hexstr_to_ipv4addr>(hex: T) -> Result { - let hex = hex.as_ref(); - if hex.len() != 8 { - bail!("Error while converting hex string to IPv4 address: unexpected string length"); - } - - let mut addr: [u8; 4] = unsafe { std::mem::uninitialized() }; - for i in 0..4 { - addr[3 - i] = (hex_nibble(hex[i * 2])? << 4) + hex_nibble(hex[i * 2 + 1])?; - } - - Ok(Ipv4Addr::from(addr)) -} - -#[derive(Debug)] -pub struct ProcFsNetRoute { - pub dest: Ipv4Addr, - pub gateway: Ipv4Addr, - pub mask: Ipv4Addr, - pub metric: u32, - pub mtu: u32, - pub iface: String, -} - -pub fn read_proc_net_route() -> Result, Error> { - let path = "/proc/net/route"; - let file = OpenOptions::new().read(true).open(&path)?; - - let mut result = Vec::new(); - for line in BufReader::new(&file).lines().skip(1) { - let content = line?; - if content.is_empty() { continue; } - let mut iter = content.split_whitespace(); - - let mut next = || iter.next() - .ok_or(format_err!("Error while parsing '{}'", path)); - - let (iface, dest, gateway) = (next()?, next()?, next()?); - for _ in 0..3 { next()?; } - let (metric, mask, mtu) = (next()?, next()?, next()?); - - result.push(ProcFsNetRoute { - dest: hexstr_to_ipv4addr(dest)?, - gateway: hexstr_to_ipv4addr(gateway)?, - mask: hexstr_to_ipv4addr(mask)?, - metric: metric.parse()?, - mtu: mtu.parse()?, - iface: iface.to_string(), - }); - } - - Ok(result) -} - -fn hexstr_to_ipv6addr>(hex: T) -> Result { - let hex = hex.as_ref(); - if hex.len() != 32 { - bail!("Error while converting hex string to IPv6 address: unexpected string length"); - } - - let mut addr: [u8; 16] = unsafe { std::mem::uninitialized() }; - for i in 0..16 { - addr[i] = (hex_nibble(hex[i * 2])? << 4) + hex_nibble(hex[i * 2 + 1])?; - } - - Ok(Ipv6Addr::from(addr)) -} - -fn hexstr_to_u8>(hex: T) -> Result { - let hex = hex.as_ref(); - if hex.len() != 2 { - bail!("Error while converting hex string to u8: unexpected string length"); - } - - Ok((hex_nibble(hex[0])? << 4) + hex_nibble(hex[1])?) -} - -fn hexstr_to_u32>(hex: T) -> Result { - let hex = hex.as_ref(); - if hex.len() != 8 { - bail!("Error while converting hex string to u32: unexpected string length"); - } - - let mut bytes: [u8; 4] = unsafe { std::mem::uninitialized() }; - for i in 0..4 { - bytes[i] = (hex_nibble(hex[i * 2])? << 4) + hex_nibble(hex[i * 2 + 1])?; - } - - Ok(u32::from_be_bytes(bytes)) -} - -#[derive(Debug)] -pub struct ProcFsNetIPv6Route { - pub dest: Ipv6Addr, - pub prefix: u8, - pub gateway: Ipv6Addr, - pub metric: u32, - pub iface: String, -} - -pub fn read_proc_net_ipv6_route() -> Result, Error> { - let path = "/proc/net/ipv6_route"; - let file = OpenOptions::new().read(true).open(&path)?; - - let mut result = Vec::new(); - for line in BufReader::new(&file).lines() { - let content = line?; - if content.is_empty() { continue; } - let mut iter = content.split_whitespace(); - - let mut next = || iter.next() - .ok_or_else(|| format_err!("Error while parsing '{}'", path)); - - let (dest, prefix) = (next()?, next()?); - for _ in 0..2 { next()?; } - let (nexthop, metric) = (next()?, next()?); - for _ in 0..3 { next()?; } - let iface = next()?; - - result.push(ProcFsNetIPv6Route { - dest: hexstr_to_ipv6addr(dest)?, - prefix: hexstr_to_u8(prefix)?, - gateway: hexstr_to_ipv6addr(nexthop)?, - metric: hexstr_to_u32(metric)?, - iface: iface.to_string(), - }); - } - - Ok(result) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_read_proc_net_route() { - read_proc_net_route().unwrap(); - } - - #[test] - fn test_read_proc_net_ipv6_route() { - read_proc_net_ipv6_route().unwrap(); - } -}