From ed74e6df9b6d8519317c239242b23b0b771350ba Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 1 Sep 2026 19:36:53 +0200 Subject: [PATCH] feat: extend jobctl --- include/jobctl.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/include/jobctl.h b/include/jobctl.h index 025100b..1788cc0 100644 --- a/include/jobctl.h +++ b/include/jobctl.h @@ -146,3 +146,87 @@ bool jobctlRunBackgroundRedirected(const char* path, const char* const* argv, in const char* errPath, bool errAppend, uint32* outPid); +/* Generalizes jobctlSpawnPiped for shell.c's new `cmd1 <<< TEXT | cmd2` + pipeline syntax, the same way jobctlSpawnPiped itself generalizes + jobctlSpawnForeground/jobctlRunBackground: spawns `path` with fd 0 wired + to a brand-new here-string pipe (`text` plus a trailing newline, written + and closed by THIS kernel thread before returning, exactly like + jobctlSpawnForegroundHereString below) instead of the console, AND + `stdoutHandle`/`joinPgid` passed straight through to jobctlSpawnPiped so + the spawned process can simultaneously be a pipeline's first stage -- + -1/threadInvalidId for either one leaves that side at jobctlSpawnPiped's + own plain defaults (console stdout, new group leader). shell.c's + runPipeline uses this for a pipeline's LEFT stage when it was written + with `<<< TEXT` instead of a plain stdin; every other here-string caller + in this file (jobctlSpawnForegroundHereString/jobctlRunBackgroundHereString + below) is a thin wrapper passing -1/threadInvalidId, matching + jobctlSpawnForeground/jobctlRunBackground's own relationship to + jobctlSpawnPiped. Returns threadInvalidId on the same load/spawn + failures as jobctlSpawnPiped, or if the pipe table itself is exhausted + (vfsPipeCreateHandle, maxPipes slots all in use) -- both leave nothing + spawned. */ +uint32 jobctlSpawnPipedHereString(const char* path, const char* const* argv, int argc, + const char* text, int stdoutHandle, uint32 joinPgid); + +/* Same as jobctlSpawnForegroundRedirected, but for shell.c's new `NAME + [ARGS] <<< TEXT [> OUT | >> OUT] [2> ERR | 2>> ERR]` here-string syntax + instead of `< IN`: `text` (plus a trailing newline, matching a real + shell's own `<<<` behavior) is fed directly into the child's fd 0 + through a real anonymous pipe (jobctlSpawnPipedHereString above) that + THIS kernel thread itself writes and closes -- unlike jobctlApplyRedirects' + inPath, there is no file opened at all, so `outPath`/`errPath` are still + applied exactly as jobctlApplyRedirects already does (fd 0 is simply + pre-wired to the here-string pipe instead of left at the console before + that call runs). The write end is closed (real EOF, not a lingering open + pipe) BEFORE this function ever returns, so by the time the child + actually gets a timeslice to read its own fd 0 it sees exactly `text` + followed by one newline followed by true end-of-file, with no + possibility of racing the shell thread for more data that will never + arrive. Returns threadInvalidId on the same load/spawn/redirect failures + as jobctlSpawnForegroundRedirected, plus a pipe-table-exhaustion failure + (vfsPipeCreateHandle, maxPipes slots all in use) -- both leave nothing + spawned. */ +uint32 jobctlSpawnForegroundHereString(const char* path, const char* const* argv, int argc, + const char* text, const char* outPath, bool outAppend, + const char* errPath, bool errAppend); + +/* Same as jobctlRunBackgroundRedirected, but with the same here-string + stdin support as jobctlSpawnForegroundHereString above -- shell.c's + `NAME [ARGS] <<< TEXT [> OUT | >> OUT] [2> ERR | 2>> ERR] &`. Returns + false (touching *outPid not at all) on the same load/spawn/redirect/ + pipe-exhaustion failures as jobctlSpawnForegroundHereString. */ +bool jobctlRunBackgroundHereString(const char* path, const char* const* argv, int argc, + const char* text, const char* outPath, bool outAppend, + const char* errPath, bool errAppend, + uint32* outPid); + +/* Runs `path` to completion with its stdout wired to a real pipe THIS + kernel thread reads back as a string -- shell.c's new `$(cmd)` command + substitution, the mirror image of jobctlSpawnForegroundHereString's + "write a fixed string into the child's stdin" above. stdin is left at + its plain default (never redirected), and the terminal is never granted + to the child (unlike every foreground job above) -- command substitution + is not itself "the" foreground job, only a synchronous helper run while + building one. Joins the child FIRST, then drains its captured stdout -- + not "read until EOF, then join": this kernel only ever releases a dead + thread's own fds (reclaimOpenFds, sched.c) from inside threadReap + (threadJoin's last step), never at threadExit/threadKill itself, so + reading first would block forever waiting for an end-of-file that only + the very threadJoin call coming after it could ever produce. Joining + first instead means `path`'s own combined stdout must fit within one + pipe buffer's worth (pipeBufferSize, pipe.h) without ever blocking on + free space while it runs -- the same margin jobctlSpawnPipedHereString + already relies on for a here-string write, applied here to a captured + read instead -- an honestly-scoped limit on how chatty a substituted + command may be, not a silent hang for anything that stays under it. + Copies at most outCap-1 captured bytes into outBuf (NUL-terminated, + truncating silently like every other fixed-capacity buffer in this + kernel), draining and discarding anything beyond that rather than + leaving it sitting in the pipe. *outStatus receives the child's real + exit code. Returns false, touching neither outBuf nor *outStatus, on + the same load/spawn/pipe-exhaustion failures as jobctlSpawnPiped/ + vfsPipeCreateHandle. */ +bool jobctlCaptureOutput(const char* path, const char* const* argv, int argc, + char* outBuf, int outCap, int64* outStatus); + +#endif