Add ulimit builtin and optimize shed -c to exec single commands directly without forking
This commit is contained in:
15
Cargo.toml
15
Cargo.toml
@@ -17,7 +17,20 @@ env_logger = "0.11.9"
|
|||||||
glob = "0.3.2"
|
glob = "0.3.2"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
nix = { version = "0.29.0", features = ["uio", "term", "user", "hostname", "fs", "default", "signal", "process", "event", "ioctl", "poll"] }
|
nix = { version = "0.29.0", features = [
|
||||||
|
"uio",
|
||||||
|
"term",
|
||||||
|
"user",
|
||||||
|
"resource",
|
||||||
|
"hostname",
|
||||||
|
"fs",
|
||||||
|
"default",
|
||||||
|
"signal",
|
||||||
|
"process",
|
||||||
|
"event",
|
||||||
|
"ioctl",
|
||||||
|
"poll"
|
||||||
|
] }
|
||||||
rand = "0.10.0"
|
rand = "0.10.0"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
scopeguard = "1.2.0"
|
scopeguard = "1.2.0"
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ pub mod test; // [[ ]] thing
|
|||||||
pub mod trap;
|
pub mod trap;
|
||||||
pub mod varcmds;
|
pub mod varcmds;
|
||||||
pub mod zoltraak;
|
pub mod zoltraak;
|
||||||
|
pub mod resource;
|
||||||
|
|
||||||
pub const BUILTINS: [&str; 47] = [
|
pub const BUILTINS: [&str; 49] = [
|
||||||
"echo", "cd", "read", "export", "local", "pwd", "source", "shift", "jobs", "fg", "bg", "disown",
|
"echo", "cd", "read", "export", "local", "pwd", "source", "shift", "jobs", "fg", "bg", "disown",
|
||||||
"alias", "unalias", "return", "break", "continue", "exit", "zoltraak", "shopt", "builtin",
|
"alias", "unalias", "return", "break", "continue", "exit", "zoltraak", "shopt", "builtin",
|
||||||
"command", "trap", "pushd", "popd", "dirs", "exec", "eval", "true", "false", ":", "readonly",
|
"command", "trap", "pushd", "popd", "dirs", "exec", "eval", "true", "false", ":", "readonly",
|
||||||
"unset", "complete", "compgen", "map", "pop", "fpop", "push", "fpush", "rotate", "wait", "type",
|
"unset", "complete", "compgen", "map", "pop", "fpop", "push", "fpush", "rotate", "wait", "type",
|
||||||
"getopts", "keymap", "read_key", "autocmd",
|
"getopts", "keymap", "read_key", "autocmd", "ulimit", "umask"
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn true_builtin() -> ShResult<()> {
|
pub fn true_builtin() -> ShResult<()> {
|
||||||
|
|||||||
169
src/builtin/resource.rs
Normal file
169
src/builtin/resource.rs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
use ariadne::Fmt;
|
||||||
|
use nix::sys::resource::{Resource, getrlimit, setrlimit};
|
||||||
|
use yansi::Color;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
getopt::{Opt, OptSpec, get_opts_from_tokens}, libsh::error::{ShErr, ShErrKind, ShResult, ShResultExt, next_color}, parse::{NdRule, Node, execute::prepare_argv}, prelude::*, state::{self}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn ulimit_opt_spec() -> [OptSpec;5] {
|
||||||
|
[
|
||||||
|
OptSpec {
|
||||||
|
opt: Opt::Short('n'), // file descriptors
|
||||||
|
takes_arg: true,
|
||||||
|
},
|
||||||
|
OptSpec {
|
||||||
|
opt: Opt::Short('u'), // max user processes
|
||||||
|
takes_arg: true,
|
||||||
|
},
|
||||||
|
OptSpec {
|
||||||
|
opt: Opt::Short('s'), // stack size
|
||||||
|
takes_arg: true,
|
||||||
|
},
|
||||||
|
OptSpec {
|
||||||
|
opt: Opt::Short('c'), // core dump file size
|
||||||
|
takes_arg: true,
|
||||||
|
},
|
||||||
|
OptSpec {
|
||||||
|
opt: Opt::Short('v'), // virtual memory
|
||||||
|
takes_arg: true,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UlimitOpts {
|
||||||
|
fds: Option<u64>,
|
||||||
|
procs: Option<u64>,
|
||||||
|
stack: Option<u64>,
|
||||||
|
core: Option<u64>,
|
||||||
|
vmem: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ulimit_opts(opt: &[Opt]) -> ShResult<UlimitOpts> {
|
||||||
|
let mut opts = UlimitOpts {
|
||||||
|
fds: None,
|
||||||
|
procs: None,
|
||||||
|
stack: None,
|
||||||
|
core: None,
|
||||||
|
vmem: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
for o in opt {
|
||||||
|
match o {
|
||||||
|
Opt::ShortWithArg('n', arg) => {
|
||||||
|
opts.fds = Some(arg.parse().map_err(|_| ShErr::simple(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
format!("invalid argument for -n: {}", arg.fg(next_color())),
|
||||||
|
))?);
|
||||||
|
},
|
||||||
|
Opt::ShortWithArg('u', arg) => {
|
||||||
|
opts.procs = Some(arg.parse().map_err(|_| ShErr::simple(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
format!("invalid argument for -u: {}", arg.fg(next_color())),
|
||||||
|
))?);
|
||||||
|
},
|
||||||
|
Opt::ShortWithArg('s', arg) => {
|
||||||
|
opts.stack = Some(arg.parse().map_err(|_| ShErr::simple(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
format!("invalid argument for -s: {}", arg.fg(next_color())),
|
||||||
|
))?);
|
||||||
|
},
|
||||||
|
Opt::ShortWithArg('c', arg) => {
|
||||||
|
opts.core = Some(arg.parse().map_err(|_| ShErr::simple(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
format!("invalid argument for -c: {}", arg.fg(next_color())),
|
||||||
|
))?);
|
||||||
|
},
|
||||||
|
Opt::ShortWithArg('v', arg) => {
|
||||||
|
opts.vmem = Some(arg.parse().map_err(|_| ShErr::simple(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
format!("invalid argument for -v: {}", arg.fg(next_color())),
|
||||||
|
))?);
|
||||||
|
},
|
||||||
|
o => return Err(ShErr::simple(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
format!("invalid option: {}", o.fg(next_color())),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ulimit(node: Node) -> ShResult<()> {
|
||||||
|
let span = node.get_span();
|
||||||
|
let NdRule::Command {
|
||||||
|
assignments: _,
|
||||||
|
argv,
|
||||||
|
} = node.class
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, opts) = get_opts_from_tokens(argv, &ulimit_opt_spec()).promote_err(span.clone())?;
|
||||||
|
let ulimit_opts = get_ulimit_opts(&opts).promote_err(span.clone())?;
|
||||||
|
|
||||||
|
if let Some(fds) = ulimit_opts.fds {
|
||||||
|
let (_, hard) = getrlimit(Resource::RLIMIT_NOFILE).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to get file descriptor limit: {}", e),
|
||||||
|
))?;
|
||||||
|
setrlimit(Resource::RLIMIT_NOFILE, fds, hard).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to set file descriptor limit: {}", e),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
if let Some(procs) = ulimit_opts.procs {
|
||||||
|
let (_, hard) = getrlimit(Resource::RLIMIT_NPROC).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to get process limit: {}", e),
|
||||||
|
))?;
|
||||||
|
setrlimit(Resource::RLIMIT_NPROC, procs, hard).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to set process limit: {}", e),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
if let Some(stack) = ulimit_opts.stack {
|
||||||
|
let (_, hard) = getrlimit(Resource::RLIMIT_STACK).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to get stack size limit: {}", e),
|
||||||
|
))?;
|
||||||
|
setrlimit(Resource::RLIMIT_STACK, stack, hard).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to set stack size limit: {}", e),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
if let Some(core) = ulimit_opts.core {
|
||||||
|
let (_, hard) = getrlimit(Resource::RLIMIT_CORE).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to get core dump size limit: {}", e),
|
||||||
|
))?;
|
||||||
|
setrlimit(Resource::RLIMIT_CORE, core, hard).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to set core dump size limit: {}", e),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
if let Some(vmem) = ulimit_opts.vmem {
|
||||||
|
let (_, hard) = getrlimit(Resource::RLIMIT_AS).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to get virtual memory limit: {}", e),
|
||||||
|
))?;
|
||||||
|
setrlimit(Resource::RLIMIT_AS, vmem, hard).map_err(|e| ShErr::at(
|
||||||
|
ShErrKind::ExecFail,
|
||||||
|
span.clone(),
|
||||||
|
format!("failed to set virtual memory limit: {}", e),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
state::set_status(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ use crate::builtin::trap::TrapTarget;
|
|||||||
use crate::libsh::error::{self, ShErr, ShErrKind, ShResult};
|
use crate::libsh::error::{self, ShErr, ShErrKind, ShResult};
|
||||||
use crate::libsh::sys::TTY_FILENO;
|
use crate::libsh::sys::TTY_FILENO;
|
||||||
use crate::libsh::utils::AutoCmdVecUtils;
|
use crate::libsh::utils::AutoCmdVecUtils;
|
||||||
use crate::parse::execute::exec_input;
|
use crate::parse::execute::{exec_dash_c, exec_input};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::procio::borrow_fd;
|
use crate::procio::borrow_fd;
|
||||||
use crate::readline::term::{LineWriter, RawModeGuard, raw_mode};
|
use crate::readline::term::{LineWriter, RawModeGuard, raw_mode};
|
||||||
@@ -130,7 +130,7 @@ fn main() -> ExitCode {
|
|||||||
if let Err(e) = if let Some(path) = args.script {
|
if let Err(e) = if let Some(path) = args.script {
|
||||||
run_script(path, args.script_args)
|
run_script(path, args.script_args)
|
||||||
} else if let Some(cmd) = args.command {
|
} else if let Some(cmd) = args.command {
|
||||||
exec_input(cmd, None, false, None)
|
exec_dash_c(cmd)
|
||||||
} else {
|
} else {
|
||||||
let res = shed_interactive(args);
|
let res = shed_interactive(args);
|
||||||
write(borrow_fd(*TTY_FILENO), b"\x1b[?2004l").ok(); // disable bracketed paste mode on exit
|
write(borrow_fd(*TTY_FILENO), b"\x1b[?2004l").ok(); // disable bracketed paste mode on exit
|
||||||
|
|||||||
@@ -5,31 +5,11 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use ariadne::Fmt;
|
use ariadne::Fmt;
|
||||||
|
use nix::sys::resource;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builtin::{
|
builtin::{
|
||||||
alias::{alias, unalias},
|
alias::{alias, unalias}, arrops::{arr_fpop, arr_fpush, arr_pop, arr_push, arr_rotate}, autocmd::autocmd, cd::cd, complete::{compgen_builtin, complete_builtin}, dirstack::{dirs, popd, pushd}, echo::echo, eval, exec, flowctl::flowctl, getopts::getopts, intro, jobctl::{self, JobBehavior, continue_job, disown, jobs}, keymap, map, pwd::pwd, read::{self, read_builtin}, resource::ulimit, shift::shift, shopt::shopt, source::source, test::double_bracket_test, trap::{TrapTarget, trap}, varcmds::{export, local, readonly, unset}, zoltraak::zoltraak
|
||||||
arrops::{arr_fpop, arr_fpush, arr_pop, arr_push, arr_rotate},
|
|
||||||
autocmd::autocmd,
|
|
||||||
cd::cd,
|
|
||||||
complete::{compgen_builtin, complete_builtin},
|
|
||||||
dirstack::{dirs, popd, pushd},
|
|
||||||
echo::echo,
|
|
||||||
eval, exec,
|
|
||||||
flowctl::flowctl,
|
|
||||||
getopts::getopts,
|
|
||||||
intro,
|
|
||||||
jobctl::{self, JobBehavior, continue_job, disown, jobs},
|
|
||||||
keymap, map,
|
|
||||||
pwd::pwd,
|
|
||||||
read::{self, read_builtin},
|
|
||||||
shift::shift,
|
|
||||||
shopt::shopt,
|
|
||||||
source::source,
|
|
||||||
test::double_bracket_test,
|
|
||||||
trap::{TrapTarget, trap},
|
|
||||||
varcmds::{export, local, readonly, unset},
|
|
||||||
zoltraak::zoltraak,
|
|
||||||
},
|
},
|
||||||
expand::{expand_aliases, expand_case_pattern, glob_to_regex},
|
expand::{expand_aliases, expand_case_pattern, glob_to_regex},
|
||||||
jobs::{ChildProc, JobStack, attach_tty, dispatch_job},
|
jobs::{ChildProc, JobStack, attach_tty, dispatch_job},
|
||||||
@@ -132,6 +112,64 @@ impl ExecArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Execute a `-c` command string, optimizing single simple commands to exec
|
||||||
|
/// directly without forking. This avoids process group issues where grandchild
|
||||||
|
/// processes (e.g. nvim spawning opencode) lose their controlling terminal.
|
||||||
|
pub fn exec_dash_c(input: String) -> ShResult<()> {
|
||||||
|
let log_tab = read_logic(|l| l.clone());
|
||||||
|
let expanded = expand_aliases(input, HashSet::new(), &log_tab);
|
||||||
|
let source_name = "<shed -c>".to_string();
|
||||||
|
let mut parser = ParsedSrc::new(Arc::new(expanded))
|
||||||
|
.with_lex_flags(super::lex::LexFlags::empty())
|
||||||
|
.with_name(source_name.clone());
|
||||||
|
if let Err(errors) = parser.parse_src() {
|
||||||
|
for error in errors {
|
||||||
|
error.print_error();
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nodes = parser.extract_nodes();
|
||||||
|
|
||||||
|
// Single simple command: exec directly without forking.
|
||||||
|
// The parser wraps single commands as Conjunction → Pipeline → Command.
|
||||||
|
// Unwrap all layers to check, then set NO_FORK on the inner Command.
|
||||||
|
if nodes.len() == 1 {
|
||||||
|
let is_single_cmd = match &nodes[0].class {
|
||||||
|
NdRule::Command { .. } => true,
|
||||||
|
NdRule::Pipeline { cmds } => cmds.len() == 1 && matches!(cmds[0].class, NdRule::Command { .. }),
|
||||||
|
NdRule::Conjunction { elements } => {
|
||||||
|
elements.len() == 1 && match &elements[0].cmd.class {
|
||||||
|
NdRule::Pipeline { cmds } => cmds.len() == 1 && matches!(cmds[0].class, NdRule::Command { .. }),
|
||||||
|
NdRule::Command { .. } => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if is_single_cmd {
|
||||||
|
// Unwrap to the inner Command node
|
||||||
|
let mut node = nodes.remove(0);
|
||||||
|
loop {
|
||||||
|
match node.class {
|
||||||
|
NdRule::Conjunction { mut elements } => { node = *elements.remove(0).cmd; }
|
||||||
|
NdRule::Pipeline { mut cmds } => { node = cmds.remove(0); }
|
||||||
|
NdRule::Command { .. } => break,
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.flags |= NdFlags::NO_FORK;
|
||||||
|
nodes.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dispatcher = Dispatcher::new(nodes, false, source_name);
|
||||||
|
// exec_cmd expects a job on the stack (normally set up by exec_pipeline).
|
||||||
|
// For the NO_FORK exec-in-place path, create one so it doesn't panic.
|
||||||
|
dispatcher.job_stack.new_job();
|
||||||
|
dispatcher.begin_dispatch()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn exec_input(
|
pub fn exec_input(
|
||||||
input: String,
|
input: String,
|
||||||
io_stack: Option<IoStack>,
|
io_stack: Option<IoStack>,
|
||||||
@@ -909,6 +947,7 @@ impl Dispatcher {
|
|||||||
"keymap" => keymap::keymap(cmd),
|
"keymap" => keymap::keymap(cmd),
|
||||||
"read_key" => read::read_key(cmd),
|
"read_key" => read::read_key(cmd),
|
||||||
"autocmd" => autocmd(cmd),
|
"autocmd" => autocmd(cmd),
|
||||||
|
"ulimit" => ulimit(cmd),
|
||||||
"true" | ":" => {
|
"true" | ":" => {
|
||||||
state::set_status(0);
|
state::set_status(0);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -946,7 +985,6 @@ impl Dispatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let no_fork = cmd.flags.contains(NdFlags::NO_FORK);
|
let no_fork = cmd.flags.contains(NdFlags::NO_FORK);
|
||||||
|
|
||||||
if argv.is_empty() {
|
if argv.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -959,36 +997,34 @@ impl Dispatcher {
|
|||||||
let existing_pgid = job.pgid();
|
let existing_pgid = job.pgid();
|
||||||
|
|
||||||
let fg_job = self.fg_job;
|
let fg_job = self.fg_job;
|
||||||
|
let interactive = self.interactive;
|
||||||
let child_logic = |pgid: Option<Pid>| -> ! {
|
let child_logic = |pgid: Option<Pid>| -> ! {
|
||||||
// Put ourselves in the correct process group before exec.
|
// For non-interactive exec-in-place (e.g. shed -c), skip process group
|
||||||
// For the first child in a pipeline pgid is None, so we
|
// and terminal setup — just transparently replace the current process.
|
||||||
// become our own group leader (setpgid(0,0)). For later
|
if interactive || !no_fork {
|
||||||
// children we join the leader's group.
|
// Put ourselves in the correct process group before exec.
|
||||||
let our_pgid = pgid.unwrap_or(Pid::from_raw(0));
|
// For the first child in a pipeline pgid is None, so we
|
||||||
let _ = setpgid(Pid::from_raw(0), our_pgid);
|
// become our own group leader (setpgid(0,0)). For later
|
||||||
|
// children we join the leader's group.
|
||||||
|
let our_pgid = pgid.unwrap_or(Pid::from_raw(0));
|
||||||
|
let _ = setpgid(Pid::from_raw(0), our_pgid);
|
||||||
|
|
||||||
// For foreground jobs, take the terminal BEFORE resetting
|
if fg_job {
|
||||||
// signals. SIGTTOU is still SIG_IGN (inherited from the shell),
|
let tty_pgid = if our_pgid == Pid::from_raw(0) {
|
||||||
// so tcsetpgrp won't stop us. This prevents a race
|
nix::unistd::getpid()
|
||||||
// where the child exec's and tries to read stdin before the
|
} else {
|
||||||
// parent has called tcsetpgrp — which would deliver SIGTTIN
|
our_pgid
|
||||||
// (now SIG_DFL after reset_signals) and stop the child.
|
};
|
||||||
if fg_job {
|
let _ = tcsetpgrp(
|
||||||
let tty_pgid = if our_pgid == Pid::from_raw(0) {
|
unsafe { BorrowedFd::borrow_raw(*crate::libsh::sys::TTY_FILENO) },
|
||||||
nix::unistd::getpid()
|
tty_pgid,
|
||||||
} else {
|
);
|
||||||
our_pgid
|
}
|
||||||
};
|
|
||||||
let _ = tcsetpgrp(
|
|
||||||
unsafe { BorrowedFd::borrow_raw(*crate::libsh::sys::TTY_FILENO) },
|
|
||||||
tty_pgid,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset signal dispositions before exec. SIG_IGN is preserved
|
if interactive || !no_fork {
|
||||||
// across execvpe, so the shell's ignored SIGTTIN/SIGTTOU would
|
crate::signal::reset_signals(fg_job);
|
||||||
// leak into child processes.
|
}
|
||||||
crate::signal::reset_signals();
|
|
||||||
|
|
||||||
let cmd = &exec_args.cmd.0;
|
let cmd = &exec_args.cmd.0;
|
||||||
let span = exec_args.cmd.1;
|
let span = exec_args.cmd.1;
|
||||||
|
|||||||
@@ -154,10 +154,10 @@ pub fn sig_setup(is_login: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset all signal dispositions to SIG_DFL.
|
/// Reset signal dispositions to SIG_DFL.
|
||||||
/// Called in child processes before exec so that the shell's custom
|
/// Called in child processes before exec so that the shell's custom
|
||||||
/// handlers and SIG_IGN dispositions don't leak into child programs.
|
/// handlers and SIG_IGN dispositions don't leak into child programs.
|
||||||
pub fn reset_signals() {
|
pub fn reset_signals(is_fg: bool) {
|
||||||
let default = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
|
let default = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
|
||||||
unsafe {
|
unsafe {
|
||||||
for sig in Signal::iterator() {
|
for sig in Signal::iterator() {
|
||||||
@@ -165,6 +165,10 @@ pub fn reset_signals() {
|
|||||||
if sig == Signal::SIGKILL || sig == Signal::SIGSTOP {
|
if sig == Signal::SIGKILL || sig == Signal::SIGSTOP {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if is_fg && (sig == Signal::SIGTTIN || sig == Signal::SIGTTOU) {
|
||||||
|
log::debug!("Not resetting SIGTTIN/SIGTTOU in foreground child");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let _ = sigaction(sig, &default);
|
let _ = sigaction(sig, &default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user