summaryrefslogtreecommitdiff
path: root/runtime/ops
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/ops')
-rw-r--r--runtime/ops/process.rs54
-rw-r--r--runtime/ops/signal.rs144
2 files changed, 150 insertions, 48 deletions
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index ab303e210..bd6328ae9 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -301,7 +301,6 @@ pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
use deno_core::error::type_error;
use std::io::Error;
use std::io::ErrorKind::NotFound;
- use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::FALSE;
use winapi::shared::minwindef::TRUE;
use winapi::shared::winerror::ERROR_INVALID_PARAMETER;
@@ -309,14 +308,46 @@ pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::OpenProcess;
use winapi::um::processthreadsapi::TerminateProcess;
+ use winapi::um::wincon::GenerateConsoleCtrlEvent;
+ use winapi::um::wincon::{CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_C_EVENT};
use winapi::um::winnt::PROCESS_TERMINATE;
- if !matches!(signal, "SIGKILL" | "SIGTERM") {
- Err(type_error(format!("Invalid signal: {}", signal)))
- } else if pid <= 0 {
- Err(type_error("Invalid pid"))
- } else {
- let handle = unsafe { OpenProcess(PROCESS_TERMINATE, FALSE, pid as DWORD) };
+ if pid < 0 {
+ return Err(type_error(format!(
+ "Invalid process id (pid) {} for signal {}.",
+ pid, signal
+ )));
+ }
+
+ if matches!(signal, "SIGINT" | "SIGBREAK" | "SIGHUP") {
+ let is_generated = unsafe {
+ GenerateConsoleCtrlEvent(
+ match signal {
+ "SIGINT" => CTRL_C_EVENT,
+ "SIGBREAK" => CTRL_BREAK_EVENT,
+ // Need tokio::windows::signal::CtrlClose or equivalent
+ // in signal.rs to get this working
+ "SIGHUP" => CTRL_CLOSE_EVENT,
+ _ => unreachable!(),
+ },
+ pid as u32,
+ )
+ };
+ match is_generated {
+ FALSE => {
+ Err(Error::from_raw_os_error(unsafe { GetLastError() } as i32).into())
+ }
+ TRUE => Ok(()),
+ _ => unreachable!(),
+ }
+ } else if matches!(signal, "SIGKILL" | "SIGTERM") {
+ // PID 0 = System Idle Process and can't be opened.
+ if pid == 0 {
+ return Err(type_error(format!("Cannot use {} on PID 0", signal)));
+ }
+
+ let handle = unsafe { OpenProcess(PROCESS_TERMINATE, FALSE, pid as u32) };
+
if handle.is_null() {
let err = match unsafe { GetLastError() } {
ERROR_INVALID_PARAMETER => Error::from(NotFound), // Invalid `pid`.
@@ -324,14 +355,19 @@ pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
};
Err(err.into())
} else {
- let r = unsafe { TerminateProcess(handle, 1) };
+ let is_terminated = unsafe { TerminateProcess(handle, 1) };
unsafe { CloseHandle(handle) };
- match r {
+ match is_terminated {
FALSE => Err(Error::last_os_error().into()),
TRUE => Ok(()),
_ => unreachable!(),
}
}
+ } else {
+ Err(type_error(format!(
+ "Signal {} is unsupported on Windows.",
+ signal
+ )))
}
}
diff --git a/runtime/ops/signal.rs b/runtime/ops/signal.rs
index 72e402094..95c166787 100644
--- a/runtime/ops/signal.rs
+++ b/runtime/ops/signal.rs
@@ -1,35 +1,24 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
-#[cfg(not(unix))]
-use deno_core::error::generic_error;
-#[cfg(not(target_os = "windows"))]
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op;
-
-use deno_core::Extension;
-#[cfg(unix)]
-use deno_core::OpState;
-#[cfg(unix)]
-use std::cell::RefCell;
-#[cfg(unix)]
-use std::rc::Rc;
-
-#[cfg(unix)]
use deno_core::AsyncRefCell;
-#[cfg(unix)]
use deno_core::CancelFuture;
-#[cfg(unix)]
use deno_core::CancelHandle;
-#[cfg(unix)]
+use deno_core::Extension;
+use deno_core::OpState;
use deno_core::RcRef;
-#[cfg(unix)]
use deno_core::Resource;
-#[cfg(unix)]
use deno_core::ResourceId;
-#[cfg(unix)]
+
use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+
#[cfg(unix)]
use tokio::signal::unix::{signal, Signal, SignalKind};
+#[cfg(windows)]
+use tokio::signal::windows::{ctrl_break, ctrl_c, CtrlBreak, CtrlC};
pub fn init() -> Extension {
Extension::builder()
@@ -60,6 +49,55 @@ impl Resource for SignalStreamResource {
}
}
+// TODO: CtrlClose could be mapped to SIGHUP but that needs a
+// tokio::windows::signal::CtrlClose type, or something from a different crate
+#[cfg(windows)]
+enum WindowsSignal {
+ Sigint(CtrlC),
+ Sigbreak(CtrlBreak),
+}
+
+#[cfg(windows)]
+impl From<CtrlC> for WindowsSignal {
+ fn from(ctrl_c: CtrlC) -> Self {
+ WindowsSignal::Sigint(ctrl_c)
+ }
+}
+
+#[cfg(windows)]
+impl From<CtrlBreak> for WindowsSignal {
+ fn from(ctrl_break: CtrlBreak) -> Self {
+ WindowsSignal::Sigbreak(ctrl_break)
+ }
+}
+
+#[cfg(windows)]
+impl WindowsSignal {
+ pub async fn recv(&mut self) -> Option<()> {
+ match self {
+ WindowsSignal::Sigint(ctrl_c) => ctrl_c.recv().await,
+ WindowsSignal::Sigbreak(ctrl_break) => ctrl_break.recv().await,
+ }
+ }
+}
+
+#[cfg(windows)]
+struct SignalStreamResource {
+ signal: AsyncRefCell<WindowsSignal>,
+ cancel: CancelHandle,
+}
+
+#[cfg(windows)]
+impl Resource for SignalStreamResource {
+ fn name(&self) -> Cow<str> {
+ "signal".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ self.cancel.cancel();
+ }
+}
+
#[cfg(target_os = "freebsd")]
pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
match s {
@@ -389,6 +427,28 @@ pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
}
}
+#[cfg(target_os = "windows")]
+pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
+ match s {
+ "SIGINT" => Ok(2),
+ "SIGBREAK" => Ok(21),
+ _ => Err(type_error(
+ "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK).",
+ )),
+ }
+}
+
+#[cfg(target_os = "windows")]
+pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
+ match s {
+ 2 => Ok("SIGINT"),
+ 21 => Ok("SIGBREAK"),
+ _ => Err(type_error(
+ "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK).",
+ )),
+ }
+}
+
#[cfg(unix)]
#[op]
fn op_signal_bind(
@@ -410,7 +470,31 @@ fn op_signal_bind(
Ok(rid)
}
-#[cfg(unix)]
+#[cfg(windows)]
+#[op]
+fn op_signal_bind(
+ state: &mut OpState,
+ sig: String,
+) -> Result<ResourceId, AnyError> {
+ let signo = signal_str_to_int(&sig)?;
+ let resource = SignalStreamResource {
+ signal: AsyncRefCell::new(match signo {
+ // SIGINT
+ 2 => ctrl_c()
+ .expect("There was an issue creating ctrl+c event stream.")
+ .into(),
+ // SIGBREAK
+ 21 => ctrl_break()
+ .expect("There was an issue creating ctrl+break event stream.")
+ .into(),
+ _ => unimplemented!(),
+ }),
+ cancel: Default::default(),
+ };
+ let rid = state.resource_table.add(resource);
+ Ok(rid)
+}
+
#[op]
async fn op_signal_poll(
state: Rc<RefCell<OpState>>,
@@ -420,6 +504,7 @@ async fn op_signal_poll(
.borrow_mut()
.resource_table
.get::<SignalStreamResource>(rid)?;
+
let cancel = RcRef::map(&resource, |r| &r.cancel);
let mut signal = RcRef::map(&resource, |r| &r.signal).borrow_mut().await;
@@ -429,7 +514,6 @@ async fn op_signal_poll(
}
}
-#[cfg(unix)]
#[op]
pub fn op_signal_unbind(
state: &mut OpState,
@@ -438,21 +522,3 @@ pub fn op_signal_unbind(
state.resource_table.close(rid)?;
Ok(())
}
-
-#[cfg(not(unix))]
-#[op]
-pub fn op_signal_bind() -> Result<(), AnyError> {
- Err(generic_error("not implemented"))
-}
-
-#[cfg(not(unix))]
-#[op]
-fn op_signal_unbind() -> Result<(), AnyError> {
- Err(generic_error("not implemented"))
-}
-
-#[cfg(not(unix))]
-#[op]
-async fn op_signal_poll() -> Result<(), AnyError> {
- Err(generic_error("not implemented"))
-}