Skip to content

Commit 02901b2

Browse files
authored
Merge pull request #395 from dezgeg/add_tool
setpgid: Add tool
2 parents 6ee94a0 + 38bdf37 commit 02901b2

File tree

8 files changed

+178
-0
lines changed

8 files changed

+178
-0
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ feat_common_core = [
4242
"nologin",
4343
"renice",
4444
"rev",
45+
"setpgid",
4546
"setsid",
4647
"uuidgen",
4748
]
@@ -106,6 +107,7 @@ mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", pa
106107
nologin = { optional = true, version = "0.0.1", package = "uu_nologin", path = "src/uu/nologin" }
107108
renice = { optional = true, version = "0.0.1", package = "uu_renice", path = "src/uu/renice" }
108109
rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" }
110+
setpgid = { optional = true, version = "0.0.1", package = "uu_setpgid", path = "src/uu/setpgid" }
109111
setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src/uu/setsid" }
110112
uuidgen = { optional = true, version = "0.0.1", package = "uu_uuidgen", path ="src/uu/uuidgen" }
111113

src/uu/setpgid/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "uu_setpgid"
3+
version = "0.0.1"
4+
edition = "2021"
5+
6+
[lib]
7+
path = "src/setpgid.rs"
8+
9+
[[bin]]
10+
name = "setpgid"
11+
path = "src/main.rs"
12+
13+
[dependencies]
14+
uucore = { workspace = true }
15+
clap = { workspace = true }
16+
libc = { workspace = true }
17+
nix = { workspace = true, features = ["process"] }

src/uu/setpgid/setpgid.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# setpgid
2+
3+
```
4+
setpgid [options] <program> [<argument>...]
5+
```
6+
7+
Run a program in a new process group

src/uu/setpgid/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uucore::bin!(uu_setpgid);

src/uu/setpgid/src/setpgid.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// This file is part of the uutils util-linux package.
2+
//
3+
// For the full copyright and license information, please view the LICENSE
4+
// file that was distributed with this source code.
5+
6+
use clap::{crate_version, Arg, ArgAction, Command as ClapCommand};
7+
use uucore::{
8+
error::{UResult, USimpleError},
9+
format_usage, help_about, help_usage,
10+
};
11+
12+
const ABOUT: &str = help_about!("setpgid.md");
13+
const USAGE: &str = help_usage!("setpgid.md");
14+
15+
#[cfg(target_family = "unix")]
16+
#[uucore::main]
17+
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
18+
use std::ffi::CString;
19+
use std::fs::File;
20+
use std::os::unix::io::AsRawFd;
21+
22+
let matches = uu_app().try_get_matches_from(args)?;
23+
24+
let remaining_args: Vec<String> = matches
25+
.get_many::<String>("args")
26+
.unwrap()
27+
.cloned()
28+
.collect();
29+
30+
if unsafe { libc::setpgid(0, 0) } != 0 {
31+
return Err(USimpleError::new(
32+
1,
33+
format!(
34+
"failed to create new process group: {}",
35+
std::io::Error::last_os_error()
36+
),
37+
));
38+
}
39+
40+
if matches.get_flag("foreground") {
41+
if let Ok(tty_file) = File::open("/dev/tty") {
42+
unsafe {
43+
libc::tcsetpgrp(tty_file.as_raw_fd(), libc::getpgrp());
44+
}
45+
}
46+
// According to strace open("/dev/tty") failure is ignored.
47+
}
48+
49+
let program = &remaining_args[0];
50+
let program_args = &remaining_args[1..];
51+
52+
// Command line arguments can't contain NUL bytes, so unwrap() is safe here.
53+
let program_cstr = CString::new(program.as_str()).unwrap();
54+
let mut argv = vec![program_cstr.clone()];
55+
for arg in program_args {
56+
argv.push(CString::new(arg.as_str()).unwrap());
57+
}
58+
59+
let Err(e) = nix::unistd::execvp(&program_cstr, &argv);
60+
Err(USimpleError::new(
61+
1,
62+
format!("failed to execute '{}': {}", program, e),
63+
))
64+
}
65+
66+
#[cfg(not(target_family = "unix"))]
67+
#[uucore::main]
68+
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
69+
let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?;
70+
71+
Err(USimpleError::new(
72+
1,
73+
"`setpgid` is unavailable on non-UNIX-like platforms.",
74+
))
75+
}
76+
77+
pub fn uu_app() -> ClapCommand {
78+
ClapCommand::new(uucore::util_name())
79+
.version(crate_version!())
80+
.about(ABOUT)
81+
.override_usage(format_usage(USAGE))
82+
.infer_long_args(true)
83+
.arg(
84+
Arg::new("foreground")
85+
.short('f')
86+
.long("foreground")
87+
.help("Make a foreground process group")
88+
.action(ArgAction::SetTrue),
89+
)
90+
.arg(
91+
Arg::new("args")
92+
.hide_short_help(true)
93+
.hide_long_help(true)
94+
.required(true)
95+
.action(ArgAction::Append)
96+
.num_args(1..)
97+
.trailing_var_arg(true),
98+
)
99+
}

tests/by-util/test_setpgid.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// This file is part of the uutils util-linux package.
2+
//
3+
// For the full copyright and license information, please view the LICENSE
4+
// file that was distributed with this source code.
5+
6+
use regex::Regex;
7+
use uutests::new_ucmd;
8+
9+
#[test]
10+
#[cfg(target_family = "unix")]
11+
fn test_nonexistent_program() {
12+
new_ucmd!()
13+
.arg("does_not_exist")
14+
.fails()
15+
.stderr_contains("failed to execute");
16+
}
17+
18+
#[test]
19+
#[cfg(target_os = "linux")]
20+
fn test_pgid_changed() {
21+
let our_pgid = unsafe { libc::getpgid(0) };
22+
// Gets pgid of the 'cut' process from /proc
23+
new_ucmd!()
24+
.args(&["cut", "-d", " ", "-f", "5", "/proc/self/stat"])
25+
.succeeds()
26+
.stdout_does_not_match(&Regex::new(&format!("^{}$", our_pgid)).unwrap());
27+
}
28+
29+
#[test]
30+
#[cfg(target_family = "unix")]
31+
fn test_flag_after_command() {
32+
new_ucmd!()
33+
.arg("echo")
34+
.arg("-f")
35+
.succeeds()
36+
.stdout_is("-f\n");
37+
}

tests/tests.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ mod test_renice;
5555
#[path = "by-util/test_rev.rs"]
5656
mod test_rev;
5757

58+
#[cfg(feature = "setpgid")]
59+
#[path = "by-util/test_setpgid.rs"]
60+
mod test_setpgid;
61+
5862
#[cfg(feature = "setsid")]
5963
#[path = "by-util/test_setsid.rs"]
6064
mod test_setsid;

0 commit comments

Comments
 (0)