1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//
// Syd: rock-solid application kernel
// src/utils/syd-pds.rs: Run a command with parent death signal set
//
// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0
use std::{
env,
ffi::OsString,
os::unix::process::CommandExt,
process::{Command, ExitCode},
str::FromStr,
};
use nix::{errno::Errno, sys::signal::Signal};
use syd::{compat::set_pdeathsig, config::*};
// Set global allocator to GrapheneOS allocator.
#[cfg(all(
not(coverage),
not(feature = "prof"),
not(target_os = "android"),
not(target_arch = "riscv64"),
target_page_size_4k,
target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;
// Set global allocator to tcmalloc if profiling is enabled.
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;
syd::main! {
use lexopt::prelude::*;
syd::set_sigpipe_dfl()?;
// Parse CLI options.
//
// Note, option parsing is POSIXly correct:
// POSIX recommends that no more options are parsed after the first
// positional argument. The other arguments are then all treated as
// positional arguments.
// See: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02
let mut opt_sig = Signal::SIGKILL;
let mut opt_cmd = env::var_os(ENV_SH).unwrap_or(OsString::from(SYD_SH));
let mut opt_arg = Vec::new();
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
match arg {
Short('h') => {
help();
return Ok(ExitCode::SUCCESS);
}
Short('s') => {
let sig = parser.value()?;
opt_sig = match sig.parse::<i32>() {
Ok(value) => match Signal::try_from(value) {
Ok(value) => value,
Err(_) => {
eprintln!("syd-pds: Invalid signal specified with -s!");
return Err(Errno::EINVAL.into());
}
},
Err(_) => {
let mut value = sig.parse::<String>()?.to_ascii_uppercase();
if !value.starts_with("SIG") {
value = format!("SIG{value}");
}
match Signal::from_str(&value) {
Ok(value) => value,
Err(_) => {
eprintln!("syd-pds: Invalid signal specified with -s!");
return Err(Errno::EINVAL.into());
}
}
}
};
}
Value(prog) => {
opt_cmd = prog;
opt_arg.extend(parser.raw_args()?);
}
_ => return Err(arg.unexpected().into()),
}
}
// Set parent death signal to the specified signal or SIGKILL.
set_pdeathsig(Some(opt_sig))?;
// Execute command, /bin/sh by default.
//
// We can not use run_cmd here because if we fork,
// the parent-death-signal will not work as expected.
Ok(ExitCode::from(
127 + Command::new(opt_cmd)
.args(opt_arg)
.exec()
.raw_os_error()
.unwrap_or(0) as u8,
))
}
fn help() {
println!("Usage: syd-pds [-h] [-s signal] {{command [args...]}}");
println!("Run a command with parent death signal set.");
println!("Use -s to specify a signal, defaults to SIGKILL.");
}