mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
feat(node/os): implement getPriority, setPriority & userInfo (#19370)
Takes #4202 over Closes #17850 --------- Co-authored-by: ecyrbe <ecyrbe@gmail.com>
This commit is contained in:
parent
78ceeec6be
commit
aa8078b688
11 changed files with 314 additions and 31 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -1296,11 +1296,13 @@ dependencies = [
|
|||
"dsa",
|
||||
"ecb",
|
||||
"elliptic-curve 0.13.4",
|
||||
"errno",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"idna 0.3.0",
|
||||
"indexmap 1.9.2",
|
||||
"lazy-regex",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"md-5",
|
||||
"md4",
|
||||
|
@ -1328,6 +1330,8 @@ dependencies = [
|
|||
"signature 1.6.4",
|
||||
"tokio",
|
||||
"typenum",
|
||||
"whoami",
|
||||
"winapi",
|
||||
"x25519-dalek",
|
||||
"x509-parser",
|
||||
]
|
||||
|
@ -6089,6 +6093,16 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "0.5.1"
|
||||
|
|
|
@ -45,12 +45,14 @@ fn macos_shared_libraries() {
|
|||
// target/release/deno:
|
||||
// /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1953.255.0)
|
||||
// /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 1228.0.0)
|
||||
// /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration (compatibility version 1.0.0, current version 1241.100.11)
|
||||
// /System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 60420.60.24)
|
||||
// /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
|
||||
// /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
|
||||
const EXPECTED: [&str; 6] =
|
||||
const EXPECTED: [&str; 7] =
|
||||
["/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
|
||||
"/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
|
||||
"/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration",
|
||||
"/System/Library/Frameworks/Security.framework/Versions/A/Security",
|
||||
"/usr/lib/libiconv.2.dylib",
|
||||
"/usr/lib/libSystem.B.dylib",
|
||||
|
|
|
@ -5,6 +5,7 @@ import os from "node:os";
|
|||
import {
|
||||
assert,
|
||||
assertEquals,
|
||||
assertNotEquals,
|
||||
assertThrows,
|
||||
} from "../../../test_util/std/testing/asserts.ts";
|
||||
|
||||
|
@ -252,28 +253,39 @@ Deno.test({
|
|||
});
|
||||
|
||||
Deno.test({
|
||||
name: "APIs not yet implemented",
|
||||
name: "os.setPriority() & os.getPriority()",
|
||||
// disabled because os.getPriority() doesn't work without sudo
|
||||
ignore: true,
|
||||
fn() {
|
||||
assertThrows(
|
||||
() => {
|
||||
os.getPriority();
|
||||
},
|
||||
Error,
|
||||
"Not implemented",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
os.setPriority(0);
|
||||
},
|
||||
Error,
|
||||
"Not implemented",
|
||||
);
|
||||
assertThrows(
|
||||
() => {
|
||||
os.userInfo();
|
||||
},
|
||||
Error,
|
||||
"Not implemented",
|
||||
const child = new Deno.Command(Deno.execPath(), {
|
||||
args: ["eval", "while (true) { console.log('foo') }"],
|
||||
}).spawn();
|
||||
const originalPriority = os.getPriority(child.pid);
|
||||
assertNotEquals(originalPriority, os.constants.priority.PRIORITY_HIGH);
|
||||
os.setPriority(child.pid, os.constants.priority.PRIORITY_HIGH);
|
||||
assertEquals(
|
||||
os.getPriority(child.pid),
|
||||
os.constants.priority.PRIORITY_HIGH,
|
||||
);
|
||||
os.setPriority(child.pid, originalPriority);
|
||||
assertEquals(os.getPriority(child.pid), originalPriority);
|
||||
child.kill();
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name:
|
||||
"os.setPriority() throw os permission denied error & os.getPriority() doesn't",
|
||||
async fn() {
|
||||
const child = new Deno.Command(Deno.execPath(), {
|
||||
args: ["eval", "while (true) { console.log('foo') }"],
|
||||
}).spawn();
|
||||
assertThrows(
|
||||
() => os.setPriority(child.pid, os.constants.priority.PRIORITY_HIGH),
|
||||
Deno.errors.PermissionDenied,
|
||||
);
|
||||
os.getPriority(child.pid);
|
||||
child.kill();
|
||||
await child.status;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -28,11 +28,13 @@ digest = { version = "0.10.5", features = ["core-api", "std"] }
|
|||
dsa = "0.6.1"
|
||||
ecb.workspace = true
|
||||
elliptic-curve.workspace = true
|
||||
errno = "0.2.8"
|
||||
hex.workspace = true
|
||||
hkdf.workspace = true
|
||||
idna = "0.3.0"
|
||||
indexmap.workspace = true
|
||||
lazy-regex.workspace = true
|
||||
libc.workspace = true
|
||||
libz-sys = { version = "1.1.8", features = ["static"] }
|
||||
md-5 = "0.10.5"
|
||||
md4 = "0.10.2"
|
||||
|
@ -60,6 +62,8 @@ sha2.workspace = true
|
|||
signature.workspace = true
|
||||
tokio.workspace = true
|
||||
typenum = "1.15.0"
|
||||
whoami = "1.4.0"
|
||||
winapi.workspace = true
|
||||
# https://github.com/dalek-cryptography/x25519-dalek/pull/89
|
||||
x25519-dalek = "2.0.0-pre.1"
|
||||
x509-parser = "0.15.0"
|
||||
|
|
|
@ -53,6 +53,7 @@ pub trait NodePermissions {
|
|||
api_name: &str,
|
||||
) -> Result<(), AnyError>;
|
||||
fn check_read(&self, path: &Path) -> Result<(), AnyError>;
|
||||
fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
pub(crate) struct AllowAllNodePermissions;
|
||||
|
@ -68,6 +69,9 @@ impl NodePermissions for AllowAllNodePermissions {
|
|||
fn check_read(&self, _path: &Path) -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
fn check_sys(&self, _kind: &str, _api_name: &str) -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
|
@ -243,6 +247,9 @@ deno_core::extension!(deno_node,
|
|||
ops::zlib::brotli::op_brotli_decompress_stream,
|
||||
ops::zlib::brotli::op_brotli_decompress_stream_end,
|
||||
ops::http::op_node_http_request<P>,
|
||||
ops::os::op_node_os_get_priority<P>,
|
||||
ops::os::op_node_os_set_priority<P>,
|
||||
ops::os::op_node_os_username<P>,
|
||||
op_node_build_os,
|
||||
op_is_any_arraybuffer,
|
||||
op_node_is_promise_rejected,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
pub mod crypto;
|
||||
pub mod http;
|
||||
pub mod idna;
|
||||
pub mod os;
|
||||
pub mod require;
|
||||
pub mod v8;
|
||||
pub mod winerror;
|
||||
|
|
188
ext/node/ops/os.rs
Normal file
188
ext/node/ops/os.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::NodePermissions;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op;
|
||||
use deno_core::OpState;
|
||||
use errno::errno;
|
||||
use errno::set_errno;
|
||||
use errno::Errno;
|
||||
|
||||
#[op]
|
||||
pub fn op_node_os_get_priority<P>(
|
||||
state: &mut OpState,
|
||||
pid: u32,
|
||||
) -> Result<i32, AnyError>
|
||||
where
|
||||
P: NodePermissions + 'static,
|
||||
{
|
||||
{
|
||||
let permissions = state.borrow_mut::<P>();
|
||||
permissions.check_sys("getPriority", "node:os.getPriority()")?;
|
||||
}
|
||||
|
||||
priority::get_priority(pid)
|
||||
}
|
||||
|
||||
#[op]
|
||||
pub fn op_node_os_set_priority<P>(
|
||||
state: &mut OpState,
|
||||
pid: u32,
|
||||
priority: i32,
|
||||
) -> Result<(), AnyError>
|
||||
where
|
||||
P: NodePermissions + 'static,
|
||||
{
|
||||
{
|
||||
let permissions = state.borrow_mut::<P>();
|
||||
permissions.check_sys("setPriority", "node:os.setPriority()")?;
|
||||
}
|
||||
|
||||
priority::set_priority(pid, priority)
|
||||
}
|
||||
|
||||
#[op]
|
||||
pub fn op_node_os_username<P>(state: &mut OpState) -> Result<String, AnyError>
|
||||
where
|
||||
P: NodePermissions + 'static,
|
||||
{
|
||||
{
|
||||
let permissions = state.borrow_mut::<P>();
|
||||
permissions.check_sys("userInfo", "node:os.userInfo()")?;
|
||||
}
|
||||
|
||||
Ok(whoami::username())
|
||||
}
|
||||
|
||||
const PRIORITY_HIGH: i32 = -14;
|
||||
|
||||
#[cfg(unix)]
|
||||
mod priority {
|
||||
use super::*;
|
||||
use libc::id_t;
|
||||
use libc::PRIO_PROCESS;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[allow(non_camel_case_types)]
|
||||
type priority_t = i32;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[allow(non_camel_case_types)]
|
||||
type priority_t = u32;
|
||||
|
||||
// Ref: https://github.com/libuv/libuv/blob/55376b044b74db40772e8a6e24d67a8673998e02/src/unix/core.c#L1533-L1547
|
||||
pub fn get_priority(pid: u32) -> Result<i32, AnyError> {
|
||||
set_errno(Errno(0));
|
||||
match (
|
||||
// SAFETY: libc::getpriority is unsafe
|
||||
unsafe { libc::getpriority(PRIO_PROCESS as priority_t, pid as id_t) },
|
||||
errno(),
|
||||
) {
|
||||
(-1, Errno(0)) => Ok(PRIORITY_HIGH),
|
||||
(-1, _) => Err(std::io::Error::last_os_error().into()),
|
||||
(priority, _) => Ok(priority),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_priority(pid: u32, priority: i32) -> Result<(), AnyError> {
|
||||
// SAFETY: libc::setpriority is unsafe
|
||||
match unsafe {
|
||||
libc::setpriority(PRIO_PROCESS as priority_t, pid as id_t, priority)
|
||||
} {
|
||||
-1 => Err(std::io::Error::last_os_error().into()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod priority {
|
||||
use super::*;
|
||||
use deno_core::error::type_error;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::shared::minwindef::FALSE;
|
||||
use winapi::shared::ntdef::NULL;
|
||||
use winapi::um::handleapi::CloseHandle;
|
||||
use winapi::um::processthreadsapi::GetCurrentProcess;
|
||||
use winapi::um::processthreadsapi::GetPriorityClass;
|
||||
use winapi::um::processthreadsapi::OpenProcess;
|
||||
use winapi::um::processthreadsapi::SetPriorityClass;
|
||||
use winapi::um::winbase::ABOVE_NORMAL_PRIORITY_CLASS;
|
||||
use winapi::um::winbase::BELOW_NORMAL_PRIORITY_CLASS;
|
||||
use winapi::um::winbase::HIGH_PRIORITY_CLASS;
|
||||
use winapi::um::winbase::IDLE_PRIORITY_CLASS;
|
||||
use winapi::um::winbase::NORMAL_PRIORITY_CLASS;
|
||||
use winapi::um::winbase::REALTIME_PRIORITY_CLASS;
|
||||
use winapi::um::winnt::PROCESS_QUERY_LIMITED_INFORMATION;
|
||||
|
||||
// Taken from: https://github.com/libuv/libuv/blob/a877ca2435134ef86315326ef4ef0c16bdbabf17/include/uv.h#L1318-L1323
|
||||
const PRIORITY_LOW: i32 = 19;
|
||||
const PRIORITY_BELOW_NORMAL: i32 = 10;
|
||||
const PRIORITY_NORMAL: i32 = 0;
|
||||
const PRIORITY_ABOVE_NORMAL: i32 = -7;
|
||||
const PRIORITY_HIGHEST: i32 = -20;
|
||||
|
||||
// Ported from: https://github.com/libuv/libuv/blob/a877ca2435134ef86315326ef4ef0c16bdbabf17/src/win/util.c#L1649-L1685
|
||||
pub fn get_priority(pid: u32) -> Result<i32, AnyError> {
|
||||
unsafe {
|
||||
let handle = if pid == 0 {
|
||||
GetCurrentProcess()
|
||||
} else {
|
||||
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid as DWORD)
|
||||
};
|
||||
if handle == NULL {
|
||||
Err(std::io::Error::last_os_error().into())
|
||||
} else {
|
||||
let result = match GetPriorityClass(handle) {
|
||||
0 => Err(std::io::Error::last_os_error().into()),
|
||||
REALTIME_PRIORITY_CLASS => Ok(PRIORITY_HIGHEST),
|
||||
HIGH_PRIORITY_CLASS => Ok(PRIORITY_HIGH),
|
||||
ABOVE_NORMAL_PRIORITY_CLASS => Ok(PRIORITY_ABOVE_NORMAL),
|
||||
NORMAL_PRIORITY_CLASS => Ok(PRIORITY_NORMAL),
|
||||
BELOW_NORMAL_PRIORITY_CLASS => Ok(PRIORITY_BELOW_NORMAL),
|
||||
IDLE_PRIORITY_CLASS => Ok(PRIORITY_LOW),
|
||||
_ => Ok(PRIORITY_LOW),
|
||||
};
|
||||
CloseHandle(handle);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ported from: https://github.com/libuv/libuv/blob/a877ca2435134ef86315326ef4ef0c16bdbabf17/src/win/util.c#L1688-L1719
|
||||
pub fn set_priority(pid: u32, priority: i32) -> Result<(), AnyError> {
|
||||
unsafe {
|
||||
let handle = if pid == 0 {
|
||||
GetCurrentProcess()
|
||||
} else {
|
||||
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid as DWORD)
|
||||
};
|
||||
if handle == NULL {
|
||||
Err(std::io::Error::last_os_error().into())
|
||||
} else {
|
||||
let priority_class =
|
||||
if priority < PRIORITY_HIGHEST || priority > PRIORITY_LOW {
|
||||
return Err(type_error("Invalid priority"));
|
||||
} else if priority < PRIORITY_HIGH {
|
||||
REALTIME_PRIORITY_CLASS
|
||||
} else if priority < PRIORITY_ABOVE_NORMAL {
|
||||
HIGH_PRIORITY_CLASS
|
||||
} else if priority < PRIORITY_NORMAL {
|
||||
ABOVE_NORMAL_PRIORITY_CLASS
|
||||
} else if priority < PRIORITY_BELOW_NORMAL {
|
||||
NORMAL_PRIORITY_CLASS
|
||||
} else if priority < PRIORITY_LOW {
|
||||
BELOW_NORMAL_PRIORITY_CLASS
|
||||
} else {
|
||||
IDLE_PRIORITY_CLASS
|
||||
};
|
||||
|
||||
let result = match SetPriorityClass(handle, priority_class) {
|
||||
FALSE => Err(std::io::Error::last_os_error().into()),
|
||||
_ => Ok(()),
|
||||
};
|
||||
CloseHandle(handle);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2497,6 +2497,19 @@ export class ERR_FS_RMDIR_ENOTDIR extends NodeSystemError {
|
|||
}
|
||||
}
|
||||
|
||||
export class ERR_OS_NO_HOMEDIR extends NodeSystemError {
|
||||
constructor() {
|
||||
const code = isWindows ? "ENOENT" : "ENOTDIR";
|
||||
const ctx: NodeSystemErrorCtx = {
|
||||
message: "not a directory",
|
||||
syscall: "home",
|
||||
code,
|
||||
errno: isWindows ? osConstants.errno.ENOENT : osConstants.errno.ENOTDIR,
|
||||
};
|
||||
super(code, ctx, "Path is not a directory");
|
||||
}
|
||||
}
|
||||
|
||||
interface UvExceptionContext {
|
||||
syscall: string;
|
||||
path?: string;
|
||||
|
|
|
@ -23,15 +23,16 @@
|
|||
// TODO(petamoriken): enable prefer-primordials for node polyfills
|
||||
// deno-lint-ignore-file prefer-primordials
|
||||
|
||||
import { notImplemented } from "ext:deno_node/_utils.ts";
|
||||
const core = globalThis.__bootstrap.core;
|
||||
import { validateIntegerRange } from "ext:deno_node/_utils.ts";
|
||||
import process from "node:process";
|
||||
import { isWindows, osType } from "ext:deno_node/_util/os.ts";
|
||||
import { ERR_OS_NO_HOMEDIR } from "ext:deno_node/internal/errors.ts";
|
||||
import { os } from "ext:deno_node/internal_binding/constants.ts";
|
||||
import { osUptime } from "ext:runtime/30_os.js";
|
||||
export const constants = os;
|
||||
import { Buffer } from "ext:deno_node/internal/buffer.mjs";
|
||||
|
||||
const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno_std/issues/1436";
|
||||
export const constants = os;
|
||||
|
||||
interface CPUTimes {
|
||||
/** The number of milliseconds the CPU has spent in user mode */
|
||||
|
@ -93,8 +94,8 @@ interface UserInfo {
|
|||
username: string;
|
||||
uid: number;
|
||||
gid: number;
|
||||
shell: string;
|
||||
homedir: string;
|
||||
shell: string | null;
|
||||
homedir: string | null;
|
||||
}
|
||||
|
||||
export function arch(): string {
|
||||
|
@ -161,7 +162,7 @@ export function freemem(): number {
|
|||
/** Not yet implemented */
|
||||
export function getPriority(pid = 0): number {
|
||||
validateIntegerRange(pid, "pid");
|
||||
notImplemented(SEE_GITHUB_ISSUE);
|
||||
return core.ops.op_node_os_get_priority(pid);
|
||||
}
|
||||
|
||||
/** Returns the string path of the current user's home directory. */
|
||||
|
@ -257,7 +258,7 @@ export function setPriority(pid: number, priority?: number) {
|
|||
validateIntegerRange(pid, "pid");
|
||||
validateIntegerRange(priority, "priority", -20, 19);
|
||||
|
||||
notImplemented(SEE_GITHUB_ISSUE);
|
||||
core.ops.op_node_os_set_priority(pid, priority);
|
||||
}
|
||||
|
||||
/** Returns the operating system's default directory for temporary files as a string. */
|
||||
|
@ -317,10 +318,40 @@ export function uptime(): number {
|
|||
|
||||
/** Not yet implemented */
|
||||
export function userInfo(
|
||||
// deno-lint-ignore no-unused-vars
|
||||
options: UserInfoOptions = { encoding: "utf-8" },
|
||||
): UserInfo {
|
||||
notImplemented(SEE_GITHUB_ISSUE);
|
||||
const uid = Deno.uid();
|
||||
const gid = Deno.gid();
|
||||
|
||||
if (isWindows) {
|
||||
uid = -1;
|
||||
gid = -1;
|
||||
}
|
||||
|
||||
// TODO(@crowlKats): figure out how to do this correctly:
|
||||
// The value of homedir returned by os.userInfo() is provided by the operating system.
|
||||
// This differs from the result of os.homedir(), which queries environment
|
||||
// variables for the home directory before falling back to the operating system response.
|
||||
let _homedir = homedir();
|
||||
if (!_homedir) {
|
||||
throw new ERR_OS_NO_HOMEDIR();
|
||||
}
|
||||
let shell = isWindows ? (Deno.env.get("SHELL") || null) : null;
|
||||
let username = core.ops.op_node_os_username();
|
||||
|
||||
if (options?.encoding === "buffer") {
|
||||
_homedir = _homedir ? Buffer.from(_homedir) : _homedir;
|
||||
shell = shell ? Buffer.from(shell) : shell;
|
||||
username = Buffer.from(username);
|
||||
}
|
||||
|
||||
return {
|
||||
uid,
|
||||
gid,
|
||||
homedir: _homedir,
|
||||
shell,
|
||||
username,
|
||||
};
|
||||
}
|
||||
|
||||
export const EOL = isWindows ? "\r\n" : "\n";
|
||||
|
|
|
@ -139,6 +139,13 @@ mod startup_snapshot {
|
|||
fn check_read(&self, _p: &Path) -> Result<(), deno_core::error::AnyError> {
|
||||
unreachable!("snapshotting!")
|
||||
}
|
||||
fn check_sys(
|
||||
&self,
|
||||
_kind: &str,
|
||||
_api_name: &str,
|
||||
) -> Result<(), deno_core::error::AnyError> {
|
||||
unreachable!("snapshotting!")
|
||||
}
|
||||
}
|
||||
|
||||
impl deno_net::NetPermissions for Permissions {
|
||||
|
|
|
@ -1886,6 +1886,10 @@ impl deno_node::NodePermissions for PermissionsContainer {
|
|||
fn check_read(&self, path: &Path) -> Result<(), AnyError> {
|
||||
self.0.lock().read.check(path, None)
|
||||
}
|
||||
|
||||
fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
|
||||
self.0.lock().sys.check(kind, Some(api_name))
|
||||
}
|
||||
}
|
||||
|
||||
impl deno_net::NetPermissions for PermissionsContainer {
|
||||
|
|
Loading…
Reference in a new issue