use std::thread;
use std::sync::Arc;
use std::net::IpAddr;
use std::sync::RwLock;
use std::path::PathBuf;
use std::cell::RefCell;
use std::net::SocketAddr;
use fnv::FnvHashMap;
use netstat2::{AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, TcpState, SocketInfo, get_sockets_info, iterate_sockets_info};
use sysinfo::{NetworkExt, System, SystemExt, CpuExt, ProcessExt, ProcessStatus, DiskExt, Pid, PidExt};
use crate::SysSpecialStat;
#[cfg(all(unix, not(target_os="android")))]
use crate::linux::LinuxSysStat;
const DEFAULT_INTERVAL: f64 = 0.2;
lazy_static! {
static ref SERVER_PORTS_TABLE: Arc<RwLock<FnvHashMap<u16, SocketAddr>>> = Arc::new(RwLock::new(FnvHashMap::default()));
}
pub fn server_ports() -> Option<Vec<u16>> {
let ports = SERVER_PORTS_TABLE.read().unwrap();
let keys = ports.keys();
if keys.len() == 0 {
return None;
}
Some(keys.map(|key| {
key.clone()
}).collect())
}
pub fn port_info(port: u16) -> Option<SocketAddr> {
if let Some(addr) = SERVER_PORTS_TABLE.read().unwrap().get(&port) {
return Some(addr.clone());
}
None
}
pub fn register_server_port(addr: SocketAddr) -> Option<SocketAddr> {
SERVER_PORTS_TABLE.write().unwrap().insert(addr.port(), addr)
}
pub fn unregister_server_port(port: u16) -> Option<SocketAddr> {
SERVER_PORTS_TABLE.write().unwrap().remove(&port)
}
pub type ProcessState = ProcessStatus;
pub type DiskType = sysinfo::DiskType;
pub type TcpStatus = TcpState;
type NetSocketsInfo = Vec<(NetProtocolType, IpAddr, u16, Option<IpAddr>, Option<u16>, Option<TcpStatus>, Vec<u32>)>;
pub struct ApmGuard<T> {
arg: T,
callback: Arc<dyn Fn(&mut T, thread::Thread)>,
}
impl<T> Drop for ApmGuard<T> {
fn drop(&mut self) {
(self.callback)(&mut self.arg, thread::current());
}
}
impl<T> ApmGuard<T> {
pub fn new(arg: T, callback: Arc<dyn Fn(&mut T, thread::Thread)>) -> Self {
ApmGuard {
arg,
callback,
}
}
}
pub enum NetIPType {
IPv4,
IPv6,
All,
}
#[derive(Debug)]
pub enum NetProtocolType {
TCP,
UDP,
All,
}
#[derive(Clone)]
pub struct SysStat {
inner: Arc<RefCell<System>>, special: Option<Arc<dyn SysSpecialStat>>, }
impl SysStat {
#[cfg(any(windows))]
pub fn new() -> Self {
SysStat {
inner: Arc::new(RefCell::new(System::new())),
special: None,
}
}
#[cfg(all(unix, not(target_os="android")))]
pub fn new() -> Self {
SysStat {
inner: Arc::new(RefCell::new(System::new())),
special: Some(Arc::new(LinuxSysStat::new(DEFAULT_INTERVAL))),
}
}
pub fn special_platform(&self) -> Option<Arc<dyn SysSpecialStat>> {
if let Some(detal) = &self.special {
return Some(detal.clone());
}
None
}
pub fn processor_count(&self) -> usize {
self.inner.borrow_mut().refresh_system();
let count = self.inner.borrow().cpus().len();
if count == 1 {
return 1;
}
count - 1
}
pub fn processor_usage(&self, n: usize) -> f32 {
self.inner.borrow_mut().refresh_system();
let inner = self.inner.borrow();
let array = inner.cpus();
let count = array.len();
if count == 1 && n == 0 {
return array[n].cpu_usage();
} else if count > 1 && n < count - 1 {
return array[n].cpu_usage();
}
0.0
}
pub fn memory_usage(&self) -> (u64, u64, u64, u64, u64, u64) {
self.inner.borrow_mut().refresh_system();
let inner = self.inner.borrow();
(inner.total_memory(), inner.free_memory(), inner.used_memory(), inner.total_swap(), inner.free_swap(), inner.used_swap()) }
#[cfg(any(windows))]
pub fn current_pid(&self) -> usize {
sysinfo::get_current_pid()
.unwrap_or(Pid::from(0))
.as_u32() as usize
}
#[cfg(any(windows))]
pub fn current_process_info(&self) -> (usize, String, PathBuf, Vec<String>, f32, u64, u64, ProcessState) {
let pid = sysinfo::get_current_pid().unwrap();
self.inner.borrow_mut().refresh_process(pid);
let inner = self.inner.borrow();
let process = inner.process(pid).unwrap();
(pid.as_u32() as usize, process.name().to_string(), process.cwd().to_owned(), Vec::from(process.cmd()), process.cpu_usage(), process.memory(), process.start_time(), process.status()) }
pub fn disk_usage(&self) -> Vec<(String, DiskType, String, PathBuf, u64, u64)> {
self.inner.borrow_mut().refresh_disks();
let inner = self.inner.borrow();
let disks = inner.disks();
let mut vec = Vec::with_capacity(disks.len());
for disk in inner.disks() {
let disk_name: String;
if let Ok(name) = disk.name().to_os_string().into_string() {
disk_name = name;
} else {
disk_name = "".to_string();
}
vec.push(
(
disk_name, disk.type_(), String::from_utf8_lossy(disk.file_system()).into_owned(), disk.mount_point().to_owned(), disk.available_space(), disk.total_space() )
);
}
vec
}
pub fn net_io_usage(&self) -> (u64, u64) {
self.inner.borrow_mut().refresh_networks();
let mut input = 0;
let mut output = 0;
let inner = self.inner.borrow();
let net = inner.networks();
for (_, network) in net {
input += network.total_received();
output += network.total_transmitted();
}
(input, output)
}
pub fn sockets_size(&self, ip_type: NetIPType, protocol_type: NetProtocolType) -> usize {
let (address_flag, protocol_flag) = filter_sockets_args(ip_type, protocol_type);
if let Ok(mut sockets) = iterate_sockets_info(address_flag, protocol_flag) {
return sockets.count();
}
0
}
pub fn sockets_info(&self, ip_type: NetIPType, protocol_type: NetProtocolType) -> NetSocketsInfo {
let mut vec = Vec::new();
let (address_flag, protocol_flag) = filter_sockets_args(ip_type, protocol_type);
if let Ok(sockets) = get_sockets_info(address_flag, protocol_flag) {
for socket in sockets {
fill_socket_info(&mut vec, socket);
}
}
vec
}
pub fn process_sockets_info(&self, pid: i32, ip_type: NetIPType, protocol_type: NetProtocolType) -> NetSocketsInfo {
let mut vec = Vec::new();
let (address_flag, protocol_flag) = filter_sockets_args(ip_type, protocol_type);
if let Ok(mut sockets) = iterate_sockets_info(address_flag, protocol_flag) {
loop {
if let Some(r) = sockets.next() {
if let Ok(socket) = r {
if contains_pid_sockets(pid, &socket) {
fill_socket_info(&mut vec, socket);
}
}
} else {
break;
}
}
}
vec
}
pub fn current_process_sockets_info(&self, pid: i32, ip_type: NetIPType, protocol_type: NetProtocolType) -> NetSocketsInfo {
self.process_sockets_info(pid, ip_type, protocol_type)
}
pub fn uptime(&self) -> u64 {
self.inner.borrow_mut().refresh_system();
self.inner.borrow().uptime()
}
}
fn filter_sockets_args(ip_type: NetIPType, protocol_type: NetProtocolType) -> (AddressFamilyFlags, ProtocolFlags) {
let address_flag: AddressFamilyFlags;
let protocol_flag: ProtocolFlags;
match ip_type {
NetIPType::IPv4 => {
address_flag = AddressFamilyFlags::IPV4;
},
NetIPType::IPv6 => {
address_flag = AddressFamilyFlags::IPV6;
},
NetIPType::All => {
address_flag = AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6;
},
}
match protocol_type {
NetProtocolType::TCP => {
protocol_flag = ProtocolFlags::TCP;
},
NetProtocolType::UDP => {
protocol_flag = ProtocolFlags::UDP;
},
NetProtocolType::All => {
protocol_flag = ProtocolFlags::TCP | ProtocolFlags::UDP;
},
}
(address_flag, protocol_flag)
}
fn contains_pid_sockets(pid: i32, socket: &SocketInfo) -> bool {
socket.associated_pids.binary_search(&(pid as u32)).is_ok()
}
fn fill_socket_info(vec: &mut NetSocketsInfo, socket: SocketInfo) {
let socket_info = match &socket.protocol_socket_info {
&ProtocolSocketInfo::Tcp(ref info) => {
(NetProtocolType::TCP, info.local_addr, info.local_port, Some(info.remote_addr), Some(info.remote_port), Some(info.state), socket.associated_pids) },
&ProtocolSocketInfo::Udp(ref info) => {
(NetProtocolType::UDP, info.local_addr, info.local_port, None, None, None, socket.associated_pids) },
};
vec.push(socket_info);
}