sys: add bun.sys.termios wrapper for Android bionic#30391
Conversation
|
Updated 3:40 AM PT - May 8th, 2026
❌ @robobun, your commit 5f2af29 has 3 failures in
🧪 To try this PR locally: bunx bun-pr 30391That installs a local version of the PR into your bun-30391 --bun |
WalkthroughAdds bun.sys-backed termios support (Android bionic layout fallback), migrates terminal code to bun.sys tcgetattr/tcsetattr, exposes a JSC testing hook and JS binding ChangesAndroid-compatible termios support
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
std.c.termios for .linux assumes the glibc/musl shape (cc[32] +
trailing c_ispeed/c_ospeed, ~60B). bionic uses the raw kernel
asm-generic/termbits.h struct (cc[19], no speed fields, 36B). The
first 36B are layout-compatible so tcgetattr/tcsetattr appear to
work, but writes to .ispeed/.ospeed land past bionic's struct and
reinitialising c_cflag zeroes CBAUD so baud becomes B0.
Add bun.sys.{termios,tcgetattr,tcsetattr}: on Android an extern
struct matching bionic and @extern'd libc calls that take it;
elsewhere transparent aliases of std.posix.*. Switch Terminal.zig
and md/ansi_renderer.zig to the wrappers, guard the .ispeed/.ospeed
assignments behind @Hasfield, and add a comptime tripwire so the
workaround can be dropped once std grows a bionic case.
A termiosLayout testing hook round-trips c_cc[VLNEXT] and
lflag.ECHO through libc on a posix_openpt fd; that property holds
iff Zig and libc agree on the struct layout.
f31304b to
2cfb968
Compare
On BSD/macOS the master fd from posix_openpt() is not a terminal (tcgetattr -> ENOTTY); only the slave side is. Open it via grantpt/unlockpt/ptsname and run the round-trip there so the layout test works on every POSIX target.
A static `import { termiosLayout }` throws SyntaxError at module
load when the binding is absent. bun:test's console output counts
that as '1 fail', but the JUnit reporter emits zero <testcase>
elements, so a gate that parses JUnit sees 0 failures. Requiring
inside the test body turns it into an assertion failure that JUnit
records. Matches sigaction-layout.test.ts.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/internal/termios-layout.test.ts`:
- Line 33: Replace the full-struct equality check between result!.readback and
result!.installed with targeted assertions that only verify intended invariants:
assert that readback.cc_lnext equals installed.cc_lnext and that the ECHO flag
bit in readback.c_lflag matches installed.c_lflag (use the ECHO
constant/bitmask), and remove the expect(result!.readback).toEqual(...) line;
this ensures the test verifies the round-trip of the specific control character
and preservation of the ECHO bit without relying on cross-libc-normalized
fields.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: c455073e-aa0a-4d68-8b06-b5d957ca24d5
📒 Files selected for processing (1)
test/internal/termios-layout.test.ts
Problem
std.c.termiosfor.linuxassumes the glibc/musl shape (cc[32]+ trailingc_ispeed/c_ospeed, ~60B). bionic uses the raw kernelasm-generic/termbits.hstruct:The first 36 bytes are layout-compatible so
tcgetattr/tcsetattrmostly work, but:.ispeed/.ospeedland past bionic's struct and are silently ignoredTerminal.zigreinitialisesc_cflag(t.cflag = .{ .CREAD = true, … }), zeroing the CBAUD bits → baud becomes B0Fix
Same pattern as #30389:
src/sys/sys.zig: addbun.sys.termios— on Android anextern structmatching bionic (cc: [19]u8, no speed fields, reusing std'stc_*flag_tsince the bit layouts come from the kernel UAPI and are identical); elsewhere= std.posix.termios. Addbun.sys.tcgetattr/tcsetattrthat@externlibc directly on Android and forward tostd.posix.*elsewhere. A comptime tripwire fires ifstd.posix.termiosever loses.ispeedand shrinks to 36B, so the workaround can be dropped once the Zig stdlib gains a bionic case.src/runtime/api/bun/Terminal.zig: switch tobun.sys.termios/tcgetattr/tcsetattr; guard the.ispeed/.ospeedassignments with@hasField(it's a PTY, so baud is advisory only on bionic).src/md/ansi_renderer.zig: same swap for the kitty-graphics probe's termios save/restore.Verification
bun run zig:check-all— all 16 targets passzig build-obj -target {aarch64,x86_64}-linux-android -lcon the wrapper — bionic struct is 36B as expected, tripwire doesn't fire, flag-type reuse compilesbun bd test test/js/bun/terminal/— all 121 tests passtest/internal/termios-layout.test.tsopens a PTY master viaposix_openpt, writes a sentinel intoc_cc[VLNEXT]and toggleslflag.ECHOviabun.sys.tcsetattr, reads both back viabun.sys.tcgetattr. The round-trip holds iff Zig and libc agree on the struct layout.No Android runtime test is included because the misbehaviour only surfaces on a real Android libc, which CI does not run;
zig build check-android-debug(from #30389) compile-checks the struct once that lands.