Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ log = "0.4"
tokio = { version = "1", optional = true, default-features = false, features = ["sync"] }
# Public Suffix List used to stop the WPAD suffix walk at the registrable domain.
psl = "2"

# Runtime for the sandboxed PAC backend, in AOT mode only: `runtime` + `std`
# but deliberately NO `cranelift` (or `winch`/`pulley`), so this build cannot
# compile wasm at all — it can only `Module::deserialize` the artifact that
Expand Down Expand Up @@ -116,6 +117,9 @@ windows-sys = { version = "0.60", features = [
# non-Windows dependency above.
rquickjs-sys = { version = "0.12.1", optional = true }

[target.'cfg(target_os = "linux")'.dependencies]
libc = "0.2"
Comment on lines +120 to +121

[dev-dependencies]
tokio = { version = "1", features = ["sync", "rt", "macros", "time"] }

Expand Down
45 changes: 33 additions & 12 deletions src/platform/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::bypass::BypassRules;
use crate::types::{LinuxProxyConfig, PlatformProxyConfig, ProxyKind};
use std::collections::HashMap;
use std::io::BufRead;
use std::os::unix::process::CommandExt;
use std::process::{Child, Command, Stdio};
use std::sync::{Arc, Mutex};

Expand Down Expand Up @@ -128,19 +129,15 @@ pub(crate) struct Watcher {
}

pub(crate) fn spawn_watcher(on_change: Arc<dyn Fn() + Send + Sync>) -> Watcher {
let mut spawned = Command::new("dconf")
.args(["watch", "/system/proxy/"])
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn();
let mut dconf = Command::new("dconf");
dconf.args(["watch", "/system/proxy/"]);
configure_watcher_command(&mut dconf);
let mut spawned = dconf.spawn();
if spawned.is_err() {
spawned = Command::new("gsettings")
.args(["monitor", "org.gnome.system.proxy"])
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn();
let mut gsettings = Command::new("gsettings");
gsettings.args(["monitor", "org.gnome.system.proxy"]);
configure_watcher_command(&mut gsettings);
spawned = gsettings.spawn();
}
let Ok(mut child) = spawned else {
log::debug!(
Expand Down Expand Up @@ -173,6 +170,30 @@ pub(crate) fn spawn_watcher(on_change: Arc<dyn Fn() + Send + Sync>) -> Watcher {
Watcher { child, thread }
}

/// Configure a proxy watcher to terminate if its owning process exits without dropping it.
fn configure_watcher_command(command: &mut Command) {
let expected_parent = std::process::id() as libc::pid_t;
command
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null());

// SAFETY: pre_exec runs after fork in the single-threaded child. prctl and
// getppid are async-signal-safe Linux system calls and do not access Rust state.
unsafe {
command.pre_exec(move || {
if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) == -1 {
Comment on lines +184 to +185
return Err(std::io::Error::last_os_error());
}
// The parent may have exited between fork and PR_SET_PDEATHSIG.
if libc::getppid() != expected_parent {
libc::raise(libc::SIGTERM);
}
Ok(())
});
}
}

impl Drop for Watcher {
fn drop(&mut self) {
if let Some(mut child) = self.child.lock().unwrap_or_else(|e| e.into_inner()).take() {
Expand Down