149 lines
9.1 KiB
C
149 lines
9.1 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);
|
|
|