Skip to content

Commit 8d590ca

Browse files
authored
timeout: cleanup return values (#9576)
- remove "WaitingFailed" which is a duplicate of "CommandTimedOut" - replace hard-coded values 126 and 127 with enum values, remove TODO - fix misleading comment. we DO return CommandTimedOut even when preserve-status is not specified - add tests for exit values 126 and 127 Signed-off-by: Etienne Cordonnier <ecordonnier@snap.com>
1 parent 5b261bc commit 8d590ca

File tree

3 files changed

+29
-13
lines changed

3 files changed

+29
-13
lines changed

src/uu/timeout/src/status.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,21 @@ use uucore::error::UError;
1919
/// assert_eq!(i32::from(ExitStatus::CommandTimedOut), 124);
2020
/// ```
2121
pub(crate) enum ExitStatus {
22-
/// When the child process times out and `--preserve-status` is not specified.
22+
/// When the child process times out.
2323
CommandTimedOut,
2424

2525
/// When `timeout` itself fails.
2626
TimeoutFailed,
2727

28+
/// When command is found but cannot be invoked (permission denied, etc.).
29+
CannotInvoke,
30+
31+
/// When command cannot be found.
32+
CommandNotFound,
33+
2834
/// When a signal is sent to the child process or `timeout` itself.
2935
SignalSent(usize),
3036

31-
/// When there is a failure while waiting for the child process to terminate.
32-
WaitingFailed,
33-
3437
/// When `SIGTERM` signal received.
3538
Terminated,
3639
}
@@ -40,8 +43,9 @@ impl From<ExitStatus> for i32 {
4043
match exit_status {
4144
ExitStatus::CommandTimedOut => 124,
4245
ExitStatus::TimeoutFailed => 125,
46+
ExitStatus::CannotInvoke => 126,
47+
ExitStatus::CommandNotFound => 127,
4348
ExitStatus::SignalSent(s) => 128 + s as Self,
44-
ExitStatus::WaitingFailed => 124,
4549
ExitStatus::Terminated => 143,
4650
}
4751
}

src/uu/timeout/src/timeout.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ fn wait_or_kill_process(
275275
process.wait()?;
276276
Ok(ExitStatus::SignalSent(signal).into())
277277
}
278-
Err(_) => Ok(ExitStatus::WaitingFailed.into()),
278+
Err(_) => Ok(ExitStatus::CommandTimedOut.into()),
279279
}
280280
}
281281

@@ -305,7 +305,6 @@ fn preserve_signal_info(signal: libc::c_int) -> libc::c_int {
305305
signal
306306
}
307307

308-
/// TODO: Improve exit codes, and make them consistent with the GNU Coreutils exit codes.
309308
fn timeout(
310309
cmd: &[String],
311310
duration: Duration,
@@ -328,12 +327,10 @@ fn timeout(
328327
.stderr(Stdio::inherit())
329328
.spawn()
330329
.map_err(|err| {
331-
let status_code = if err.kind() == ErrorKind::NotFound {
332-
// FIXME: not sure which to use
333-
127
334-
} else {
335-
// FIXME: this may not be 100% correct...
336-
126
330+
let status_code = match err.kind() {
331+
ErrorKind::NotFound => ExitStatus::CommandNotFound.into(),
332+
ErrorKind::PermissionDenied => ExitStatus::CannotInvoke.into(),
333+
_ => ExitStatus::CannotInvoke.into(),
337334
};
338335
USimpleError::new(
339336
status_code,

tests/by-util/test_timeout.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,18 @@ fn test_terminate_child_on_receiving_terminate() {
223223
.code_is(143)
224224
.stdout_contains("child received TERM");
225225
}
226+
227+
#[test]
228+
fn test_command_not_found() {
229+
// Test exit code 127 when command doesn't exist
230+
new_ucmd!()
231+
.args(&["1", "/this/command/definitely/does/not/exist"])
232+
.fails_with_code(127);
233+
}
234+
235+
#[test]
236+
fn test_command_cannot_invoke() {
237+
// Test exit code 126 when command exists but cannot be invoked
238+
// Try to execute a directory (should give permission denied or similar)
239+
new_ucmd!().args(&["1", "/"]).fails_with_code(126);
240+
}

0 commit comments

Comments
 (0)