233 lines
15 KiB
C
233 lines
15 KiB
C
#ifndef FROSTBYTE_JOBCTL_H
|
|
#define FROSTBYTE_JOBCTL_H
|
|
|
|
#include "types.h"
|
|
|
|
/*
|
|
* Generic "run a binary by path, with real job control" helpers, callable
|
|
* only from plain kernel-thread context (shell.c's shellRun, or a boot-time
|
|
* demo thread in kmain.c) -- never from inside a syscall's interrupt-gate
|
|
* handler, exactly like keyboardReaderReadBlocking/pipeReadBlocking's own
|
|
* restriction, since jobctlWaitForeground below parks with threadJoin's
|
|
* real halt() loop.
|
|
*
|
|
* Unlike syscall.c's doSpawn (sysSpawn's underlying implementation, used by
|
|
* one ring-3 process to spawn another), a job started here always becomes
|
|
* its OWN new process group leader (threadSetPgid(pid, pid)) regardless of
|
|
* the caller's own pgid -- matching a real shell's fork()+setpgid(child,
|
|
* child) convention for every new job/pipeline, not doSpawn's default of
|
|
* inheriting the parent's group (right for a process spawning a plain
|
|
* worker of its own, wrong for an interactive shell launching a new
|
|
* foreground/background command). argv is already kernel memory here (the
|
|
* caller IS the kernel, not a ring-3 process going through a syscall), so
|
|
* neither function copies or range-checks it the way copyUserArgv/
|
|
* userRangeOk (syscall.c) must for a user-supplied pointer.
|
|
*/
|
|
|
|
/* Loads `path` and spawns it as a new process group leader, hands it "the
|
|
terminal" (keyboardSetForegroundPgid, keyboard.h) immediately, and
|
|
returns its pid without waiting -- split from the actual wait
|
|
(jobctlWaitForeground below) so a caller can learn the pid the instant
|
|
the job exists, not only after it has already exited (shell.c's own
|
|
background job table needs the same immediacy from
|
|
jobctlRunBackground below, so this mirrors it instead of hiding the pid
|
|
inside a single combined call). Returns threadInvalidId, touching the
|
|
terminal not at all, if `path` cannot be loaded (missing from the VFS,
|
|
malformed ELF, or the thread table is full). */
|
|
uint32 jobctlSpawnForeground(const char* path, const char* const* argv, int argc);
|
|
|
|
/* Blocks (threadJoin) until the pid jobctlSpawnForeground just returned
|
|
exits, then reclaims the terminal for the CALLING thread's own group --
|
|
deliberately not "whatever pgid owned the terminal right before
|
|
jobctlSpawnForeground ran" (which could itself be some earlier, now-dead
|
|
job's stale id): a real shell always gets its own prompt's terminal back
|
|
when a foreground job quits, so a later Ctrl-C typed at the prompt
|
|
reaches the shell's NEXT job, not a group nothing will ever answer for
|
|
again. Returns the job's real exit code, same contract as threadJoin
|
|
itself. */
|
|
int64 jobctlWaitForeground(uint32 pid);
|
|
|
|
/* Loads `path` and spawns it as a new process group leader exactly like
|
|
jobctlSpawnForeground, but never touches the foreground pgid at all and
|
|
returns the new pid immediately without waiting -- a real shell's `&`
|
|
suffix. The caller is responsible for eventually reaping it (e.g.
|
|
shell.c's own background job table, reaped opportunistically via
|
|
threadJoinAnyChild). Returns false (touching *outPid not at all) on the
|
|
same load/spawn failures as jobctlSpawnForeground. */
|
|
bool jobctlRunBackground(const char* path, const char* const* argv, int argc,
|
|
uint32* outPid);
|
|
|
|
/* Generalizes jobctlSpawnForeground/jobctlRunBackground's shared "load and
|
|
spawn" step for shell.c's new `cmd1 | cmd2` pipeline syntax: `stdinHandle`/
|
|
`stdoutHandle` are raw pipe.h handles from vfsPipeCreateHandle (vfs.h),
|
|
baked into the new process's own fd 0/fd 1 (vfsPipeAttachInto) BEFORE it
|
|
ever runs a single instruction -- -1 for either one leaves that fd at its
|
|
normal default (the real console), exactly like an unpiped command's own
|
|
stdin/stdout. `joinPgid` is threadInvalidId for "become a new group
|
|
leader" (jobctlSpawn's existing default, what every other caller here
|
|
still wants) or an already-live pgid to join instead -- a pipeline's
|
|
second-and-later stage joins its first stage's own new group so a single
|
|
Ctrl-C reaches every stage at once, exactly like a real shell's pipeline
|
|
job. Already fully general for a MIDDLE stage of a longer pipeline too:
|
|
`stdinHandle`/`stdoutHandle` are independent, so a stage can have both a
|
|
piped stdin AND a piped stdout at once (shell.c's runPipelineStages does
|
|
exactly this for every interior stage of a 3+-stage pipeline) -- the only
|
|
things that ever needed generalizing for that were shell.c's own
|
|
parsing/orchestration and jobctlWaitPipelineStages below, never this
|
|
function. Never touches the terminal itself: shell.c's own pipeline
|
|
runner grants and reclaims it once, for the pipeline as a whole, via
|
|
jobctlWaitPipelineStages below -- not per stage. Returns threadInvalidId
|
|
on the same load/spawn failures as jobctlSpawnForeground. */
|
|
uint32 jobctlSpawnPiped(const char* path, const char* const* argv, int argc,
|
|
int stdinHandle, int stdoutHandle, uint32 joinPgid);
|
|
|
|
/* Blocks until every one of `pids[0..count-1]` (jobctlSpawnPiped's own
|
|
return values for a pipeline's stages, `pids[1..count-1]` already joined
|
|
to `pids[0]`'s group) has exited, then reclaims the terminal for the
|
|
CALLING thread's own group -- the same two-step contract
|
|
jobctlWaitForeground has for a single job, just for a whole N-stage
|
|
pipeline at once (2 <= count <= shellMaxPipelineStages, shell.h). Grants
|
|
the terminal to `pids[0]`'s group (which every other pid is already a
|
|
member of) before waiting, exactly like jobctlSpawnForeground already
|
|
does for a single job, so a Ctrl-C typed while the pipeline runs reaches
|
|
every stage. threadJoin's own real blocking semantics mean joining every
|
|
pid in this fixed order is correct regardless of which stage actually
|
|
exits first. Returns the LAST stage's exit code, matching a real
|
|
pipeline's own `$?` convention (the last command's status). */
|
|
int64 jobctlWaitPipelineStages(const uint32* pids, int count);
|
|
|
|
/* Installs `inPath`/`outPath`/`errPath` file redirections (vfsOpenInto,
|
|
vfs.h) into an ALREADY-SPAWNED child's fd 0/fd 1/fd 2 -- shell.c's new
|
|
`cmd [< IN] [> OUT | >> OUT] [2> ERR | 2>> ERR]` syntax. Any path may be
|
|
nullptr to leave that fd untouched (still whatever jobctlSpawn/
|
|
jobctlSpawnPiped already gave it -- the real console by default, or a
|
|
pipe end for a pipeline stage). `outAppend`/`errAppend` select `>>`/
|
|
`2>>` (oAppend, never truncates) over `>`/`2>` (oTrunc) when the
|
|
matching path is non-null; ignored otherwise. fd 0's default console
|
|
reader is released first, exactly like jobctlSpawnPiped's own stdin
|
|
override above, so redirecting it never leaks that reader slot; fd 1
|
|
and fd 2 have no such resource (their own default, fdConsole, owns
|
|
nothing -- neither is ever handed a reader by vfsFdInit). Exposed as
|
|
its own function (rather than folded into jobctlSpawnPiped's already-
|
|
long parameter list) so shell.c's `runPipeline` can layer a file
|
|
redirect onto exactly one end of a pipeline stage -- the other end
|
|
already wired to the shared pipe by jobctlSpawnPiped -- without a
|
|
combinatorial explosion of pipe-handle vs. path parameters on one call.
|
|
Returns false, leaving the child alive and running with whatever fds it
|
|
already had, on a bad path (missing without oCreate's implicit
|
|
create-on-write, unwritable parent, a fifo opened with the wrong mode,
|
|
exhausted pipe/inode resources) -- the caller decides whether that
|
|
warrants killing it (every caller in this kernel currently does, via
|
|
threadKill+threadJoin, exactly like runPipeline already does for a
|
|
failed second-stage spawn). */
|
|
bool jobctlApplyRedirects(uint32 pid, const char* inPath, const char* outPath, bool outAppend,
|
|
const char* errPath, bool errAppend);
|
|
|
|
/* Same as jobctlSpawnForeground, but installs `inPath`/`outPath`/`errPath`
|
|
file redirections (jobctlApplyRedirects above) into the child's fd 0/
|
|
fd 1/fd 2 BEFORE the terminal is ever granted to it -- shell.c's
|
|
`NAME [ARGS] [< IN] [> OUT | >> OUT] [2> ERR | 2>> ERR]`. On a redirect
|
|
failure the half-spawned child is killed and reaped before returning
|
|
threadInvalidId, exactly as if the load/spawn itself had failed: the
|
|
terminal must never be granted to a child whose own stdio could not be
|
|
wired up as asked. */
|
|
uint32 jobctlSpawnForegroundRedirected(const char* path, const char* const* argv, int argc,
|
|
const char* inPath, const char* outPath, bool outAppend,
|
|
const char* errPath, bool errAppend);
|
|
|
|
/* Same as jobctlRunBackground, but with the same file-redirection support
|
|
as jobctlSpawnForegroundRedirected above -- shell.c's
|
|
`NAME [ARGS] [< IN] [> OUT | >> OUT] [2> ERR | 2>> ERR] &`. Returns
|
|
false (touching *outPid not at all) on the same load/spawn failures as
|
|
jobctlRunBackground, or on a redirect failure (the half-spawned child is
|
|
killed and reaped first, same as the foreground case). */
|
|
bool jobctlRunBackgroundRedirected(const char* path, const char* const* argv, int argc,
|
|
const char* inPath, const char* outPath, bool outAppend,
|
|
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
|