From a59ae7a9f8254af92d9779fa64b01058f911765e Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 4 Sep 2026 21:28:35 +0200 Subject: [PATCH] feat: extend kmain with pipelineDriverThread and redirTypePath --- src/kmain.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/src/kmain.c b/src/kmain.c index cc35d37..d672f90 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -677,6 +677,179 @@ static void pipelineTypeCommand(void) injectKeyPress(scancodeMakeSpace); injectShiftedKeyPress(scancodeMakeBackslash); injectKeyPress(scancodeMakeSpace); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeS); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeN); + injectKeyPress(scancodeMakeK); + injectKeyPress(scancodeMakeEnter); +} + +/* + * The live ring-3 proof for shell.c's new `cmd1 | cmd2` pipeline syntax + * (jobctl.h's jobctlSpawnPiped/jobctlWaitPipelineStages do the actual + * fd-baking and multi-wait work): types "pipesrc | pipesink" at the real interactive + * shell through the same injected-keystroke mechanism jobctlDriverThread + * above already uses, well after that thread's own demo has fully + * finished with the shell's shared line buffer (both threads' typed + * characters go through the exact same broadcast keyboardHandleScancode + * path, so they cannot safely interleave -- pipelineDriverInitialDelayMs + * is chosen with a comfortable margin over jobctlDriverThread's own total + * ~1550ms explicit sleep budget above to guarantee that). The actual + * pass/fail proof is entirely pipesink.c's own doing (its "payload-ok" + * needle only prints once it has verified, byte for byte, the exact + * payload pipesrc.c wrote through the real pipe) -- this thread's only job + * is to type the command and give the two spawned processes (neither of + * which ever sleeps or waits on a real timer, unlike alarmtest/polltest) a + * comfortable amount of real scheduled time to run to completion. + */ +static void pipelineDriverThread(void) +{ + threadSleep(pipelineDriverInitialDelayMs); + while (currentThread()->state == threadSleeping) + halt(); + + kprintf("[kbd] typing shell command for the pipeline demo\n"); + jobctlTypeFlushLine(); + pipelineTypeCommand(); + + threadSleep(pipelineDriverSettleMs); + while (currentThread()->state == threadSleeping) + halt(); + + for (;;) + halt(); +} + +/* Additional letters/symbols needed only by redirDriverThread's own three + commands below -- same cross-checked-against-scancodeLower sourcing as + every block above (index 0x20 is 'd', 0x11 is 'w', 0x32 is 'm', 0x2D is + 'x', 0x34 is '.', 0x35 is '/', 0x1E is 'a', 0x33 is ',' -- '>' is + shift+'.' and '<' is shift+',' on a real US keyboard, scancodeUpper's + own mapping for those two indices, the same way scancodeMakeSeven is + shifted into '&' and scancodeMakeBackslash into '|' above). */ +#define scancodeMakeD 0x20 +#define scancodeMakeW 0x11 +#define scancodeMakeM 0x32 +#define scancodeMakeX 0x2D +#define scancodeMakeDot 0x34 +#define scancodeMakeSlash 0x35 +#define scancodeMakeA 0x1E +#define scancodeMakeComma 0x33 + +static void redirTypePath(void) +{ + injectKeyPress(scancodeMakeSlash); + injectKeyPress(scancodeMakeT); + injectKeyPress(scancodeMakeM); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeSlash); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeD); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeDot); + injectKeyPress(scancodeMakeT); + injectKeyPress(scancodeMakeX); + injectKeyPress(scancodeMakeT); +} + +static void redirTypeWriterName(void) +{ + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeD); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeW); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeT); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeR); +} + +static void redirTypeReaderName(void) +{ + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeD); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeA); + injectKeyPress(scancodeMakeD); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeR); +} + +static void redirTypeWriteTruncateCommand(void) +{ + redirTypeWriterName(); + injectKeyPress(scancodeMakeSpace); + injectShiftedKeyPress(scancodeMakeDot); + injectKeyPress(scancodeMakeSpace); + redirTypePath(); + injectKeyPress(scancodeMakeEnter); +} + +static void redirTypeWriteAppendCommand(void) +{ + redirTypeWriterName(); + injectKeyPress(scancodeMakeSpace); + injectShiftedKeyPress(scancodeMakeDot); + injectShiftedKeyPress(scancodeMakeDot); + injectKeyPress(scancodeMakeSpace); + redirTypePath(); + injectKeyPress(scancodeMakeEnter); +} + +static void redirTypeReadCommand(void) +{ + redirTypeReaderName(); + injectKeyPress(scancodeMakeSpace); + injectShiftedKeyPress(scancodeMakeComma); + injectKeyPress(scancodeMakeSpace); + redirTypePath(); + injectKeyPress(scancodeMakeEnter); +} + +#define redirDriverInitialDelayMs 3600 +#define redirDriverStepSettleMs 300 + +/* + * The live ring-3 proof for shell.c's new `cmd < IN` / `cmd > OUT` / + * `cmd >> OUT` file redirection (jobctlApplyRedirects/ + * jobctlSpawnForegroundRedirected, jobctl.h): types three commands, one at + * a time, at the real interactive shell through the same injected- + * keystroke mechanism jobctlDriverThread/pipelineDriverThread above + * already use -- redirDriverInitialDelayMs gives pipelineDriverThread's + * own total ~3000ms budget (2500ms initial + 500ms settle) a comfortable + * margin before this thread ever touches the same shared keyboard buffer. + * + * Unlike pipelineDriverThread's single typed line, this is THREE separate + * commands, each one run to completion in the FOREGROUND (the shell + * genuinely blocks inside jobctlWaitForeground for each), so + * redirDriverStepSettleMs between them is not just cosmetic pacing -- + * typing the next command's keystrokes while the shell is still mid-line + * on the previous one would queue them in the same 32-byte console-reader + * ring buffer (keyboard.c) that a real user's own typing shares, with no + * guarantee they would not overflow it or be echoed against the wrong + * still-open line. Each settle is comfortably longer than + * redirwriter/redirreader's own trivial one-shot work (a single write or + * read-to-EOF, neither ever sleeps or blocks on a timer) needs to spawn, + * run, exit, and be reaped, the same safety margin philosophy every + * other injected multi-step demo above already uses. + * + * "redirwriter > /tmp/redir.txt" (create/truncate) followed by + * "redirwriter >> /tmp/redir.txt" (append, same path) leaves a real tmpfs + * file holding two back-to-back copies of redirwriter.c's own payload; + * "redirreader < /tmp/redir.txt" then reads that file back through a } void kmain(uint32 magic, uint64 multibootInfo)