Skip to content

0.16.0 --- the terminal is openkal's, and a disposition is accepted only where it is already in effect - #38

Merged
Sunrisepeak merged 2 commits into
mainfrom
openkal-0.14
Sep 20, 2026
Merged

Sunrisepeak merged 2 commits into
mainfrom
openkal-0.14

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

修复消费者报告 #36 的全部四处,并跟随 openkal 0.14(mcpplibs/openkal#38)。

改了什么

报告里的缺陷 现在
tcsetattr 被 ENOTTY 拒绝,进不了 raw mode TCSETS/TCSETSW/TCSETSF 路由到 kal_terminal_set_mode
tcgetattr 返回 0 却不写调用者的结构 TCGETSkal_terminal_get_mode 组装出真实的 struct termios
TIOCGWINSZ 返回 0 却不写 kal_terminal_size 回填;尺寸未知时写 0(串口本机就是这么答的),因为 isatty 只读这个调用的成败
SIG_IGN 报成功却不安装 只接受已经生效的处置:SIG_DFL 对除 SIGPIPE 外的信号成功,SIG_IGN 只对 SIGPIPE 成功,查询也不再一律答 SIG_DFL

SIGPIPE 是唯一一个处置不是默认值的信号,而且不是偶然:openkal 要求对已断开的流写入报告而不是结束程序,所以底下的实现早已把它安排成不动作——程序请求忽略它,是在请求它已经拥有的东西。

位的映射方向不对称

  • KAL_TERM_PASS_CONTROL 只在 ISIGIXONIEXTEN 全清时报告,因为这个位的含义是「环境不保留任何按键」。
  • :只看 ISIG。程序清 ISIG 就是想要中断键作为数据;要求三个全清才转达,会让 cfmakeraw 能用而「只清 ISIG」的程序静默无效——正是本报告的缺陷形状。

struct termios 里 openkal 不命名的字段(OPOST、线速、控制字符、VMIN/VTIME、以及 W/F 形式要求的排空)是陈述而非发明的,README 的限制表新增了一行说明改它们不会生效。要「会放弃的读」用 kal_timeout_read

验证

examples/terminal 是报告里的程序缩成一段可比对的 transcript,tools/pty-keys.py 在 pty 里跑它并在它就绪后敲键。判据是关系而不是取值:本端口的 transcript 与同一份源码在宿主 C 库上的 transcript 逐行比对。本地实测两边完全一致:

isatty 1
before   lflag_icanon=1 lflag_echo=1 lflag_isig=1 iflag_ixon=1 vmin=1 vtime=0
tcsetattr rc=0 errno=0
readback lflag_icanon=0 lflag_echo=0 lflag_isig=0 iflag_ixon=0 vmin=1 vtime=0
reading
byte 0x61 / byte 0x62 / byte 0x03 / byte 0x71
restored lflag_icanon=1 lflag_echo=1 lflag_isig=1 iflag_ixon=1 vmin=1 vtime=0
done

CI 新增一步做同样的比对,并先检查控制组里确实出现了 byte 0x03——否则这项比对什么也证明不了。

依赖:openkal 0.14.0、openkal-linux 0.14.0、openkal-macos 0.11.0、openkal-windows 0.9.0、openkal-opensbi 0.8.0。分支名 openkal-0.14 与各仓库一致。

…nly where it is already in effect

Reported by a consumer as #36: a program that entered
raw mode with tcgetattr, cfmakeraw and tcsetattr was ended by the interrupt
keystroke, and the three fallbacks were closed too.

* THE ioctl DISPATCHER REACHES openkal.terminal. TCGETS, TCSETS with its two
  draining forms, and TIOCGWINSZ are performed through kal_terminal_get_mode,
  kal_terminal_set_mode and kal_terminal_size. They were answered by a bare
  `return 0' and a refusal: TCGETS and TIOCGWINSZ reported success and wrote
  nothing into the caller's structure, so what cfmakeraw edited was the
  caller's own uninitialised stack, and TCSETS was refused with ENOTTY, so
  what it edited never reached the terminal.

* THE MODE IS TAKEN FROM ISIG AND REPORTED FROM ALL THREE, WHICH IS
  DELIBERATE AND NOT A SLIP. KAL_TERM_PASS_CONTROL set means that NO
  keystroke is reserved, so the reading direction sets it only where ISIG,
  IXON and IEXTEN are all clear --- that is what the terminal is. Asking is a
  different question: a program clears ISIG because it wants the interrupt
  keystroke as data, and requiring all three before asking would make
  cfmakeraw work and a program that cleared ISIG alone silently do nothing.

* WHAT THE STRUCTURE CARRIES THAT openkal DOES NOT NAME is stated rather than
  invented: the translations this environment really performs, the control
  characters at their agreed values, and a line speed --- because zero in
  that field is not "unknown" but "hang up". README records what a tcsetattr
  cannot change here, which is everything openkal does not name.

* A SIGNAL DISPOSITION IS ACCEPTED ONLY WHERE IT IS THE ONE IN EFFECT.
  SIG_IGN was accepted for every signal and installed for none, so a program
  that asked not to be ended by the interrupt keystroke was told it had
  succeeded and was ended by it anyway. SIG_DFL now succeeds for every signal
  but SIGPIPE, SIG_IGN for SIGPIPE alone, and the enquiry reports SIG_IGN for
  SIGPIPE rather than a zeroed record. SIGPIPE is the one disposition that is
  not the default, and not by accident: openkal requires a write to a stream
  whose far end is gone to report the condition rather than end the program.

* MEASURED, AND THE CRITERION IS A RELATION. examples/terminal is the
  reported program reduced to a transcript; tools/pty-keys.py runs it upon a
  pseudo-terminal and types at it once it is ready. The transcript above this
  port and the transcript of the same source above the system's own C library
  are compared line for line, and they agree: raw mode is entered, the
  interrupt keystroke arrives as the byte 0x03, the program survives it, and
  the terminal is left as it was found.

Requires openkal 0.14.0 and the implementations that carry the new position.
…hat excludes is named

The two transcripts agreed in every keystroke and every mode and differed in
one byte per line: openkal's mode word carries what is TYPED at a terminal
and names nothing about what is written to one, so the output
post-processing this environment performs survives a tcsetattr over this port
and is cleared by one over the system's own C library. README's limits table
records the effect; the comparison excludes it and nothing else.
@Sunrisepeak
Sunrisepeak merged commit 5f7f0ac into main Sep 20, 2026
5 checks passed
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