Open tajkhan opened 2 months ago
I'm experiencing the same issue. I found that enabling the libc
feature of crossterm makes it work again. It looks like 0.27 always uses the "libc" method, but in 0.28 a new way that does not require libc was added as the default. It is this new non-libc version of the function that doesn't work under WSL.
Please post a full error message including backtrace for this if you can.
Good to hear switching back to libc works. I pushed to keep the libc code in place when the rustix implementation was added, and was starting to second guess myself about being a little over cautious.
I don't have a WSL environment to test this in, however a notable difference in the strace output between the rustix and libc versions here is that rustix is using TCGETS2
(introduced in Linux 2.6.20) for the ioctl, while libc is using TCGETS
. I found a report that WSL doesn't support TCGETS2
on serial ports, so it's plausible that the explanation here is that WSL doesn't support TCGETS2
on its terminal either. I'll look into having rustix use TCGETS
.
The full error message would help confirm this. And if anyone who is able to reproduce this is able to run the program under strace and post the output, that would be helpful as well. Thanks!
Thanks for looking into this. For the following program:
fn main() {
crossterm::terminal::enable_raw_mode().unwrap();
}
I get this output from strace, which does indeed point to TCGETS2 being the issue:
execve("./target/debug/crossterm-test", ["./target/debug/crossterm-test"...], 0x7fffde974aa0 /* 49 vars */) = 0
brk(NULL) = 0x7fffe62fa000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffeeed12f0) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f55805d0000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=129419, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 129419, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5580570000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=125488, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 127720, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5580550000
mmap(0x7f5580553000, 94208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f5580553000
mmap(0x7f558056a000, 16384, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a000) = 0x7f558056a000
mmap(0x7f558056e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d000) = 0x7f558056e000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0I\17\357\204\3$\f\221\2039x\324\224\323\236S"..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2220400, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2264656, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5580320000
mprotect(0x7f5580348000, 2023424, PROT_NONE) = 0
mmap(0x7f5580348000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7f5580348000
mmap(0x7f55804dd000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f55804dd000
mmap(0x7f5580536000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x215000) = 0x7f5580536000
mmap(0x7f558053c000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f558053c000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5580310000
arch_prctl(ARCH_SET_FS, 0x7f55803107c0) = 0
set_tid_address(0x7f5580310a90) = 3291
set_robust_list(0x7f5580310aa0, 24) = 0
rseq(0x7f5580311160, 0x20, 0, 0x53053053) = -1 ENOSYS (Function not implemented)
mprotect(0x7f5580536000, 16384, PROT_READ) = 0
mprotect(0x7f558056e000, 4096, PROT_READ) = 0
mprotect(0x7f5580643000, 16384, PROT_READ) = 0
mprotect(0x7f55805c8000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=8192*1024}) = 0
munmap(0x7f5580570000, 129419) = 0
poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0
x7f5580362520}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fc25d7d2520}, 8) = 0
getrandom("\x4f\xc8\xee\xb0\x5d\x3a\x97\x3f", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x7fffe62fa000
brk(0x7fffe631b000) = 0x7fffe631b000
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=8192*1024}) = 0
newfstatat(3, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
read(3, "7f5580310000-7f5580313000 rw-p 0"..., 4096) = 3572
close(3) = 0
sched_getaffinity(3291, 32, [0, 1, 2, 3, 4, 5, 6, 7]) = 32
rt_sigaction(SIGSEGV, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fc25d7d2520}, 8) = 0
rt_sigaction(SIGSEGV, {sa_handler=0x7f5580610440, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f5580362520}, NULL, 8) = 0
rt_sigaction(SIGBUS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fc25d7d2520}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0x7f5580610440, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f5580362520}, NULL, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f5580580000
mprotect(0x7f5580580000, 4096, PROT_NONE) = 0
sigaltstack({ss_sp=0x7f5580581000, ss_flags=0, ss_size=8192}, NULL) = 0
ioctl(0, TIOCGWINSZ, {ws_row=51, ws_col=105, ws_xpixel=1680, ws_ypixel=1632}) = 0
ioctl(0, TCGETS2, 0x7fffeeed0ddc) = -1 ENOTTY (Inappropriate ioctl for device)
write(2, "thread '", 8thread ') = 8
write(2, "main", 4main) = 4
write(2, "' panicked at ", 14' panicked at ) = 14
write(2, "src/main.rs", 11src/main.rs) = 11
write(2, ":", 1:) = 1
write(2, "2", 12) = 1
write(2, ":", 1:) = 1
write(2, "44", 244) = 2
write(2, ":\n", 2:
) = 2
write(2, "called `Result::unwrap()` on an "..., 124called `Result::unwrap()` on an `Err` value: Os { code: 25, kind: Uncategorized, message: "Inappropriate ioctl for device" }) = 124
write(2, "\n", 1
) = 1
write(2, "note: run with `RUST_BACKTRACE=1"..., 78note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
) = 78
futex(0x7f558056f210, FUTEX_WAKE_PRIVATE, 2147483647) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7f5580580000, 12288) = 0
exit_group(101) = ?
+++ exited with 101 +++
The backtrace doesn't go any deeper than the call to enable_raw_mode, but in case it's still helpful it is:
thread 'main' panicked at src/main.rs:2:44:
called `Result::unwrap()` on an `Err` value: Os { code: 25, kind: Uncategorized, message: "Inappropriate ioctl for device" }
stack backtrace:
0: rust_begin_unwind
at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/std/src/panicking.rs:652:5
1: core::panicking::panic_fmt
at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/panicking.rs:72:14
2: core::result::unwrap_failed
at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/result.rs:1679:5
3: core::result::Result<T,E>::unwrap
at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/result.rs:1102:23
4: crossterm_test::main
at ./src/main.rs:2:5
5: core::ops::function::FnOnce::call_once
at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ops/function.rs:250:5
The calling sequence that causes the error is:
enable_raw_mode
-> get_terminal_attr
-> rustix::termios::tcgetattr
.
Thanks for the diagnosis. Can you also try to get strace output for the libc feature too? It would be a useful comparison to see where they differ.
Here's the strace output with the libc feature:
execve("./target/debug/crossterm-test", ["./target/debug/crossterm-test"], 0x7fffecb9dcd0 /* 49 vars */) = 0
brk(NULL) = 0x7fffe2a39000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffe9e0e8a0) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5a02ef0000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=129419, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 129419, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5a02ed0000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=125488, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 127720, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5a02eb0000
mmap(0x7f5a02eb3000, 94208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7f5a02eb3000
mmap(0x7f5a02eca000, 16384, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a000) = 0x7f5a02eca000
mmap(0x7f5a02ece000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d000) = 0x7f5a02ece000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\237\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0 \0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 48, 848) = 48
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0I\17\357\204\3$\f\221\2039x\324\224\323\236S"..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2220400, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2264656, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5a02c80000
mprotect(0x7f5a02ca8000, 2023424, PROT_NONE) = 0
mmap(0x7f5a02ca8000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7f5a02ca8000
mmap(0x7f5a02e3d000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f5a02e3d000
mmap(0x7f5a02e96000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x215000) = 0x7f5a02e96000
mmap(0x7f5a02e9c000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5a02e9c000
close(3) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5a02c70000
arch_prctl(ARCH_SET_FS, 0x7f5a02c707c0) = 0
set_tid_address(0x7f5a02c70a90) = 5557
set_robust_list(0x7f5a02c70aa0, 24) = 0
rseq(0x7f5a02c71160, 0x20, 0, 0x53053053) = -1 ENOSYS (Function not implemented)
mprotect(0x7f5a02e96000, 16384, PROT_READ) = 0
mprotect(0x7f5a02ece000, 4096, PROT_READ) = 0
mprotect(0x7f5a02fa3000, 16384, PROT_READ) = 0
mprotect(0x7f5a02f38000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=8192*1024}) = 0
munmap(0x7f5a02ed0000, 129419) = 0
poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f5a02cc2520}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f79694a2520}, 8) = 0
getrandom("\x87\xd5\xa7\x88\xd4\x50\x9e\xa6", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0x7fffe2a39000
brk(0x7fffe2a5a000) = 0x7fffe2a5a000
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=8192*1024}) = 0
newfstatat(3, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
read(3, "7f5a02c70000-7f5a02c73000 rw-p 0"..., 4096) = 3516
close(3) = 0
sched_getaffinity(5557, 32, [0, 1, 2, 3, 4, 5, 6, 7]) = 32
rt_sigaction(SIGSEGV, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f79694a2520}, 8) = 0
rt_sigaction(SIGSEGV, {sa_handler=0x7f5a02f72240, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f5a02cc2520}, NULL, 8) = 0
rt_sigaction(SIGBUS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f79694a2520}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0x7f5a02f72240, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x7f5a02cc2520}, NULL, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f5a02ee0000
mprotect(0x7f5a02ee0000, 4096, PROT_NONE) = 0
sigaltstack({ss_sp=0x7f5a02ee1000, ss_flags=0, ss_size=8192}, NULL) = 0
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, SNDCTL_TMR_START or TCSETS, {B38400 -opost -isig -icanon -echo ...}) = 0
ioctl(0, TCGETS, {B38400 -opost -isig -icanon -echo ...}) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7f5a02ee0000, 12288) = 0
exit_group(0) = ?
+++ exited with 0 +++
I've now posted https://github.com/bytecodealliance/rustix/pull/1146 with a possible fix. I don't have a WSL environment to test it in myself, but if anyone who does would like to try it, it should be sufficient to add these lines to Cargo.toml:
[patch.crates-io]
rustix = { git = "https://github.com/bytecodealliance/rustix", rev = "4d1f3eaaae138138817dcaaabf5eaea6d9b93a08" }
Yes, that rustix patch seems to fix it for me. Thanks!
Thanks for testing! I've now released rustix 0.38.36 with the fix.
Added a note to the first comment for anyone that finds this issue marking the solution as run cargo update
(until #926 is merged and released).
Solution
rustix has released 0.38.36, which fixes this bug. Crossterm 0.28.1 is compatible with this version of rustix. Run
cargo update
to update your dependencies in your lock file.Problem
Describe the bug Was following the hecto tutorial which uses crossterm and found that the
enable_raw_mode()
function in bash under WSL in the latest version "0.28.0" returns an error. Apparently was working fine in version "0.27.0".To Reproduce Steps to reproduce the behavior:
cargo add crossterm
. By default it will add the latest version "0.28.0".cargo run
Result::unwrap()
on anErr
value: Os { code: 25, kind: Uncategorized, message: "Inappropriate ioctl for device" }'Expected behavior Calling
enable_raw_mode()
should returnOk(())
and switch the terminal to raw mode.OS I am running bash in Ubuntu in WSL under Windows 10 home edition; The terminal is xterm-256color.