Skip to content

Commit 24818d6

Browse files
committed
Implement --background flag
1 parent 0340d6d commit 24818d6

File tree

7 files changed

+64
-5
lines changed

7 files changed

+64
-5
lines changed

src/common/context.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub struct Context {
2424
pub askpass: bool,
2525
pub stdin: bool,
2626
pub bell: bool,
27+
pub background: bool,
2728
pub prompt: Option<String>,
2829
pub non_interactive: bool,
2930
pub use_session_records: bool,
@@ -95,6 +96,7 @@ impl Context {
9596
askpass: sudo_options.askpass,
9697
stdin: sudo_options.stdin,
9798
bell: sudo_options.bell,
99+
background: sudo_options.background,
98100
prompt,
99101
non_interactive: sudo_options.non_interactive,
100102
files_to_edit: vec![],
@@ -162,6 +164,7 @@ impl Context {
162164
askpass: sudo_options.askpass,
163165
stdin: sudo_options.stdin,
164166
bell: sudo_options.bell,
167+
background: false,
165168
prompt: sudo_options.prompt,
166169
non_interactive: sudo_options.non_interactive,
167170
files_to_edit,
@@ -185,6 +188,7 @@ impl Context {
185188
askpass: sudo_options.askpass,
186189
stdin: sudo_options.stdin,
187190
bell: sudo_options.bell,
191+
background: false,
188192
prompt: sudo_options.prompt,
189193
non_interactive: sudo_options.non_interactive,
190194
files_to_edit: vec![],
@@ -231,6 +235,7 @@ impl Context {
231235
askpass: sudo_options.askpass,
232236
stdin: sudo_options.stdin,
233237
bell: sudo_options.bell,
238+
background: false,
234239
prompt: sudo_options.prompt,
235240
non_interactive: sudo_options.non_interactive,
236241
files_to_edit: vec![],
@@ -275,6 +280,7 @@ impl Context {
275280
group: &self.target_group,
276281
umask: controls.umask,
277282

283+
background: self.background,
278284
use_pty: controls.use_pty,
279285
noexec: controls.noexec,
280286
})

src/exec/mod.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::{
1313
os::unix::ffi::OsStrExt,
1414
os::unix::process::CommandExt,
1515
path::{Path, PathBuf},
16-
process::Command,
16+
process::{self, Command},
1717
time::Duration,
1818
};
1919

@@ -24,13 +24,12 @@ use crate::{
2424
exec::no_pty::exec_no_pty,
2525
log::{dev_info, dev_warn, user_error},
2626
system::{
27-
_exit,
27+
ForkResult, Group, User, _exit, fork,
2828
interface::ProcessId,
29-
kill, killpg, mark_fds_as_cloexec, set_target_user,
29+
kill, killpg, mark_fds_as_cloexec, set_target_user, setpgid,
3030
signal::{consts::*, signal_name, SignalNumber, SignalSet},
3131
term::UserTerm,
3232
wait::{Wait, WaitError, WaitOptions},
33-
Group, User,
3433
},
3534
};
3635

@@ -71,6 +70,7 @@ pub struct RunOptions<'a> {
7170
pub group: &'a Group,
7271
pub umask: Umask,
7372

73+
pub background: bool,
7474
pub use_pty: bool,
7575
pub noexec: bool,
7676
}
@@ -83,6 +83,19 @@ pub fn run_command(
8383
options: RunOptions<'_>,
8484
env: impl IntoIterator<Item = (impl AsRef<OsStr>, impl AsRef<OsStr>)>,
8585
) -> io::Result<ExitReason> {
86+
if options.background {
87+
// SAFETY: There should be no other threads at this point.
88+
match unsafe { fork() }? {
89+
ForkResult::Parent(_) => process::exit(0),
90+
ForkResult::Child => {
91+
// Child continues in an orphaned process group.
92+
// Reads from the terminal fail with EIO.
93+
// Writes succeed unless tostop is set on the terminal.
94+
setpgid(ProcessId::new(0), ProcessId::new(0))?;
95+
}
96+
}
97+
}
98+
8699
// FIXME: should we pipe the stdio streams?
87100
let qualified_path = options.command;
88101
let mut command = Command::new(qualified_path);
@@ -185,6 +198,7 @@ pub fn run_command(
185198
command,
186199
user_tty,
187200
options.user,
201+
options.background,
188202
),
189203
Err(err) => {
190204
dev_info!("Could not open user's terminal, not allocating a pty: {err}");

src/exec/use_pty/parent.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use std::collections::VecDeque;
22
use std::ffi::c_int;
33
use std::io;
4+
use std::os::fd::{FromRawFd, OwnedFd};
45
use std::process::{Command, Stdio};
56

7+
use libc::{close, O_CLOEXEC};
8+
9+
use crate::cutils::cerr;
610
use crate::exec::event::{EventHandle, EventRegistry, PollEvent, Process, StopReason};
711
use crate::exec::use_pty::monitor::exec_monitor;
812
use crate::exec::use_pty::SIGCONT_FG;
@@ -31,6 +35,7 @@ pub(in crate::exec) fn exec_pty(
3135
mut command: Command,
3236
user_tty: UserTerm,
3337
pty_owner: &User,
38+
background: bool,
3439
) -> io::Result<ExitReason> {
3540
// Allocate a pseudoterminal.
3641
let pty = get_pty(pty_owner)?;
@@ -81,6 +86,10 @@ pub(in crate::exec) fn exec_pty(
8186
ParentEvent::Pty,
8287
);
8388

89+
if background {
90+
tty_pipe.disable_input(&mut registry);
91+
}
92+
8493
let user_tty = tty_pipe.left_mut();
8594

8695
// Check if we are the foreground process
@@ -118,6 +127,24 @@ pub(in crate::exec) fn exec_pty(
118127
// change the terminal mode.
119128
exec_bg = true;
120129
}
130+
} else if background {
131+
// Running in background (sudo -b), no access to terminal input.
132+
// In non-pty mode, the command runs in an orphaned process
133+
// group and reads from the controlling terminal fail with EIO.
134+
// We cannot do the same while running in a pty but if we set
135+
// stdin to a half-closed pipe, reads from it will get EOF.
136+
exec_bg = true;
137+
138+
let mut pipes = [-1, -1];
139+
// SAFETY: A valid pointer to a mutable array of 2 fds is passed in.
140+
unsafe {
141+
cerr(libc::pipe2(pipes.as_mut_ptr(), O_CLOEXEC))?;
142+
}
143+
// SAFETY: pipe2 created two owned pipe fds.
144+
unsafe {
145+
command.stdin(OwnedFd::from_raw_fd(pipes[0]));
146+
close(pipes[1]);
147+
}
121148
}
122149

123150
if !io::stdout().is_terminal_for_pgrp(parent_pgrp) {

src/su/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ impl SuContext {
210210
group: &self.group,
211211
umask: Umask::Preserve,
212212

213+
background: false,
213214
use_pty: true,
214215
noexec: false,
215216
}

src/sudo/cli/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ pub struct SudoRunOptions {
385385
pub askpass: bool,
386386
// -B
387387
pub bell: bool,
388+
// -b
389+
pub background: bool,
388390
// -E
389391
/* ignored, part of env_var_list */
390392
// -k
@@ -416,6 +418,7 @@ impl TryFrom<SudoOptions> for SudoRunOptions {
416418
fn try_from(mut opts: SudoOptions) -> Result<Self, Self::Error> {
417419
let askpass = mem::take(&mut opts.askpass);
418420
let bell = mem::take(&mut opts.bell);
421+
let background = mem::take(&mut opts.background);
419422
let reset_timestamp = mem::take(&mut opts.reset_timestamp);
420423
let non_interactive = mem::take(&mut opts.non_interactive);
421424
let stdin = mem::take(&mut opts.stdin);
@@ -466,6 +469,7 @@ impl TryFrom<SudoOptions> for SudoRunOptions {
466469
Ok(Self {
467470
askpass,
468471
bell,
472+
background,
469473
reset_timestamp,
470474
non_interactive,
471475
stdin,
@@ -487,6 +491,8 @@ struct SudoOptions {
487491
askpass: bool,
488492
// -B
489493
bell: bool,
494+
// -b
495+
background: bool,
490496
// -D
491497
chdir: Option<SudoPath>,
492498
// -g
@@ -698,6 +704,9 @@ impl SudoOptions {
698704
"-B" | "--bell" => {
699705
options.bell = true;
700706
}
707+
"-b" | "--background" => {
708+
options.background = true;
709+
}
701710
"-E" | "--preserve-env" => {
702711
user_warn!(
703712
"preserving the entire environment is not supported, '{flag}' is ignored",
@@ -878,6 +887,7 @@ fn reject_all(context: &str, opts: SudoOptions) -> Result<(), String> {
878887
check_options!(
879888
askpass,
880889
bell,
890+
background,
881891
chdir,
882892
edit,
883893
group,

src/sudo/cli/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ fn help() {
330330
let cmd = SudoAction::try_parse_from(["sudo", "-h"]).unwrap();
331331
assert!(cmd.is_help());
332332

333-
let cmd = SudoAction::try_parse_from(["sudo", "-bh"]);
333+
let cmd = SudoAction::try_parse_from(["sudo", "-zh"]);
334334
assert!(cmd.is_err());
335335

336336
let cmd = SudoAction::try_parse_from(["sudo", "--help"]).unwrap();

src/sudo/env/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ fn create_test_context(sudo_options: SudoRunOptions) -> Context {
132132
non_interactive: sudo_options.non_interactive,
133133
use_session_records: false,
134134
bell: false,
135+
background: false,
135136
files_to_edit: vec![],
136137
}
137138
}

0 commit comments

Comments
 (0)