Skip to content

chcpu: report each error only once - #617

Open
mmclinton wants to merge 2 commits into
uutils:mainfrom
mmclinton:chcpu-report-errors-once
Open

chcpu: report each error only once#617
mmclinton wants to merge 2 commits into
uutils:mainfrom
mmclinton:chcpu-report-errors-once

Conversation

@mmclinton

Copy link
Copy Markdown

This is my first contribution here, so please say if the scope or style should
be different and I will adjust.

The problem

chcpu prints every failure twice. The first copy has no chcpu: prefix, and
when a CPU list has several failures only the first one is repeated:

$ chcpu --enable 99998,99999
CPU 99998 does not exist
CPU 99999 does not exist
chcpu: CPU 99998 does not exist
$ echo $?
1

With this change:

$ chcpu --enable 99998,99999
chcpu: CPU 99998 does not exist
chcpu: CPU 99999 does not exist
$ echo $?
1

The partial-success path was wrong in a different way: it printed only the
unprefixed copy, so whether a diagnostic carried the program name depended on
which exit path was taken.

before                              after
$ chcpu --enable 1,99999            $ chcpu --enable 1,99999
CPU 1 is already enabled            CPU 1 is already enabled
CPU 99999 does not exist            chcpu: CPU 99999 does not exist
$ echo $?                           $ echo $?
64                                  64

Why it happened

CpuList::run reported each failure itself with a plain eprintln!, and then
also returned the first error. Returning it means #[uucore::main] prints it a
second time, this time with the prefix.

Both halves were there for a reason: the eprintln! so that every CPU in a list
gets a diagnostic rather than just the first, and the returned error so the exit
status is non-zero. The fix keeps both of those properties.

The change

Each per-CPU failure is now reported with uucore::show!, which prints
chcpu: <error> and sets the exit code, so run no longer returns an error.
This is the pattern uucore documents for non-fatal errors when an operation is
applied to many items.

Exit codes are unchanged: 1 when nothing succeeded, 64 for partial success.
There is no change to the CLI surface, so chcpu.md and --help are untouched,
and no new dependencies.

I also replaced the fold with a for loop, because the closure now performs
I/O and a plain loop seemed easier to follow. That part is not required by the
fix, so I am happy to drop it if you would rather keep the diff smaller.

Tests

chcpu had no test file, so this adds one and registers it in tests/tests.rs.
Ten tests in two groups:

  • Seven argument-parsing tests. These only reach clap, so they are not gated to
    Linux and also cover the macOS and Windows CI legs, where the utility itself
    is unimplemented.
  • Three Linux-only tests for the error paths, two of which fail without the fix
    above.

The Linux tests need no privileges and change no CPU state. An out-of-range CPU
index is rejected before anything is written, and enabling an already-enabled
CPU returns before writing. The partial-success test looks up a hot-pluggable
CPU at runtime and skips if the machine has none, since cpu0 often has no
online attribute.

The suite goes from 163 tests to 173, and both commits pass fmt, clippy and the
tests on their own.

Note

This implementation also differs from util-linux 2.42.2 in its exit status for
some inputs, and in validating a CPU list as a whole rather than element by
element. Both are pre-existing and each is its own behavioural question, so I
left them out to keep this PR on the duplicated output. Happy to open an issue.

mmc added 2 commits August 5, 2026 20:23
Every failure was printed twice: `CpuList::run` reported it itself with a raw
`eprintln!`, then also returned it, so `uucore` printed it again with the
`chcpu: ` prefix. The unprefixed copy came first, which is the one users saw.

Reporting each per-CPU failure with `uucore::show!` keeps the diagnostic for
every element of a CPU list, gives all of them the program-name prefix that
was previously only on the duplicate, and sets the exit code rather than
returning an error - the pattern uucore documents for non-fatal errors when
applying an operation to many items. Exit codes are untouched: 1 when nothing
succeeded, 64 for partial success.

Adds the first tests for this utility, covering the error paths. They need no
privileges and change no CPU state: an absent CPU index is rejected before
anything is written, and enabling an already-enabled CPU only prints.
These reach only clap, never sysfs, so they also guard the option surface on
the platforms where the utility itself is unimplemented.
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.

1 participant