use std::mem::MaybeUninit;
use deno_core::OpState;
use deno_core::ToV8;
use deno_core::op2;
use deno_permissions::PermissionCheckError;
use deno_permissions::PermissionsContainer;
use sys_traits::EnvHomeDir;
mod cpus;
pub mod priority;
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum OsError {
#[class(inherit)]
#[error(transparent)]
Priority(#[inherit] priority::PriorityError),
#[class(inherit)]
#[error(transparent)]
Permission(
#[from]
#[inherit]
PermissionCheckError,
),
#[class(inherit)]
#[error("Failed to get user info")]
FailedToGetUserInfo(
#[source]
#[inherit]
std::io::Error,
),
#[class(inherit)]
#[error("Failed to get groups")]
FailedToGetGroups(
#[source]
#[inherit]
std::io::Error,
),
}
#[op2(fast, stack_trace)]
pub fn op_node_os_get_priority(
state: &mut OpState,
pid: u32,
) -> Result<i32, OsError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions.check_sys("getPriority", "node:os.getPriority()")?;
}
priority::get_priority(pid).map_err(OsError::Priority)
}
#[op2(fast, stack_trace)]
pub fn op_node_os_set_priority(
state: &mut OpState,
pid: u32,
priority: i32,
) -> Result<(), OsError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions.check_sys("setPriority", "node:os.setPriority()")?;
}
priority::set_priority(pid, priority).map_err(OsError::Priority)
}
#[derive(ToV8)]
pub struct UserInfo {
username: String,
homedir: String,
shell: Option<String>,
}
#[cfg(unix)]
fn get_user_info(uid: u32) -> Result<UserInfo, OsError> {
use std::ffi::CStr;
let mut pw: MaybeUninit<libc::passwd> = MaybeUninit::uninit();
let mut result: *mut libc::passwd = std::ptr::null_mut();
let max_buf_size = unsafe { libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) };
let buf_size = if max_buf_size < 0 {
16_384
} else {
max_buf_size as usize
};
let mut buf = {
let mut b = Vec::<MaybeUninit<libc::c_char>>::with_capacity(buf_size);
unsafe {
b.set_len(buf_size);
}
b
};
let s = unsafe {
libc::getpwuid_r(
uid,
pw.as_mut_ptr(),
buf.as_mut_ptr().cast(),
buf_size,
std::ptr::addr_of_mut!(result),
)
};
if result.is_null() {
if s != 0 {
return Err(
OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
);
} else {
return Err(OsError::FailedToGetUserInfo(std::io::Error::from(
std::io::ErrorKind::NotFound,
)));
}
}
let pw = unsafe { pw.assume_init() };
let username = unsafe { CStr::from_ptr(pw.pw_name) };
let homedir = unsafe { CStr::from_ptr(pw.pw_dir) };
let shell = unsafe { CStr::from_ptr(pw.pw_shell) };
Ok(UserInfo {
username: username.to_string_lossy().into_owned(),
homedir: homedir.to_string_lossy().into_owned(),
shell: Some(shell.to_string_lossy().into_owned()),
})
}
#[cfg(windows)]
fn get_user_info(_uid: u32) -> Result<UserInfo, OsError> {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::Threading::GetCurrentProcess;
use windows_sys::Win32::System::Threading::OpenProcessToken;
use windows_sys::Win32::UI::Shell::GetUserProfileDirectoryW;
struct Handle(HANDLE);
impl Drop for Handle {
fn drop(&mut self) {
unsafe {
CloseHandle(self.0);
}
}
}
let mut token: MaybeUninit<HANDLE> = MaybeUninit::uninit();
unsafe {
if OpenProcessToken(
GetCurrentProcess(),
windows_sys::Win32::Security::TOKEN_READ,
token.as_mut_ptr(),
) == 0
{
return Err(
OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
);
}
}
let token = Handle(unsafe { token.assume_init() });
let mut bufsize = 0;
unsafe {
GetUserProfileDirectoryW(token.0, std::ptr::null_mut(), &mut bufsize);
let err = GetLastError();
if err != ERROR_INSUFFICIENT_BUFFER {
return Err(OsError::FailedToGetUserInfo(
std::io::Error::from_raw_os_error(err as i32),
));
}
}
let mut path = vec![0; bufsize as usize];
unsafe {
if GetUserProfileDirectoryW(token.0, path.as_mut_ptr(), &mut bufsize) == 0 {
return Err(
OsError::FailedToGetUserInfo(std::io::Error::last_os_error()),
);
}
}
path.pop();
let homedir_wide = OsString::from_wide(&path);
let homedir = homedir_wide.to_string_lossy().into_owned();
Ok(UserInfo {
username: deno_whoami::username(),
homedir,
shell: None,
})
}
#[op2(stack_trace)]
pub fn op_node_os_user_info(
state: &mut OpState,
#[smi] uid: u32,
) -> Result<UserInfo, OsError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions
.check_sys("userInfo", "node:os.userInfo()")
.map_err(OsError::Permission)?;
}
get_user_info(uid)
}
#[op2(fast, stack_trace)]
pub fn op_geteuid(state: &mut OpState) -> Result<u32, PermissionCheckError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions.check_sys("uid", "node:os.geteuid()")?;
}
#[cfg(windows)]
let euid = 0;
#[cfg(unix)]
let euid = unsafe { libc::geteuid() };
Ok(euid)
}
#[op2(fast, stack_trace)]
pub fn op_getegid(state: &mut OpState) -> Result<u32, PermissionCheckError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions.check_sys("getegid", "node:os.getegid()")?;
}
#[cfg(windows)]
let egid = 0;
#[cfg(unix)]
let egid = unsafe { libc::getegid() };
Ok(egid)
}
#[op2(stack_trace)]
#[serde]
pub fn op_getgroups(state: &mut OpState) -> Result<Vec<u32>, OsError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions.check_sys("gid", "node:process.getgroups()")?;
}
#[cfg(windows)]
{
Ok(vec![])
}
#[cfg(unix)]
{
let ngroups = unsafe { libc::getgroups(0, std::ptr::null_mut()) };
if ngroups < 0 {
return Err(OsError::FailedToGetGroups(std::io::Error::last_os_error()));
}
if ngroups == 0 {
return Ok(vec![]);
}
let mut groups: Vec<libc::gid_t> = vec![0; ngroups as usize];
let ngroups = unsafe { libc::getgroups(ngroups, groups.as_mut_ptr()) };
if ngroups < 0 {
return Err(OsError::FailedToGetGroups(std::io::Error::last_os_error()));
}
groups.truncate(ngroups as usize);
#[allow(
clippy::unnecessary_cast,
reason = "gid_t may not be u32 on all platforms"
)]
Ok(groups.iter().map(|&g| g as u32).collect())
}
}
#[op2(stack_trace)]
#[serde]
pub fn op_cpus(state: &mut OpState) -> Result<Vec<cpus::CpuInfo>, OsError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions.check_sys("cpus", "node:os.cpus()")?;
}
Ok(cpus::cpu_info().unwrap_or_default())
}
#[op2(stack_trace)]
#[string]
pub fn op_homedir(
state: &mut OpState,
) -> Result<Option<String>, PermissionCheckError> {
{
let permissions = state.borrow_mut::<PermissionsContainer>();
permissions.check_sys("homedir", "node:os.homedir()")?;
}
Ok(
sys_traits::impls::RealSys
.env_home_dir()
.map(|path| path.to_string_lossy().into_owned()),
)
}