use std::convert::Infallible; use std::io; use std::sync::Arc; use avril_caps::{Capabilities, PathTarget}; use avril_core::Op; use avril_host::io::response; type Info = (u32, String, String, String, String, Vec); fn denied(error: String) -> io::Error { io::Error::new(io::ErrorKind::PermissionDenied, error.to_string()) } fn cwd(caps: &Capabilities) -> io::Result { let current = std::env::current_dir()?; let target = PathTarget::resolve(¤t)?; caps.check_read(&target) .map_err(|error| denied(error.to_string()))?; target .as_path() .to_str() .map(str::to_owned) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cwd is not UTF-8")) } fn sys(caps: &Capabilities, name: &str, value: fn() -> io::Result) -> io::Result { caps.check_sys(name) .map_err(|error| denied(error.to_string()))?; value() } pub fn ops(caps: Arc, args: Vec) -> Vec { let info: Info = ( std::process::id(), std::env::consts::OS.to_owned(), std::env::consts::ARCH.to_owned(), env!("CARGO_PKG_VERSION").to_owned(), avril_core::v8::V8::get_version().to_owned(), args, ); vec![ Op::new("op_process_cwd", { let caps = caps.clone(); move |(): ()| Ok::<_, Infallible>(response(cwd(&caps))) }), Op::new("op_sys_hostname", { let caps = caps.clone(); move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "hostname", hostname))) }), Op::new("op_sys_os_release", { let caps = caps.clone(); move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "osRelease", os_release))) }), Op::new("op_sys_uptime", { let caps = caps.clone(); move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "uptime", uptime))) }), Op::new("op_sys_loadavg", { let caps = caps.clone(); move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "loadavg", loadavg))) }), Op::new("op_runtime_info", move |(): ()| { Ok::<_, Infallible>(info.clone()) }), ] } #[cfg(unix)] fn text(bytes: &[libc::c_char]) -> io::Result { let end = bytes .iter() .position(|byte| *byte == 0) .unwrap_or(bytes.len()); let bytes: Vec = bytes[..end].iter().map(|byte| *byte as u8).collect(); String::from_utf8(bytes) .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "System string is not UTF-8")) } #[cfg(unix)] fn hostname() -> io::Result { let mut buffer = [0 as libc::c_char; 256]; if unsafe { libc::gethostname(buffer.as_mut_ptr(), buffer.len()) } != 0 { return Err(io::Error::last_os_error()); } text(&buffer) } #[cfg(unix)] fn os_release() -> io::Result { let mut name: libc::utsname = unsafe { std::mem::zeroed() }; if unsafe { libc::uname(&mut name) } != 0 { return Err(io::Error::last_os_error()); } text(&name.release) } #[cfg(target_os = "linux")] fn uptime() -> io::Result { let mut info: libc::sysinfo = unsafe { std::mem::zeroed() }; if unsafe { libc::sysinfo(&mut info) } != 0 { return Err(io::Error::last_os_error()); } Ok(info.uptime as f64) } #[cfg(target_os = "macos")] fn uptime() -> io::Result { let mut boot: libc::timeval = unsafe { std::mem::zeroed() }; let mut size = std::mem::size_of::(); if unsafe { libc::sysctlbyname( c"kern.boottime".as_ptr(), (&raw mut boot).cast(), &mut size, std::ptr::null_mut(), 0, ) } != 0 { return Err(io::Error::last_os_error()); } let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map_err(io::Error::other)?; Ok(now.as_secs_f64() - boot.tv_sec as f64) } #[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))] fn uptime() -> io::Result { Err(io::Error::new( io::ErrorKind::Unsupported, "uptime is not supported on this platform", )) } #[cfg(unix)] fn loadavg() -> io::Result<[f64; 3]> { let mut samples = [0.0; 3]; if unsafe { libc::getloadavg(samples.as_mut_ptr(), 3) } != 3 { return Err(io::Error::last_os_error()); } Ok(samples) } #[cfg(windows)] fn hostname() -> io::Result { use windows_sys::Win32::System::SystemInformation::{ ComputerNamePhysicalDnsHostname, GetComputerNameExW, }; let mut buffer = [0u16; 256]; let mut size = buffer.len() as u32; if unsafe { GetComputerNameExW( ComputerNamePhysicalDnsHostname, buffer.as_mut_ptr(), &mut size, ) } == 0 { return Err(io::Error::last_os_error()); } String::from_utf16(&buffer[..size as usize]) .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Hostname is not UTF-16")) } // windows-sys does not expose RtlGetVersion under Win32, and GetVersionExW // reports the compatibility version rather than the real one. #[cfg(windows)] #[link(name = "ntdll")] unsafe extern "system" { fn RtlGetVersion( version: *mut windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW, ) -> i32; } #[cfg(windows)] fn os_release() -> io::Result { use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW; let mut version = OSVERSIONINFOW { dwOSVersionInfoSize: std::mem::size_of::() as u32, ..Default::default() }; if unsafe { RtlGetVersion(&mut version) } != 0 { return Err(io::Error::other("RtlGetVersion failed")); } Ok(format!( "{}.{}.{}", version.dwMajorVersion, version.dwMinorVersion, version.dwBuildNumber )) } #[cfg(windows)] fn uptime() -> io::Result { use windows_sys::Win32::System::SystemInformation::GetTickCount64; Ok(unsafe { GetTickCount64() } as f64 / 1000.0) } #[cfg(windows)] fn loadavg() -> io::Result<[f64; 3]> { Ok([0.0, 0.0, 0.0]) } #[cfg(not(any(unix, windows)))] fn hostname() -> io::Result { Err(io::Error::new( io::ErrorKind::Unsupported, "hostname is not supported on this platform", )) } #[cfg(not(any(unix, windows)))] fn os_release() -> io::Result { Err(io::Error::new( io::ErrorKind::Unsupported, "osRelease is not supported on this platform", )) } #[cfg(not(any(unix, windows)))] fn uptime() -> io::Result { Err(io::Error::new( io::ErrorKind::Unsupported, "uptime is not supported on this platform", )) } #[cfg(not(any(unix, windows)))] fn loadavg() -> io::Result<[f64; 3]> { Err(io::Error::new( io::ErrorKind::Unsupported, "loadavg is not supported on this platform", )) }