feat: add jobctlRunBackground and jobctlApplyRedirects to jobctl
This commit is contained in:
1 parent
83421e7dba
commit
39528a6ce6
1 file changed
+156
+156
@@ -159,3 +159,159 @@ int64 jobctlWaitPipelineStages(const uint32* pids, int count)
|
||||
already does for a single job. */
|
||||
keyboardSetForegroundPgid(pids[0]);
|
||||
|
||||
/* threadJoin blocks on a SPECIFIC pid regardless of which stage
|
||||
actually exits first -- joining every one, in this fixed order,
|
||||
reaps all of them correctly no matter the real completion order, the
|
||||
same way a real shell's own wait() for each of a pipeline's known
|
||||
children would. */
|
||||
int64 lastCode = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
lastCode = threadJoin(pids[i]);
|
||||
|
||||
keyboardSetForegroundPgid(callerPgid);
|
||||
|
||||
return lastCode;
|
||||
}
|
||||
|
||||
bool jobctlRunBackground(const char* path, const char* const* argv, int argc,
|
||||
uint32* outPid)
|
||||
{
|
||||
uint32 pid = jobctlSpawnPiped(path, argv, argc, -1, -1, threadInvalidId);
|
||||
if (pid == threadInvalidId)
|
||||
return false;
|
||||
|
||||
/* Deliberately no keyboardSetForegroundPgid call at all -- a
|
||||
backgrounded job never owns the terminal, so a Ctrl-C typed while it
|
||||
runs reaches whatever WAS already in the foreground instead (the
|
||||
shell itself, if nothing else is running in the foreground), never
|
||||
this job. */
|
||||
*outPid = pid;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jobctlApplyRedirects(uint32 pid, const char* inPath, const char* outPath, bool outAppend,
|
||||
const char* errPath, bool errAppend)
|
||||
{
|
||||
FileDescriptor* fds = threadAt(pid)->fds;
|
||||
|
||||
if (inPath) {
|
||||
/* Same "release the default console reader before overwriting fd 0"
|
||||
step jobctlSpawnPiped already does for a piped stdin above --
|
||||
keyboardCloseReader is a safe no-op on an already-closed/invalid
|
||||
handle (readerAt's own bounds+used check, keyboard.c), so this
|
||||
is correct even if fd 0 already holds something else entirely
|
||||
(e.g. a pipe end from an earlier jobctlSpawnPiped call on this
|
||||
same pid, in runPipeline's left-side case below). */
|
||||
keyboardCloseReader(fds[0].pipeHandle);
|
||||
if (vfsOpenInto(fds, 0, inPath, oReadOnly) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outPath) {
|
||||
uint32 flags = oCreate | oWriteOnly | (outAppend ? oAppend : oTrunc);
|
||||
if (vfsOpenInto(fds, 1, outPath, flags) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (errPath) {
|
||||
uint32 flags = oCreate | oWriteOnly | (errAppend ? oAppend : oTrunc);
|
||||
if (vfsOpenInto(fds, 2, errPath, flags) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 jobctlSpawnForegroundRedirected(const char* path, const char* const* argv, int argc,
|
||||
const char* inPath, const char* outPath, bool outAppend,
|
||||
const char* errPath, bool errAppend)
|
||||
{
|
||||
uint32 pid = jobctlSpawnPiped(path, argv, argc, -1, -1, threadInvalidId);
|
||||
if (pid == threadInvalidId)
|
||||
return threadInvalidId;
|
||||
|
||||
if (!jobctlApplyRedirects(pid, inPath, outPath, outAppend, errPath, errAppend)) {
|
||||
threadKill(pid);
|
||||
threadJoin(pid);
|
||||
return threadInvalidId;
|
||||
}
|
||||
|
||||
keyboardSetForegroundPgid(pid);
|
||||
return pid;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint32 pid = jobctlSpawnPiped(path, argv, argc, -1, -1, threadInvalidId);
|
||||
if (pid == threadInvalidId)
|
||||
return false;
|
||||
|
||||
if (!jobctlApplyRedirects(pid, inPath, outPath, outAppend, errPath, errAppend)) {
|
||||
threadKill(pid);
|
||||
threadJoin(pid);
|
||||
return false;
|
||||
}
|
||||
|
||||
*outPid = pid;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Generalizes jobctlSpawnPiped's own fd-baking-before-first-instruction
|
||||
machinery to also here-string-feed stdin: spawns `path` with fd 0 wired
|
||||
to a brand-new pipe AND fd 1/pgid wired exactly as `stdoutHandle`/
|
||||
`joinPgid` ask (passed straight through to jobctlSpawnPiped, unlike the
|
||||
fixed -1/threadInvalidId every earlier here-string caller in this file
|
||||
used), then feeds `text` plus a trailing newline into the new pipe's
|
||||
write end and closes it -- all BEFORE returning, so the child can never
|
||||
observe the write end still open. Ordering matters: the write end is
|
||||
only added/used/closed AFTER jobctlSpawnPiped has already attached the
|
||||
read end (pipeAddReader, via vfsPipeAttachInto in vfs.c) -- pipe.c's
|
||||
pipeReleaseIfUnused only frees a pipe's slot once BOTH readers and
|
||||
writers reach 0, so closing the write end while the read end is still
|
||||
attached (readers==1) leaves the pipe, and the data already sitting in
|
||||
its ring buffer, alive for the child to actually read; closing it
|
||||
BEFORE the read end attached (readers==0) would instead free the whole
|
||||
slot out from under the still-buffered payload the instant this
|
||||
function's own pipeCloseWrite ran, handing the child a dangling handle.
|
||||
`text`'s own length plus one newline byte is always far under
|
||||
pipeBufferSize (256, pipe.h) for any line this shell's own lineCapacity
|
||||
(128, shell.c) could ever have typed it from, so the non-blocking
|
||||
pipeWrite below can never see "buffer full" and needs no retry loop the
|
||||
way a general-purpose writer would. Returns threadInvalidId, having
|
||||
spawned and written nothing, on the same load/spawn failures as
|
||||
jobctlSpawnPiped, or if the pipe table itself is exhausted
|
||||
(vfsPipeCreateHandle). */
|
||||
uint32 jobctlSpawnPipedHereString(const char* path, const char* const* argv, int argc,
|
||||
const char* text, int stdoutHandle, uint32 joinPgid)
|
||||
{
|
||||
int handle = vfsPipeCreateHandle();
|
||||
if (handle < 0)
|
||||
return threadInvalidId;
|
||||
|
||||
uint32 pid = jobctlSpawnPiped(path, argv, argc, handle, stdoutHandle, joinPgid);
|
||||
if (pid == threadInvalidId) {
|
||||
pipeDestroyIfUnattached(handle);
|
||||
return threadInvalidId;
|
||||
}
|
||||
|
||||
pipeAddWriter(handle);
|
||||
uint64 length = strlen(text);
|
||||
if (length > 0)
|
||||
pipeWrite(handle, text, length);
|
||||
pipeWrite(handle, "\n", 1);
|
||||
pipeCloseWrite(handle);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
uint32 jobctlSpawnForegroundHereString(const char* path, const char* const* argv, int argc,
|
||||
const char* text, const char* outPath, bool outAppend,
|
||||
const char* errPath, bool errAppend)
|
||||
{
|
||||
uint32 pid = jobctlSpawnPipedHereString(path, argv, argc, text, -1, threadInvalidId);
|
||||
if (pid == threadInvalidId)
|
||||
return threadInvalidId;
|
||||
|
||||
Reference in new issue
Block a user