deno_node/ops/
process.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2
3use deno_core::OpState;
4use deno_core::op2;
5use deno_core::v8;
6use deno_permissions::PermissionCheckError;
7use deno_permissions::PermissionsContainer;
8#[cfg(unix)]
9use nix::unistd::Gid;
10#[cfg(unix)]
11use nix::unistd::Group;
12#[cfg(unix)]
13use nix::unistd::Uid;
14#[cfg(unix)]
15use nix::unistd::User;
16
17#[derive(Debug, thiserror::Error, deno_error::JsError)]
18pub enum ProcessError {
19  #[class(inherit)]
20  #[error(transparent)]
21  Permission(
22    #[from]
23    #[inherit]
24    PermissionCheckError,
25  ),
26  #[class(generic)]
27  #[error("{0} identifier does not exist: {1}")]
28  #[property("code" = "ERR_UNKNOWN_CREDENTIAL")]
29  UnknownCredential(String, String),
30  #[class(inherit)]
31  #[error(transparent)]
32  Io(#[from] std::io::Error),
33  #[class(generic)]
34  #[error("Operation not supported on this platform")]
35  NotSupported,
36  #[class(type)]
37  #[error("Invalid {0} parameter")]
38  InvalidParam(String),
39}
40
41#[cfg(unix)]
42impl From<nix::Error> for ProcessError {
43  fn from(err: nix::Error) -> Self {
44    ProcessError::Io(std::io::Error::from_raw_os_error(err as i32))
45  }
46}
47
48#[cfg(unix)]
49fn kill(pid: i32, sig: i32) -> i32 {
50  // SAFETY: FFI call to libc
51  if unsafe { libc::kill(pid, sig) } < 0 {
52    std::io::Error::last_os_error().raw_os_error().unwrap()
53  } else {
54    0
55  }
56}
57
58#[cfg(not(unix))]
59fn kill(pid: i32, _sig: i32) -> i32 {
60  match deno_subprocess_windows::process_kill(pid, _sig) {
61    Ok(_) => 0,
62    Err(e) => e.as_uv_error(),
63  }
64}
65
66#[op2(fast, stack_trace)]
67pub fn op_node_process_kill(
68  state: &mut OpState,
69  #[smi] pid: i32,
70  #[smi] sig: i32,
71) -> Result<i32, deno_permissions::PermissionCheckError> {
72  state
73    .borrow_mut::<PermissionsContainer>()
74    .check_run_all("process.kill")?;
75  Ok(kill(pid, sig))
76}
77
78#[op2(fast)]
79pub fn op_process_abort() {
80  std::process::abort();
81}
82
83#[cfg(not(any(target_os = "android", target_os = "windows")))]
84enum Id {
85  Number(u32),
86  Name(String),
87}
88
89#[cfg(not(any(target_os = "android", target_os = "windows")))]
90fn get_group_id(name: &str) -> Result<Gid, ProcessError> {
91  let group = Group::from_name(name)?;
92
93  if let Some(group) = group {
94    Ok(group.gid)
95  } else {
96    Err(ProcessError::UnknownCredential(
97      "Group".to_string(),
98      name.to_string(),
99    ))
100  }
101}
102
103#[cfg(not(any(target_os = "android", target_os = "windows")))]
104fn serialize_id<'a>(
105  scope: &mut v8::PinScope<'a, '_>,
106  value: v8::Local<'a, v8::Value>,
107) -> Result<Id, ProcessError> {
108  if value.is_number() {
109    let num = value.uint32_value(scope).unwrap();
110    return Ok(Id::Number(num));
111  }
112
113  if value.is_string() {
114    let name = value.to_string(scope).unwrap();
115    return Ok(Id::Name(name.to_rust_string_lossy(scope)));
116  }
117
118  Err(ProcessError::InvalidParam("id".to_string()))
119}
120
121#[cfg(not(any(target_os = "android", target_os = "windows")))]
122#[op2(fast, stack_trace)]
123pub fn op_node_process_setegid<'a>(
124  scope: &mut v8::PinScope<'a, '_>,
125  state: &mut OpState,
126  id: v8::Local<'a, v8::Value>,
127) -> Result<(), ProcessError> {
128  {
129    let permissions = state.borrow_mut::<PermissionsContainer>();
130    permissions.check_sys("setegid", "node:process.setegid")?;
131  }
132
133  let gid = match serialize_id(scope, id)? {
134    Id::Number(number) => Gid::from_raw(number),
135    Id::Name(name) => get_group_id(&name)?,
136  };
137
138  nix::unistd::setegid(gid)?;
139
140  Ok(())
141}
142
143#[cfg(any(target_os = "android", target_os = "windows"))]
144#[op2(fast, stack_trace)]
145pub fn op_node_process_setegid(
146  _scope: &mut v8::PinScope<'_, '_>,
147  _state: &mut OpState,
148  _id: v8::Local<'_, v8::Value>,
149) -> Result<(), ProcessError> {
150  Err(ProcessError::NotSupported)
151}
152
153#[cfg(not(any(target_os = "android", target_os = "windows")))]
154fn get_user_id(name: &str) -> Result<Uid, ProcessError> {
155  let user = User::from_name(name)?;
156
157  if let Some(user) = user {
158    Ok(user.uid)
159  } else {
160    Err(ProcessError::UnknownCredential(
161      "User".to_string(),
162      name.to_string(),
163    ))
164  }
165}
166
167#[cfg(not(any(target_os = "android", target_os = "windows")))]
168#[op2(fast, stack_trace)]
169pub fn op_node_process_seteuid<'a>(
170  scope: &mut v8::PinScope<'a, '_>,
171  state: &mut OpState,
172  id: v8::Local<'a, v8::Value>,
173) -> Result<(), ProcessError> {
174  {
175    let permissions = state.borrow_mut::<PermissionsContainer>();
176    permissions.check_sys("seteuid", "node:process.seteuid")?;
177  }
178
179  let uid = match serialize_id(scope, id)? {
180    Id::Number(number) => Uid::from_raw(number),
181    Id::Name(name) => get_user_id(&name)?,
182  };
183
184  nix::unistd::seteuid(uid)?;
185
186  Ok(())
187}
188
189#[cfg(any(target_os = "android", target_os = "windows"))]
190#[op2(fast, stack_trace)]
191pub fn op_node_process_seteuid(
192  _scope: &mut v8::PinScope<'_, '_>,
193  _state: &mut OpState,
194  _id: v8::Local<'_, v8::Value>,
195) -> Result<(), ProcessError> {
196  Err(ProcessError::NotSupported)
197}
198
199#[cfg(not(any(target_os = "android", target_os = "windows")))]
200#[op2(fast, stack_trace)]
201pub fn op_node_process_setgid<'a>(
202  scope: &mut v8::PinScope<'a, '_>,
203  state: &mut OpState,
204  id: v8::Local<'a, v8::Value>,
205) -> Result<(), ProcessError> {
206  {
207    let permissions = state.borrow_mut::<PermissionsContainer>();
208    permissions.check_sys("setgid", "node:process.setgid")?;
209  }
210
211  let gid = match serialize_id(scope, id)? {
212    Id::Number(number) => Gid::from_raw(number),
213    Id::Name(name) => get_group_id(&name)?,
214  };
215
216  nix::unistd::setgid(gid)?;
217
218  Ok(())
219}
220
221#[cfg(any(target_os = "android", target_os = "windows"))]
222#[op2(fast, stack_trace)]
223pub fn op_node_process_setgid(
224  _scope: &mut v8::PinScope<'_, '_>,
225  _state: &mut OpState,
226  _id: v8::Local<'_, v8::Value>,
227) -> Result<(), ProcessError> {
228  Err(ProcessError::NotSupported)
229}
230
231#[cfg(not(any(target_os = "android", target_os = "windows")))]
232#[op2(fast, stack_trace)]
233pub fn op_node_process_setuid<'a>(
234  scope: &mut v8::PinScope<'a, '_>,
235  state: &mut OpState,
236  id: v8::Local<'a, v8::Value>,
237) -> Result<(), ProcessError> {
238  {
239    let permissions = state.borrow_mut::<PermissionsContainer>();
240    permissions.check_sys("setuid", "node:process.setuid")?;
241  }
242
243  let uid = match serialize_id(scope, id)? {
244    Id::Number(number) => Uid::from_raw(number),
245    Id::Name(name) => get_user_id(&name)?,
246  };
247
248  nix::unistd::setuid(uid)?;
249
250  Ok(())
251}
252
253#[cfg(any(target_os = "android", target_os = "windows"))]
254#[op2(fast, stack_trace)]
255pub fn op_node_process_setuid(
256  _scope: &mut v8::PinScope<'_, '_>,
257  _state: &mut OpState,
258  _id: v8::Local<'_, v8::Value>,
259) -> Result<(), ProcessError> {
260  Err(ProcessError::NotSupported)
261}