summaryrefslogtreecommitdiff
path: root/runtime/ops/os/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/ops/os/mod.rs')
-rw-r--r--runtime/ops/os/mod.rs307
1 files changed, 307 insertions, 0 deletions
diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs
new file mode 100644
index 000000000..3b4645403
--- /dev/null
+++ b/runtime/ops/os/mod.rs
@@ -0,0 +1,307 @@
+// 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 std::collections::HashMap;
+use std::env;
+
+mod sys_info;
+
+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_gid::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(),
+ op_uid::decl(),
+ ])
+}
+
+pub fn init(exit_code: ExitCode) -> Extension {
+ let mut builder = Extension::builder();
+ init_ops(&mut builder)
+ .state(move |state| {
+ state.put::<ExitCode>(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<String, AnyError> {
+ let current_exe = env::current_exe().unwrap();
+ state.borrow_mut::<Permissions>().read.check_blind(
+ &current_exe,
+ "exec_path",
+ "Deno.execPath()",
+ )?;
+ // 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::<Permissions>().env.check(&key)?;
+ if key.is_empty() {
+ return Err(type_error("Key is an empty string."));
+ }
+ if key.contains(&['=', '\0'] as &[char]) {
+ return Err(type_error(format!(
+ "Key contains invalid characters: {:?}",
+ key
+ )));
+ }
+ if value.contains('\0') {
+ return Err(type_error(format!(
+ "Value contains invalid characters: {:?}",
+ value
+ )));
+ }
+ env::set_var(key, value);
+ Ok(())
+}
+
+#[op]
+fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
+ state.borrow_mut::<Permissions>().env.check_all()?;
+ Ok(env::vars().collect())
+}
+
+#[op]
+fn op_get_env(
+ state: &mut OpState,
+ key: String,
+) -> Result<Option<String>, AnyError> {
+ let skip_permission_check =
+ state.borrow::<crate::ops::UnstableChecker>().unstable
+ && NODE_ENV_VAR_ALLOWLIST.contains(&key);
+
+ if !skip_permission_check {
+ state.borrow_mut::<Permissions>().env.check(&key)?;
+ }
+
+ if key.is_empty() {
+ return Err(type_error("Key is an empty string."));
+ }
+
+ if key.contains(&['=', '\0'] as &[char]) {
+ return Err(type_error(format!(
+ "Key contains invalid characters: {:?}",
+ key
+ )));
+ }
+
+ 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::<Permissions>().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::<ExitCode>().set(code);
+}
+
+#[op]
+fn op_exit(state: &mut OpState) {
+ let code = state.borrow::<ExitCode>().get();
+ std::process::exit(code)
+}
+
+#[op]
+fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("loadavg", Some("Deno.loadavg()"))?;
+ Ok(sys_info::loadavg())
+}
+
+#[op]
+fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("hostname", Some("Deno.hostname()"))?;
+ Ok(sys_info::hostname())
+}
+
+#[op]
+fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("osRelease", Some("Deno.osRelease()"))?;
+ Ok(sys_info::os_release())
+}
+
+#[op]
+fn op_network_interfaces(
+ state: &mut OpState,
+) -> Result<Vec<NetworkInterface>, AnyError> {
+ super::check_unstable(state, "Deno.networkInterfaces");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("networkInterfaces", Some("Deno.networkInterfaces()"))?;
+ Ok(netif::up()?.map(NetworkInterface::from).collect())
+}
+
+#[derive(serde::Serialize)]
+struct NetworkInterface {
+ family: &'static str,
+ name: String,
+ address: String,
+ netmask: String,
+ scopeid: Option<u32>,
+ cidr: String,
+ mac: String,
+}
+
+impl From<netif::Interface> 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,
+ }
+ }
+}
+
+#[op]
+fn op_system_memory_info(
+ state: &mut OpState,
+) -> Result<Option<sys_info::MemInfo>, AnyError> {
+ super::check_unstable(state, "Deno.systemMemoryInfo");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("systemMemoryInfo", Some("Deno.systemMemoryInfo()"))?;
+ Ok(sys_info::mem_info())
+}
+
+#[cfg(not(windows))]
+#[op]
+fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.gid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("gid", Some("Deno.gid()"))?;
+ // TODO(bartlomieju):
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ unsafe {
+ Ok(Some(libc::getgid()))
+ }
+}
+
+#[cfg(windows)]
+#[op]
+fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.gid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("gid", Some("Deno.gid()"))?;
+ Ok(None)
+}
+
+#[cfg(not(windows))]
+#[op]
+fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.uid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("uid", Some("Deno.uid()"))?;
+ // TODO(bartlomieju):
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ unsafe {
+ Ok(Some(libc::getuid()))
+ }
+}
+
+#[cfg(windows)]
+#[op]
+fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.uid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("uid", Some("Deno.uid()"))?;
+ Ok(None)
+}