Skip to content

nohup: make the substitute stdin unreadable like GNU - #14655

Open
costajohnt wants to merge 3 commits into
uutils:mainfrom
costajohnt:fix/nohup-stdin-unreadable
Open

costajohnt wants to merge 3 commits into
uutils:mainfrom
costajohnt:fix/nohup-stdin-unreadable

Conversation

@costajohnt

Copy link
Copy Markdown
Contributor

When stdin is a terminal, nohup replaces it with /dev/null. We opened it read-only, so a command that went on to read from stdin got a silent EOF and exited 0:

$ nohup cat; echo $?
nohup: ignoring input and appending output to 'nohup.out'
0

GNU opens /dev/null write-only on purpose. From the manual: "Make the substitute file descriptor unreadable, so that commands that mistakenly attempt to read from standard input can report an error." Under GNU nohup the same command exits 1 and nohup.out holds cat: stdin: Bad file descriptor.

This change opens /dev/null with OpenOptions::new().write(true) in replace_fds() so a read on the substitute stdin fails with EBADF, matching GNU. stdout and stderr handling is unchanged.

The Windows side (platform/windows.rs) uses Stdio::null() for the child's stdin and is left as is; I did not find an equivalent of an unreadable handle there.

Testing:

  • New test_nohup_replaced_stdin_is_not_readable in tests/by-util/test_nohup.rs runs nohup cat under terminal_simulation(true), asserts exit status 1 and that nohup.out contains Bad file descriptor. It fails on main (exit 0) and passes with this change. Gated on the same target set as the sibling pty tests (apple, linux, android, freebsd, openbsd).
  • cargo test --features nohup --no-default-features --test tests -- test_nohup: 8 passed on macOS.
  • cargo fmt --all -- --check, cargo clippy -p uu_nohup --all-targets -- -D warnings, cargo clippy --features nohup --no-default-features --all-targets -- -D warnings: clean.
  • Manual check with script -q /dev/null on macOS: nohup cat and nohup gdate --file - both now exit 1 with the read error in nohup.out, same as gnohup.
  • GNU tests/misc/nohup.sh never reads stdin after the redirection, so no GNU test result should change. Not run locally (Linux only).

Fixes #14556

Reproduces against GNU coreutils 9.11 on Linux and macOS (gnohup cat under a pty exits 1 with cat: stdin: Bad file descriptor). The Cygwin report in the issue thread that could not reproduce was on GNU 9.0.

When stdin is a terminal, nohup replaces it with /dev/null. It was
opened read-only, so a command that went on to read stdin got a silent
EOF and exited 0 (`nohup cat`, `nohup date --file -`).

GNU opens /dev/null write-only on purpose: "Make the substitute file
descriptor unreadable, so that commands that mistakenly attempt to read
from standard input can report an error." Do the same, so such a read
fails with EBADF and the command exits 1 as it does under GNU nohup.

Fixes uutils#14556
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/cp/special-f. tests/cp/special-f is passing on 'main'. Maybe you have to rebase?
Skip an intermittent issue tests/cut/bounded-memory (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/pr/bounded-memory (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/inotify-dir-recreate (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/retry (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/date/resolution (passes in this run but fails in the 'main' branch)

@oech3

oech3 commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

There is a file named as NUL on Windows. unix needs to open /dev/null instead of Stdio::null() for the case /dev is not mounted. I'm not sure if we can fail to open NUL, or have a bug suggested by you on Windows.

Comment thread tests/by-util/test_nohup.rs Outdated
// EOF (GNU opens /dev/null write-only). Since nohup execs the command, the
// exit status is the command's own.
#[test]
#[cfg(any(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not cfg(unix)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No real reason, I copied the gating from the tests above. Switched it to cfg(unix).

@costajohnt

Copy link
Copy Markdown
Contributor Author

Good point, I think Windows has the same issue. Rust's Stdio::null() opens \\.\NUL with read access when it's used for stdin, so a child that reads it just gets EOF instead of an error. Opening NUL write-only (and mapping a failed open to the same cannot replace STDIN error, as you say it isn't guaranteed to succeed) looks like the equivalent fix. I'd rather keep that out of this PR and send it separately, if that works for you.

Also merged main to pick up the xattr fix for the OpenBSD cp failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nohup: programs that are waiting for stdin exit with 0 instead of 1

2 participants