Skip to content

Conversation

@littleKitchen
Copy link

Summary

Fixes #10324

POSIX allows tcsetattr() to return success while only partially applying the requested settings:

The tcsetattr() function shall return successfully if it was able to perform any of the requested actions, even if some of the requested actions could not be performed.

This PR adds verification by reading back terminal settings after tcsetattr() and comparing them with the requested configuration.

Changes

  • Added termios_eq() function to compare two Termios structs (flags, control characters, and baud rates)
  • After tcsetattr(), call tcgetattr() and verify settings match
  • If settings don't match, exit with error: "<device>: unable to perform all requested operations"

Testing

All existing tests pass (cargo test -p uu_stty).

This matches GNU stty behavior, which performs tcgetattr() after setting and uses eq_mode() to detect partial application.

POSIX allows tcsetattr() to return success while only partially
applying the requested settings. This change adds verification by
reading back the terminal settings after tcsetattr() and comparing
them with the requested configuration.

If the settings don't match, stty now exits with an error message:
"<device>: unable to perform all requested operations"

This matches GNU stty behavior, which uses tcgetattr() after setting
and compares with eq_mode() to detect partial application.

Fixes uutils#10324
@ChrisDryden
Copy link
Collaborator

Just for doing some local tests to validate, do you have some testing commands that you used to compare the gnu behavior?

Would it be possible to add an integration test for this as well?

@github-actions
Copy link

GNU testsuite comparison:

Skipping an intermittent issue tests/shuf/shuf-reservoir (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/sort/sort-stale-thread-mem (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/inotify-dir-recreate (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/basenc/bounded-memory is now passing!

Adds test_multiple_settings_verified to verify that when multiple
terminal settings are applied at once, they are all correctly set.
This tests the tcsetattr verification logic in the normal success path.
@littleKitchen
Copy link
Author

Thanks for the review! I've added an integration test in the latest commit.

Testing Commands (GNU vs uutils comparison)

The issue is that POSIX allows tcsetattr() to return success while only partially applying settings. To compare behavior:

# GNU stty - after tcsetattr, it reads back and compares with eq_mode()
# If settings don't match, exits with: "<device>: unable to perform all requested operations"

# Test setting multiple values at once:
stty -F /dev/pts/0 intr ^A quit ^B erase ^H
stty -F /dev/pts/0  # Verify all three are set

About the integration test

I added test_multiple_settings_verified which:

  1. Sets multiple control characters at once (intr, quit, erase)
  2. Verifies all settings were actually applied by reading them back
  3. Resets to sane defaults

This tests the normal success path of the verification logic.

Note: Triggering an actual partial tcsetattr failure is difficult in a test environment since modern systems typically apply all settings atomically. The verification is a safety net for edge cases (unusual terminals, resource constraints, etc.) that are hard to reproduce reliably in CI.

@github-actions
Copy link

GNU testsuite comparison:

Skipping an intermittent issue tests/shuf/shuf-reservoir (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/sort/sort-stale-thread-mem (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/inotify-dir-recreate (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/basenc/bounded-memory is now passing!

tcsetattr is a POSIX function for setting terminal attributes,
used in the stty implementation.
The previous implementation compared ALL termios fields after tcsetattr,
which would fail if any field differed - even ones the user didn't change.

On some platforms (like musl with PTYs), unrelated fields may have
different values after tcsetattr even though the requested changes
were applied correctly.

This fix:
1. Saves the original termios before applying changes
2. After tcsetattr, compares only the fields that were actually changed
   (where requested != original)
3. Only fails if a specifically requested change wasn't applied

This properly detects partial application per POSIX while avoiding
false positives from unrelated field differences.
@littleKitchen littleKitchen force-pushed the fix/stty-verify-tcsetattr branch from 26e9c96 to f5ba572 Compare January 30, 2026 02:57
@github-actions
Copy link

GNU testsuite comparison:

Skipping an intermittent issue tests/shuf/shuf-reservoir (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/sort/sort-stale-thread-mem (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/inotify-dir-recreate (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/basenc/bounded-memory is now passing!

@sylvestre
Copy link
Contributor

A bunch of tests are failing

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.

stty: no verification that tcsetattr applied all settings

3 participants