// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use super::utils::into_string; use crate::permissions::Permissions; use crate::worker::ExitCode; use deno_core::error::{type_error, AnyError}; use deno_core::url::Url; use deno_core::Extension; use deno_core::OpState; use deno_core::{op, ExtensionBuilder}; use deno_node::NODE_ENV_VAR_ALLOWLIST; use serde::Serialize; use std::collections::HashMap; use std::env; fn init_ops(builder: &mut ExtensionBuilder) -> &mut ExtensionBuilder { builder.ops(vec![ op_env::decl(), op_exec_path::decl(), op_exit::decl(), op_delete_env::decl(), op_get_env::decl(), op_getgid::decl(), op_getuid::decl(), op_hostname::decl(), op_loadavg::decl(), op_network_interfaces::decl(), op_os_release::decl(), op_set_env::decl(), op_set_exit_code::decl(), op_system_memory_info::decl(), ]) } pub fn init(exit_code: ExitCode) -> Extension { let mut builder = Extension::builder(); init_ops(&mut builder) .state(move |state| { state.put::(exit_code.clone()); Ok(()) }) .build() } pub fn init_for_worker() -> Extension { let mut builder = Extension::builder(); init_ops(&mut builder) .middleware(|op| match op.name { "op_exit" => noop_op::decl(), "op_set_exit_code" => noop_op::decl(), _ => op, }) .build() } #[op] fn noop_op() -> Result<(), AnyError> { Ok(()) } #[op] fn op_exec_path(state: &mut OpState) -> Result { let current_exe = env::current_exe().unwrap(); state .borrow_mut::() .read .check_blind(¤t_exe, "exec_path")?; // Now apply URL parser to current exe to get fully resolved path, otherwise // we might get `./` and `../` bits in `exec_path` let exe_url = Url::from_file_path(current_exe).unwrap(); let path = exe_url.to_file_path().unwrap(); into_string(path.into_os_string()) } #[op] fn op_set_env( state: &mut OpState, key: String, value: String, ) -> Result<(), AnyError> { state.borrow_mut::().env.check(&key)?; let invalid_key = key.is_empty() || key.contains(&['=', '\0'] as &[char]); let invalid_value = value.contains('\0'); if invalid_key || invalid_value { return Err(type_error("Key or value contains invalid characters.")); } env::set_var(key, value); Ok(()) } #[op] fn op_env(state: &mut OpState) -> Result, AnyError> { state.borrow_mut::().env.check_all()?; Ok(env::vars().collect()) } #[op] fn op_get_env( state: &mut OpState, key: String, ) -> Result, AnyError> { let skip_permission_check = state.borrow::().unstable && NODE_ENV_VAR_ALLOWLIST.contains(&key); if !skip_permission_check { state.borrow_mut::().env.check(&key)?; } if key.is_empty() || key.contains(&['=', '\0'] as &[char]) { return Err(type_error("Key contains invalid characters.")); } let r = match env::var(key) { Err(env::VarError::NotPresent) => None, v => Some(v?), }; Ok(r) } #[op] fn op_delete_env(state: &mut OpState, key: String) -> Result<(), AnyError> { state.borrow_mut::().env.check(&key)?; if key.is_empty() || key.contains(&['=', '\0'] as &[char]) { return Err(type_error("Key contains invalid characters.")); } env::remove_var(key); Ok(()) } #[op] fn op_set_exit_code(state: &mut OpState, code: i32) { state.borrow_mut::().set(code); } #[op] fn op_exit(state: &mut OpState) { let code = state.borrow::().get(); std::process::exit(code) } #[op] fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> { super::check_unstable(state, "Deno.loadavg"); state.borrow_mut::().env.check_all()?; match sys_info::loadavg() { Ok(loadavg) => Ok((loadavg.one, loadavg.five, loadavg.fifteen)), Err(_) => Ok((0.0, 0.0, 0.0)), } } #[op] fn op_hostname(state: &mut OpState) -> Result { super::check_unstable(state, "Deno.hostname"); state.borrow_mut::().env.check_all()?; let hostname = sys_info::hostname().unwrap_or_else(|_| "".to_string()); Ok(hostname) } #[op] fn op_os_release(state: &mut OpState) -> Result { super::check_unstable(state, "Deno.osRelease"); state.borrow_mut::().env.check_all()?; let release = sys_info::os_release().unwrap_or_else(|_| "".to_string()); Ok(release) } #[op] fn op_network_interfaces( state: &mut OpState, ) -> Result, AnyError> { super::check_unstable(state, "Deno.networkInterfaces"); state.borrow_mut::().env.check_all()?; Ok(netif::up()?.map(NetworkInterface::from).collect()) } #[derive(serde::Serialize)] struct NetworkInterface { family: &'static str, name: String, address: String, netmask: String, scopeid: Option, cidr: String, mac: String, } impl From for NetworkInterface { fn from(ifa: netif::Interface) -> Self { let family = match ifa.address() { std::net::IpAddr::V4(_) => "IPv4", std::net::IpAddr::V6(_) => "IPv6", }; let (address, range) = ifa.cidr(); let cidr = format!("{:?}/{}", address, range); let name = ifa.name().to_owned(); let address = format!("{:?}", ifa.address()); let netmask = format!("{:?}", ifa.netmask()); let scopeid = ifa.scope_id(); let [b0, b1, b2, b3, b4, b5] = ifa.mac(); let mac = format!( "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", b0, b1, b2, b3, b4, b5 ); Self { family, name, address, netmask, scopeid, cidr, mac, } } } // Copied from sys-info/lib.rs (then tweaked) #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct MemInfo { pub total: u64, pub free: u64, pub available: u64, pub buffers: u64, pub cached: u64, pub swap_total: u64, pub swap_free: u64, } #[op] fn op_system_memory_info( state: &mut OpState, ) -> Result, AnyError> { super::check_unstable(state, "Deno.systemMemoryInfo"); state.borrow_mut::().env.check_all()?; match sys_info::mem_info() { Ok(info) => Ok(Some(MemInfo { total: info.total, free: info.free, available: info.avail, buffers: info.buffers, cached: info.cached, swap_total: info.swap_total, swap_free: info.swap_free, })), Err(_) => Ok(None), } } #[cfg(not(windows))] #[op] fn op_getgid(state: &mut OpState) -> Result, AnyError> { super::check_unstable(state, "Deno.getGid"); state.borrow_mut::().env.check_all()?; // TODO(bartlomieju): #[allow(clippy::undocumented_unsafe_blocks)] unsafe { Ok(Some(libc::getgid())) } } #[cfg(windows)] #[op] fn op_getgid(state: &mut OpState) -> Result, AnyError> { super::check_unstable(state, "Deno.getGid"); state.borrow_mut::().env.check_all()?; Ok(None) } #[cfg(not(windows))] #[op] fn op_getuid(state: &mut OpState) -> Result, AnyError> { super::check_unstable(state, "Deno.getUid"); state.borrow_mut::().env.check_all()?; // TODO(bartlomieju): #[allow(clippy::undocumented_unsafe_blocks)] unsafe { Ok(Some(libc::getuid())) } } #[cfg(windows)] #[op] fn op_getuid(state: &mut OpState) -> Result, AnyError> { super::check_unstable(state, "Deno.getUid"); state.borrow_mut::().env.check_all()?; Ok(None) }