char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somrearrange libs into lib and extensions into extsf4a0f5b

main
6.8 KiB235 linesraw
1use std::convert::Infallible;
2use std::io;
3use std::sync::Arc;
4
5use avril_caps::{Capabilities, PathTarget};
6use avril_core::Op;
7
8use avril_host::io::response;
9
10type Info = (u32, String, String, String, String, Vec<String>);
11
12fn denied(error: String) -> io::Error {
13    io::Error::new(io::ErrorKind::PermissionDenied, error.to_string())
14}
15
16fn cwd(caps: &Capabilities) -> io::Result<String> {
17    let current = std::env::current_dir()?;
18    let target = PathTarget::resolve(&current)?;
19    caps.check_read(&target)
20        .map_err(|error| denied(error.to_string()))?;
21    target
22        .as_path()
23        .to_str()
24        .map(str::to_owned)
25        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "cwd is not UTF-8"))
26}
27
28fn sys<T>(caps: &Capabilities, name: &str, value: fn() -> io::Result<T>) -> io::Result<T> {
29    caps.check_sys(name)
30        .map_err(|error| denied(error.to_string()))?;
31    value()
32}
33
34pub fn ops(caps: Arc<Capabilities>, args: Vec<String>) -> Vec<Op> {
35    let info: Info = (
36        std::process::id(),
37        std::env::consts::OS.to_owned(),
38        std::env::consts::ARCH.to_owned(),
39        env!("CARGO_PKG_VERSION").to_owned(),
40        avril_core::v8::V8::get_version().to_owned(),
41        args,
42    );
43    vec![
44        Op::new("op_process_cwd", {
45            let caps = caps.clone();
46            move |(): ()| Ok::<_, Infallible>(response(cwd(&caps)))
47        }),
48        Op::new("op_sys_hostname", {
49            let caps = caps.clone();
50            move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "hostname", hostname)))
51        }),
52        Op::new("op_sys_os_release", {
53            let caps = caps.clone();
54            move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "osRelease", os_release)))
55        }),
56        Op::new("op_sys_uptime", {
57            let caps = caps.clone();
58            move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "uptime", uptime)))
59        }),
60        Op::new("op_sys_loadavg", {
61            let caps = caps.clone();
62            move |(): ()| Ok::<_, Infallible>(response(sys(&caps, "loadavg", loadavg)))
63        }),
64        Op::new("op_runtime_info", move |(): ()| {
65            Ok::<_, Infallible>(info.clone())
66        }),
67    ]
68}
69
70#[cfg(unix)]
71fn text(bytes: &[libc::c_char]) -> io::Result<String> {
72    let end = bytes
73        .iter()
74        .position(|byte| *byte == 0)
75        .unwrap_or(bytes.len());
76    let bytes: Vec<u8> = bytes[..end].iter().map(|byte| *byte as u8).collect();
77    String::from_utf8(bytes)
78        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "System string is not UTF-8"))
79}
80
81#[cfg(unix)]
82fn hostname() -> io::Result<String> {
83    let mut buffer = [0 as libc::c_char; 256];
84    if unsafe { libc::gethostname(buffer.as_mut_ptr(), buffer.len()) } != 0 {
85        return Err(io::Error::last_os_error());
86    }
87    text(&buffer)
88}
89
90#[cfg(unix)]
91fn os_release() -> io::Result<String> {
92    let mut name: libc::utsname = unsafe { std::mem::zeroed() };
93    if unsafe { libc::uname(&mut name) } != 0 {
94        return Err(io::Error::last_os_error());
95    }
96    text(&name.release)
97}
98
99#[cfg(target_os = "linux")]
100fn uptime() -> io::Result<f64> {
101    let mut info: libc::sysinfo = unsafe { std::mem::zeroed() };
102    if unsafe { libc::sysinfo(&mut info) } != 0 {
103        return Err(io::Error::last_os_error());
104    }
105    Ok(info.uptime as f64)
106}
107
108#[cfg(target_os = "macos")]
109fn uptime() -> io::Result<f64> {
110    let mut boot: libc::timeval = unsafe { std::mem::zeroed() };
111    let mut size = std::mem::size_of::<libc::timeval>();
112    if unsafe {
113        libc::sysctlbyname(
114            c"kern.boottime".as_ptr(),
115            (&raw mut boot).cast(),
116            &mut size,
117            std::ptr::null_mut(),
118            0,
119        )
120    } != 0
121    {
122        return Err(io::Error::last_os_error());
123    }
124    let now = std::time::SystemTime::now()
125        .duration_since(std::time::UNIX_EPOCH)
126        .map_err(io::Error::other)?;
127    Ok(now.as_secs_f64() - boot.tv_sec as f64)
128}
129
130#[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))]
131fn uptime() -> io::Result<f64> {
132    Err(io::Error::new(
133        io::ErrorKind::Unsupported,
134        "uptime is not supported on this platform",
135    ))
136}
137
138#[cfg(unix)]
139fn loadavg() -> io::Result<[f64; 3]> {
140    let mut samples = [0.0; 3];
141    if unsafe { libc::getloadavg(samples.as_mut_ptr(), 3) } != 3 {
142        return Err(io::Error::last_os_error());
143    }
144    Ok(samples)
145}
146
147#[cfg(windows)]
148fn hostname() -> io::Result<String> {
149    use windows_sys::Win32::System::SystemInformation::{
150        ComputerNamePhysicalDnsHostname, GetComputerNameExW,
151    };
152    let mut buffer = [0u16; 256];
153    let mut size = buffer.len() as u32;
154    if unsafe {
155        GetComputerNameExW(
156            ComputerNamePhysicalDnsHostname,
157            buffer.as_mut_ptr(),
158            &mut size,
159        )
160    } == 0
161    {
162        return Err(io::Error::last_os_error());
163    }
164    String::from_utf16(&buffer[..size as usize])
165        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Hostname is not UTF-16"))
166}
167
168// windows-sys does not expose RtlGetVersion under Win32, and GetVersionExW
169// reports the compatibility version rather than the real one.
170#[cfg(windows)]
171#[link(name = "ntdll")]
172unsafe extern "system" {
173    fn RtlGetVersion(
174        version: *mut windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW,
175    ) -> i32;
176}
177
178#[cfg(windows)]
179fn os_release() -> io::Result<String> {
180    use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW;
181    let mut version = OSVERSIONINFOW {
182        dwOSVersionInfoSize: std::mem::size_of::<OSVERSIONINFOW>() as u32,
183        ..Default::default()
184    };
185    if unsafe { RtlGetVersion(&mut version) } != 0 {
186        return Err(io::Error::other("RtlGetVersion failed"));
187    }
188    Ok(format!(
189        "{}.{}.{}",
190        version.dwMajorVersion, version.dwMinorVersion, version.dwBuildNumber
191    ))
192}
193
194#[cfg(windows)]
195fn uptime() -> io::Result<f64> {
196    use windows_sys::Win32::System::SystemInformation::GetTickCount64;
197    Ok(unsafe { GetTickCount64() } as f64 / 1000.0)
198}
199
200#[cfg(windows)]
201fn loadavg() -> io::Result<[f64; 3]> {
202    Ok([0.0, 0.0, 0.0])
203}
204
205#[cfg(not(any(unix, windows)))]
206fn hostname() -> io::Result<String> {
207    Err(io::Error::new(
208        io::ErrorKind::Unsupported,
209        "hostname is not supported on this platform",
210    ))
211}
212
213#[cfg(not(any(unix, windows)))]
214fn os_release() -> io::Result<String> {
215    Err(io::Error::new(
216        io::ErrorKind::Unsupported,
217        "osRelease is not supported on this platform",
218    ))
219}
220
221#[cfg(not(any(unix, windows)))]
222fn uptime() -> io::Result<f64> {
223    Err(io::Error::new(
224        io::ErrorKind::Unsupported,
225        "uptime is not supported on this platform",
226    ))
227}
228
229#[cfg(not(any(unix, windows)))]
230fn loadavg() -> io::Result<[f64; 3]> {
231    Err(io::Error::new(
232        io::ErrorKind::Unsupported,
233        "loadavg is not supported on this platform",
234    ))
235}