diff --git a/src/kmain.c b/src/kmain.c index 747dbdb..22f9c1b 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -1196,6 +1196,179 @@ static void hereStringPipelineDriverThread(void) * "stderrwriter 2>> /tmp/stderr.txt" (append, same path) leaves a real * tmpfs file holding two back-to-back copies of stderrwriter.c's own * payload; "stderrreader < /tmp/stderr.txt" then reads that file back + * through a REAL redirected stdin and compares it byte-for-byte against + * exactly that doubled payload. The actual pass/fail proof is entirely + * stderrreader.c's own doing (its "payload-ok" needle only prints on an + * exact match) -- this thread's only job is to type the three commands and + * give the scheduler real time between them. + */ +static void stderrDriverThread(void) +{ + threadSleep(stderrDriverInitialDelayMs); + while (currentThread()->state == threadSleeping) + halt(); + + kprintf("[kbd] typing shell commands for the stderr redirection demo\n"); + jobctlTypeFlushLine(); + + stderrTypeWriteTruncateCommand(); + threadSleep(stderrDriverStepSettleMs); + while (currentThread()->state == threadSleeping) + halt(); + + stderrTypeWriteAppendCommand(); + threadSleep(stderrDriverStepSettleMs); + while (currentThread()->state == threadSleeping) + halt(); + + stderrTypeReadCommand(); + threadSleep(stderrDriverStepSettleMs); + while (currentThread()->state == threadSleeping) + halt(); + + for (;;) + halt(); +} + +/* 10700ms gives hereStringPipelineDriverThread's own total budget + (9000+500=9500ms, above its own real hcat/hsink spawn/run/reap time) a + comfortable margin before this thread ever touches the same shared + keyboard buffer, the same "next demo's initial delay clears the previous + demo's total typing budget" convention every driver thread above already + established. Settle is 600ms (slightly more than pipelineDriverThread's + own 500ms for a 2-stage pipe) since this demo's pipe has one more real + process (pipesrc, pipecat, AND pipesink) to spawn, run, and reap before + the final needle can print. */ +#define threeStagePipelineDriverInitialDelayMs 10700 +#define threeStagePipelineDriverSettleMs 600 + +static void threeStagePipelineTypeCommand(void) +{ + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeS); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeCPressMake); + injectKeyPress(scancodeMakeSpace); + injectShiftedKeyPress(scancodeMakeBackslash); + injectKeyPress(scancodeMakeSpace); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeCPressMake); + injectKeyPress(scancodeMakeA); + injectKeyPress(scancodeMakeT); + 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 REAL N-stage pipeline + * (runPipelineStages/jobctlWaitPipelineStages) going past two stages: types + * "pipesrc | pipecat | pipesink" at the real interactive shell through the + * same injected-keystroke mechanism every driver thread above already + * uses, well after hereStringPipelineDriverThread's own total budget has + * finished with the shell's shared line buffer. + * + * pipesrc.c and pipesink.c are reused completely unchanged from the + * two-stage pipeline demo (FALA 29) -- their own narration ("wrote + * payload", "blocking on read, waiting for pipesrc", "payload-ok") is still + * literally true here, just relayed through one more real process instead + * of being directly adjacent. The new thing this demo actually proves is + * user/pipecat.c in the middle: jobctlSpawnPiped bakes a real pipe into + * BOTH its fd 0 (from pipesrc's pipe) and its fd 1 (to pipesink's pipe) + * before it ever runs a single instruction -- the first process in this + * kernel with two DIFFERENT real inter-process pipes on its stdio at once + * (hcat.c's own simultaneous piped-stdin-and-stdout, FALA 34, had a + * shell-fed here-string on the stdin side, never a second real pipe). The + * actual pass/fail proof is still entirely pipesink.c's own doing (its + * "payload-ok" needle only prints on an exact byte-for-byte match of + * whatever pipesrc originally wrote) -- this thread's only job is to type + * the command and give all three spawned processes (none of which ever + * sleeps or waits on a real timer) a comfortable amount of real scheduled + * time to run to completion. + */ +static void threeStagePipelineDriverThread(void) +{ + threadSleep(threeStagePipelineDriverInitialDelayMs); + while (currentThread()->state == threadSleeping) + halt(); + + kprintf("[kbd] typing shell command for the three-stage pipeline demo\n"); + jobctlTypeFlushLine(); + threeStagePipelineTypeCommand(); + + threadSleep(threeStagePipelineDriverSettleMs); + while (currentThread()->state == threadSleeping) + halt(); + + for (;;) + halt(); +} + +/* '=' (sysGetEnv/sysSetEnv's own operator, shell.c's `export N=V` + builtin), 'y'/'Y', and 'v'/'V' are the only three make codes no earlier + driver thread above ever needed -- every other letter "export", + "GREETING", "envprint", and "env" use (e,x,p,o,r,t,g,i,n,f,s,b) was + already defined by an earlier block. Cross-checked against + keyboard.c's own scancodeLower/scancodeUpper tables the same way every + block above documents for its own new codes. */ +#define scancodeMakeEquals 0x0D +#define scancodeMakeY 0x15 +#define scancodeMakeV 0x2F + +static void envTypeExportCommand(void) +{ + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeX); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeO); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeT); + injectKeyPress(scancodeMakeSpace); + injectShiftedKeyPress(scancodeMakeG); + injectShiftedKeyPress(scancodeMakeR); + injectShiftedKeyPress(scancodeMakeE); + injectShiftedKeyPress(scancodeMakeE); + injectShiftedKeyPress(scancodeMakeT); + injectShiftedKeyPress(scancodeMakeI); + injectShiftedKeyPress(scancodeMakeN); + injectShiftedKeyPress(scancodeMakeG); + injectKeyPress(scancodeMakeEquals); + injectKeyPress(scancodeMakeF); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeO); + injectKeyPress(scancodeMakeS); + injectKeyPress(scancodeMakeT); + injectKeyPress(scancodeMakeB); + injectKeyPress(scancodeMakeY); + injectKeyPress(scancodeMakeT); + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeEnter); +} + +static void envTypeEnvprintCommand(void) +{ + injectKeyPress(scancodeMakeE); + injectKeyPress(scancodeMakeN); + injectKeyPress(scancodeMakeV); + injectKeyPress(scancodeMakeP); + injectKeyPress(scancodeMakeR); + injectKeyPress(scancodeMakeI); + injectKeyPress(scancodeMakeN); } void kmain(uint32 magic, uint64 multibootInfo)