Merge pull request #395 from dezgeg/add_tool

setpgid: Add tool
This commit is contained in:
Daniel Hofstetter
2025-09-23 15:55:00 +02:00
committed by GitHub
8 changed files with 178 additions and 0 deletions

11
Cargo.lock generated
View File

@@ -1331,6 +1331,7 @@ dependencies = [
"uu_nologin",
"uu_renice",
"uu_rev",
"uu_setpgid",
"uu_setsid",
"uu_uuidgen",
"uucore",
@@ -1500,6 +1501,16 @@ dependencies = [
"uucore",
]
[[package]]
name = "uu_setpgid"
version = "0.0.1"
dependencies = [
"clap",
"libc",
"nix",
"uucore",
]
[[package]]
name = "uu_setsid"
version = "0.0.1"

View File

@@ -42,6 +42,7 @@ feat_common_core = [
"nologin",
"renice",
"rev",
"setpgid",
"setsid",
"uuidgen",
]
@@ -106,6 +107,7 @@ mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", pa
nologin = { optional = true, version = "0.0.1", package = "uu_nologin", path = "src/uu/nologin" }
renice = { optional = true, version = "0.0.1", package = "uu_renice", path = "src/uu/renice" }
rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" }
setpgid = { optional = true, version = "0.0.1", package = "uu_setpgid", path = "src/uu/setpgid" }
setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src/uu/setsid" }
uuidgen = { optional = true, version = "0.0.1", package = "uu_uuidgen", path ="src/uu/uuidgen" }

17
src/uu/setpgid/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "uu_setpgid"
version = "0.0.1"
edition = "2021"
[lib]
path = "src/setpgid.rs"
[[bin]]
name = "setpgid"
path = "src/main.rs"
[dependencies]
uucore = { workspace = true }
clap = { workspace = true }
libc = { workspace = true }
nix = { workspace = true, features = ["process"] }

View File

@@ -0,0 +1,7 @@
# setpgid
```
setpgid [options] <program> [<argument>...]
```
Run a program in a new process group

View File

@@ -0,0 +1 @@
uucore::bin!(uu_setpgid);

View File

@@ -0,0 +1,99 @@
// This file is part of the uutils util-linux package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use clap::{crate_version, Arg, ArgAction, Command as ClapCommand};
use uucore::{
error::{UResult, USimpleError},
format_usage, help_about, help_usage,
};
const ABOUT: &str = help_about!("setpgid.md");
const USAGE: &str = help_usage!("setpgid.md");
#[cfg(target_family = "unix")]
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
use std::ffi::CString;
use std::fs::File;
use std::os::unix::io::AsRawFd;
let matches = uu_app().try_get_matches_from(args)?;
let remaining_args: Vec<String> = matches
.get_many::<String>("args")
.unwrap()
.cloned()
.collect();
if unsafe { libc::setpgid(0, 0) } != 0 {
return Err(USimpleError::new(
1,
format!(
"failed to create new process group: {}",
std::io::Error::last_os_error()
),
));
}
if matches.get_flag("foreground") {
if let Ok(tty_file) = File::open("/dev/tty") {
unsafe {
libc::tcsetpgrp(tty_file.as_raw_fd(), libc::getpgrp());
}
}
// According to strace open("/dev/tty") failure is ignored.
}
let program = &remaining_args[0];
let program_args = &remaining_args[1..];
// Command line arguments can't contain NUL bytes, so unwrap() is safe here.
let program_cstr = CString::new(program.as_str()).unwrap();
let mut argv = vec![program_cstr.clone()];
for arg in program_args {
argv.push(CString::new(arg.as_str()).unwrap());
}
let Err(e) = nix::unistd::execvp(&program_cstr, &argv);
Err(USimpleError::new(
1,
format!("failed to execute '{}': {}", program, e),
))
}
#[cfg(not(target_family = "unix"))]
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?;
Err(USimpleError::new(
1,
"`setpgid` is unavailable on non-UNIX-like platforms.",
))
}
pub fn uu_app() -> ClapCommand {
ClapCommand::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.infer_long_args(true)
.arg(
Arg::new("foreground")
.short('f')
.long("foreground")
.help("Make a foreground process group")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("args")
.hide_short_help(true)
.hide_long_help(true)
.required(true)
.action(ArgAction::Append)
.num_args(1..)
.trailing_var_arg(true),
)
}

View File

@@ -0,0 +1,37 @@
// This file is part of the uutils util-linux package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use regex::Regex;
use uutests::new_ucmd;
#[test]
#[cfg(target_family = "unix")]
fn test_nonexistent_program() {
new_ucmd!()
.arg("does_not_exist")
.fails()
.stderr_contains("failed to execute");
}
#[test]
#[cfg(target_os = "linux")]
fn test_pgid_changed() {
let our_pgid = unsafe { libc::getpgid(0) };
// Gets pgid of the 'cut' process from /proc
new_ucmd!()
.args(&["cut", "-d", " ", "-f", "5", "/proc/self/stat"])
.succeeds()
.stdout_does_not_match(&Regex::new(&format!("^{}$", our_pgid)).unwrap());
}
#[test]
#[cfg(target_family = "unix")]
fn test_flag_after_command() {
new_ucmd!()
.arg("echo")
.arg("-f")
.succeeds()
.stdout_is("-f\n");
}

View File

@@ -55,6 +55,10 @@ mod test_renice;
#[path = "by-util/test_rev.rs"]
mod test_rev;
#[cfg(feature = "setpgid")]
#[path = "by-util/test_setpgid.rs"]
mod test_setpgid;
#[cfg(feature = "setsid")]
#[path = "by-util/test_setsid.rs"]
mod test_setsid;