https://gitlab.synchro.net/main/sbbs/-/commit/19901be145ac554e92b4b388
Added Files:
src/doors/termgfx/door32.c door32.h
Modified Files:
exec/load/syncretro_lobby.js src/doors/syncconquer/door/door_io.c src/doors/syncdoom/syncdoom.c src/doors/syncduke/syncduke.h syncduke_config.c syncduke_door.c syncduke_io.c src/doors/syncmoo1/hw_sbbs.c syncmoo1.h syncmoo1_door.c syncmoo1_io.c syncmoo1_plat.c syncmoo1_plat.h src/doors/syncretro/README.md main.c syncretro.h syncretro_door.c syncretro_input.c syncretro_io.c syncretro_plat.c syncretro_plat.h src/doors/termgfx/CMakeLists.txt xtrn/syncnes/lobby.js
Log Message:
doors: one DOOR32.SYS parser, and every door can be a stdio door (*nix)
Synchronet does not hand a native door the client socket. It opens a
loopback socketpair, gives the door one end, and pumps between it and the
BBS's own ring buffers -- so the BBS does the telnet negotiation and the
SSH crypto, and a door has never seen an IAC byte in its life. That is why these doors work over SSH without knowing what SSH is.
Mystic on *nix does the same job the other way: it forks the door and
redirects stdin/stdout, doing the telnet and SSH itself. The stream that arrives is just as clean. So the doors can run there -- but only if they
can read ONE descriptor and write ANOTHER.
* SyncDOOM and SyncDuke already could (g_rfd/g_wfd, syncduke_input_fd).
* SyncRetro, SyncMOO1 and SyncConquer read the fd they WRITE. That is
right for a socket, right at a dev console (a tty descriptor is
bidirectional), and stone deaf down a pipe: the door draws a picture
and ignores every keystroke. Now split.
THE DROP FILE ALREADY SAID SO, AND NOBODY WAS LISTENING. DOOR32.SYS line 1
comm type 0 means LOCAL -- there is no socket because the BBS redirected
our stdio. Synchronet writes exactly that (comm type 0, INVALID_SOCKET)
when an external program is registered XTRN_STDIO; Mystic does the same.
Five doors carried five copies of that parser and not one of them knew.
So the parser moves to termgfx/door32.c. The copies had drifted -- four different ways to copy the alias, one door that never read it -- and each
had learned something the others hadn't: the 0-based-vs-1-based line
numbering, and why the alias is memcpy'd rather than snprintf'd (the
truncation is deliberate; snprintf earns a -Wformat-truncation that
-Werror turns into a build failure on some targets).
It also fixes a hole the copies shared: atoi("") and atoi("garbage") both return 0, and 0 is now a MEANINGFUL comm type. A truncated drop file made
a door believe the BBS had redirected its stdio and ignore the perfectly
good socket handle on the next line. Line 1 is parsed strictly now; no
number means UNKNOWN, never local. And a comm type we cannot use -- serial, undefined, or a telnet line with a dead handle -- is REPORTED rather than
left to fail as a door that starts, shows nothing and answers nothing.
A STDIO DOOR MUST NOT WRITE TO STDERR. The BBS dup2()s stderr onto the same stream as stdout (xtrn.cpp's EX_STDIO path), so on a stdio door stderr IS
the player's screen -- and these doors are chatty on it. SyncDOOM stamps timestamped lines there; 1oom prints config errors there; SyncDuke
DELIBERATELY routes Chocolate Duke's printf/Error() there, which is correct when stderr is the BBS log and exactly wrong when it is the terminal. termgfx_stderr_capture() sends it to data/<door>/<door>_n<node>.log instead.
Two bugs that only a live stdio run could find:
* SyncDuke's dup2(STDERR, STDOUT) -- right for a socket door, where fd 1
is unused -- DESTROYS the connection of a stdio door, where fd 1 IS the
player. It wrote every sixel frame into the BBS log. It now keeps a
duplicate of the real stdout, taken before the engine takes fd 1.
* A blocking stdin parks the whole game inside read() until the player
happens to press a key: no tick, no render, apparently hung. Both new
input descriptors are set non-blocking.
Verified on all five doors, socket and stdio, byte-for-byte comparable
output on each path, stdin actually read (strace), and zero stderr leaked
onto the player's stream. SyncRetro was also live-tested through
Synchronet's own EX_STDIO path -- an NES cartridge played on a pty with no socket at all -- which is what turned the stderr leak up.
What this does NOT add is telnet. A BBS that hands over the RAW client
socket would need the door to negotiate options, unescape IAC IAC, and
survive a NAWS window-resize whose payload bytes arrive looking like
keystrokes -- one of which, 0x11, is the quit key. Measured, not assumed:
fed a real negotiation and a resize, the door read the NAWS column count
(17 = 0x11) as Ctrl-Q and exited. The output side needs no escaping and is
now measured too: 461 KB of live wire capture -- sixel, base64 audio APCs, overlays -- contains not one byte >= 0x80. (The CP437 text tiers of the
sibling doors are NOT 7-bit, and would.)
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net