Skip to content

Commit a00d4db

Browse files
authored
Sudoedit entrypoint (#1195)
2 parents 52d093c + c2ad5f9 commit a00d4db

File tree

5 files changed

+70
-19
lines changed

5 files changed

+70
-19
lines changed

src/sudo/cli/help.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
pub const USAGE_MSG: &str = "\
22
usage: sudo -h | -K | -k | -V
3-
usage: sudo -v [-BknS] [-g group] [-u user]
4-
usage: sudo -l [-BknS] [-g group] [-U user] [-u user] [command [arg ...]]
5-
usage: sudo [-BknS] [-D directory] [-g group] [-u user] [-i | -s] [command [arg ...]]
6-
usage: sudo -e [-BknS] [-D directory] [-g group] [-u user] file ...";
3+
usage: sudo [-BknS] [-p prompt] [-D directory] [-g group] [-u user] [-i | -s] [command [arg ...]]
4+
usage: sudo -v [-BknS] [-p prompt] [-g group] [-u user]
5+
usage: sudo -l [-BknS] [-p prompt] [-g group] [-U user] [-u user] [command [arg ...]]
6+
usage: sudo -e [-BknS] [-p prompt] [-D directory] [-g group] [-u user] file ...";
77

88
const DESCRIPTOR: &str = "sudo - run commands as another user";
99

@@ -17,6 +17,7 @@ const HELP_MSG: &str = "Options:
1717
-k, --reset-timestamp invalidate timestamp file
1818
-l, --list list user's privileges or check a specific command; use twice for longer format
1919
-n, --non-interactive non-interactive mode, no prompts are used
20+
-p, --prompt=prompt use the specified password prompt
2021
-S, --stdin read password from standard input
2122
-s, --shell run shell as the target user; a command may also be specified
2223
-U, --other-user=user in list mode, display privileges for user

src/sudo/cli/help_edit.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
pub const USAGE_MSG: &str = "\
2+
usage: sudoedit -h | -V
3+
usage: sudoedit [-BknS] [-p prompt] [-g group] [-u user] file ...";
4+
5+
const DESCRIPTOR: &str = "sudo - edit files as another user";
6+
7+
const HELP_MSG: &str = "Options:
8+
Options:
9+
-B, --bell ring bell when prompting
10+
-g, --group=group run command as the specified group name or ID
11+
-h, --help display help message and exit
12+
-k, --reset-timestamp invalidate timestamp file
13+
-n, --non-interactive non-interactive mode, no prompts are used
14+
-p, --prompt=prompt use the specified password prompt
15+
-S, --stdin read password from standard input
16+
-u, --user=user run command (or edit file) as specified user
17+
name or ID
18+
-V, --version display version information and exit
19+
-- stop processing command line arguments";
20+
21+
pub fn long_help_message() -> String {
22+
format!("{DESCRIPTOR}\n{USAGE_MSG}\n{HELP_MSG}")
23+
}

src/sudo/cli/mod.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::{borrow::Cow, mem};
55
use crate::common::{SudoPath, SudoString};
66

77
pub mod help;
8+
#[cfg_attr(not(feature = "sudoedit"), allow(unused))]
9+
pub mod help_edit;
810

911
#[cfg(test)]
1012
mod tests;
@@ -21,6 +23,13 @@ pub enum SudoAction {
2123
Version(SudoVersionOptions),
2224
}
2325

26+
pub(super) fn is_sudoedit(command_path: Option<String>) -> bool {
27+
use std::os::unix::ffi::OsStrExt;
28+
std::path::Path::new(&command_path.unwrap_or_default())
29+
.file_name()
30+
.is_some_and(|name| name.as_bytes().starts_with(b"sudoedit"))
31+
}
32+
2433
impl SudoAction {
2534
/// try to parse and environment variable assignment
2635
/// parse command line arguments from the environment and handle errors
@@ -107,8 +116,6 @@ impl TryFrom<SudoOptions> for SudoHelpOptions {
107116
let help = mem::take(&mut opts.help);
108117
debug_assert!(help);
109118

110-
reject_all("--help", opts)?;
111-
112119
Ok(Self {})
113120
}
114121
}
@@ -124,8 +131,6 @@ impl TryFrom<SudoOptions> for SudoVersionOptions {
124131
let version = mem::take(&mut opts.version);
125132
debug_assert!(version);
126133

127-
reject_all("--version", opts)?;
128-
129134
Ok(Self {})
130135
}
131136
}
@@ -518,12 +523,11 @@ impl SudoArg {
518523
];
519524

520525
/// argument assignments and shorthand options preprocessing
521-
fn normalize_arguments<I>(iter: I) -> Result<Vec<Self>, String>
526+
/// the iterator should only iterate over the actual arguments
527+
fn normalize_arguments<I>(mut arg_iter: I) -> Result<Vec<Self>, String>
522528
where
523-
I: IntoIterator<Item = String>,
529+
I: Iterator<Item = String>,
524530
{
525-
// the first argument is the sudo command - so we can skip it
526-
let mut arg_iter = iter.into_iter().skip(1);
527531
let mut processed = vec![];
528532

529533
while let Some(arg) = arg_iter.next() {
@@ -628,8 +632,16 @@ impl SudoOptions {
628632
I: IntoIterator<Item = T>,
629633
T: Into<String> + Clone,
630634
{
631-
let mut options = Self::default();
632-
let arg_iter = SudoArg::normalize_arguments(iter.into_iter().map(Into::into))?
635+
let mut arg_iter = iter.into_iter().map(Into::into);
636+
637+
let invoked_as_sudoedit = is_sudoedit(arg_iter.next());
638+
639+
let mut options = Self {
640+
edit: invoked_as_sudoedit,
641+
..Self::default()
642+
};
643+
644+
let arg_iter = SudoArg::normalize_arguments(arg_iter)?
633645
.into_iter()
634646
.peekable();
635647

@@ -644,7 +656,7 @@ impl SudoOptions {
644656
"warning: preserving the entire environment is not supported, `{flag}` is ignored"
645657
)
646658
}
647-
"-e" | "--edit" => {
659+
"-e" | "--edit" if !invoked_as_sudoedit => {
648660
options.edit = true;
649661
}
650662
"-H" | "--set-home" => {

src/sudo/cli/tests.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,14 @@ fn edit() {
316316
let cmd = SudoAction::try_parse_from(["sudo", "--edit", "filepath"]).unwrap();
317317
assert!(cmd.is_edit());
318318

319+
let cmd = SudoAction::try_parse_from(["sudoedit", "filepath"]).unwrap();
320+
assert!(cmd.is_edit());
321+
319322
let res = SudoAction::try_parse_from(["sudo", "--edit"]);
320323
assert!(res.is_err());
324+
325+
let res = SudoAction::try_parse_from(["sudoedit", "--edit", "filepath"]);
326+
assert!(res.is_err());
321327
}
322328

323329
#[test]

src/sudo/mod.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::system::interface::UserId;
77
use crate::system::timestamp::RecordScope;
88
use crate::system::User;
99
use crate::system::{time::Duration, timestamp::SessionRecordFile, Process};
10-
use cli::help;
1110
#[cfg(test)]
1211
pub(crate) use cli::SudoAction;
1312
#[cfg(not(test))]
@@ -67,11 +66,21 @@ fn sudo_process() -> Result<(), Error> {
6766

6867
self_check()?;
6968

69+
let usage_msg: &str;
70+
let long_help: fn() -> String;
71+
if cli::is_sudoedit(std::env::args().next()) {
72+
usage_msg = cli::help_edit::USAGE_MSG;
73+
long_help = cli::help_edit::long_help_message;
74+
} else {
75+
usage_msg = cli::help::USAGE_MSG;
76+
long_help = cli::help::long_help_message;
77+
}
78+
7079
// parse cli options
7180
match SudoAction::from_env() {
7281
Ok(action) => match action {
7382
SudoAction::Help(_) => {
74-
eprintln_ignore_io_error!("{}", help::long_help_message());
83+
eprintln_ignore_io_error!("{}", long_help());
7584
std::process::exit(0);
7685
}
7786
SudoAction::Version(_) => {
@@ -98,7 +107,7 @@ fn sudo_process() -> Result<(), Error> {
98107
SudoAction::Run(options) => {
99108
// special case for when no command is given
100109
if options.positional_args.is_empty() && !options.shell && !options.login {
101-
eprintln_ignore_io_error!("{}", help::USAGE_MSG);
110+
eprintln_ignore_io_error!("{}", usage_msg);
102111
std::process::exit(1);
103112
} else {
104113
#[cfg(feature = "dev")]
@@ -117,7 +126,7 @@ fn sudo_process() -> Result<(), Error> {
117126
}
118127
},
119128
Err(e) => {
120-
eprintln_ignore_io_error!("{e}\n{}", help::USAGE_MSG);
129+
eprintln_ignore_io_error!("{e}\n{}", usage_msg);
121130
std::process::exit(1);
122131
}
123132
}

0 commit comments

Comments
 (0)