feat: add jobctlRunBackgroundHereString and jobctlCaptureOutput
This commit is contained in:
1 parent
39528a6ce6
commit
fa1787020d
1 file changed
+112
+112
@@ -315,3 +315,115 @@ uint32 jobctlSpawnForegroundHereString(const char* path, const char* const* argv
|
|||||||
if (pid == threadInvalidId)
|
if (pid == threadInvalidId)
|
||||||
return threadInvalidId;
|
return threadInvalidId;
|
||||||
|
|
||||||
|
/* inPath is always nullptr here: fd 0 is already the here-string pipe
|
||||||
|
jobctlSpawnPipedHereString just wired up, exactly like a piped
|
||||||
|
stdin's slot in runPipeline (shell.c) is never ALSO given a file
|
||||||
|
redirect on the same fd. */
|
||||||
|
if (!jobctlApplyRedirects(pid, nullptr, outPath, outAppend, errPath, errAppend)) {
|
||||||
|
threadKill(pid);
|
||||||
|
threadJoin(pid);
|
||||||
|
return threadInvalidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboardSetForegroundPgid(pid);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
uint32 pid = jobctlSpawnPipedHereString(path, argv, argc, text, -1, threadInvalidId);
|
||||||
|
if (pid == threadInvalidId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!jobctlApplyRedirects(pid, nullptr, outPath, outAppend, errPath, errAppend)) {
|
||||||
|
threadKill(pid);
|
||||||
|
threadJoin(pid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outPid = pid;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The mirror image of jobctlSpawnPipedHereString above: that one feeds a
|
||||||
|
fixed string INTO a freshly spawned child's stdin (the shell is the
|
||||||
|
writer), this one reads a freshly spawned child's stdout back OUT as a
|
||||||
|
string (the shell is the reader) -- shell.c's new `$(cmd)` command
|
||||||
|
substitution. Attaches this thread as the pipe's reader (pipeAddReader)
|
||||||
|
BEFORE jobctlSpawnPiped ever attaches the child as its writer, the exact
|
||||||
|
opposite order of jobctlSpawnPipedHereString's own writer-after-reader
|
||||||
|
ordering but for the same reason: whichever end never gets attached at
|
||||||
|
all must not let pipeCloseRead/pipeCloseWrite release the slot out from
|
||||||
|
under the other end while it is still in use. stdin is left at its plain
|
||||||
|
default (whatever fd 0 already is for this kernel thread, exactly like
|
||||||
|
jobctlRunBackground's own unpiped stdin) -- an honestly-scoped limitation
|
||||||
|
matching runPipelineStages' "no stage may be a builtin": a substituted
|
||||||
|
command that genuinely needs to read stdin itself has nothing useful
|
||||||
|
wired to it here, since command substitution has no argv-supplied input
|
||||||
|
of its own to give it.
|
||||||
|
Joins the child (threadJoin) BEFORE ever reading a single byte, then
|
||||||
|
drains whatever it wrote with plain non-blocking pipeRead calls -- the
|
||||||
|
opposite order of "read until EOF, then join" would deadlock in this
|
||||||
|
kernel every time: reclaimOpenFds (sched.c), which closes a dead
|
||||||
|
thread's own fd 1 and so is what actually makes pipeRead ever report
|
||||||
|
real end-of-file (writers reaching 0), runs ONLY from inside threadReap
|
||||||
|
(threadJoin's own last step), never from threadExit/threadKill
|
||||||
|
themselves -- a thread that has already died but not yet been reaped
|
||||||
|
still counts, from the pipe's own point of view, as holding fd 1 open.
|
||||||
|
Reading first would therefore block forever waiting for an EOF that can
|
||||||
|
only ever arrive AFTER the very threadJoin call that would come next.
|
||||||
|
Joining first instead means the child's own write(s) must already have
|
||||||
|
completed (and fit in the pipe's ring buffer without blocking on free
|
||||||
|
space, exactly like jobctlSpawnPipedHereString's own here-string write
|
||||||
|
already assumes for the identical pipeBufferSize=256 constant) by the
|
||||||
|
time it actually exits -- an honestly-scoped limitation this function
|
||||||
|
shares with that one: a substituted command whose own combined stdout
|
||||||
|
exceeds pipeBufferSize bytes would block on a full buffer waiting for a
|
||||||
|
reader that will not start draining until this function's own
|
||||||
|
threadJoin below returns, which itself requires the write to unblock
|
||||||
|
first -- the same ordering deadlock in the opposite direction, avoided
|
||||||
|
here simply by staying within the same generous margin every other
|
||||||
|
pipeBufferSize-sized fixed buffer in this kernel already relies on.
|
||||||
|
Copies at most outCap-1 bytes into outBuf (NUL-terminated); any further
|
||||||
|
buffered bytes past that are still drained (into a small scratch
|
||||||
|
buffer, discarded) rather than left sitting in the pipe. *outStatus
|
||||||
|
receives the child's real exit code. Returns false, touching neither
|
||||||
|
outBuf nor *outStatus, on the same load/spawn failures as
|
||||||
|
jobctlSpawnPiped, or if the pipe table itself is exhausted
|
||||||
|
(vfsPipeCreateHandle). */
|
||||||
|
bool jobctlCaptureOutput(const char* path, const char* const* argv, int argc,
|
||||||
|
char* outBuf, int outCap, int64* outStatus)
|
||||||
|
{
|
||||||
|
int handle = vfsPipeCreateHandle();
|
||||||
|
if (handle < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pipeAddReader(handle);
|
||||||
|
|
||||||
|
uint32 pid = jobctlSpawnPiped(path, argv, argc, -1, handle, threadInvalidId);
|
||||||
|
if (pid == threadInvalidId) {
|
||||||
|
pipeCloseRead(handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outStatus = threadJoin(pid);
|
||||||
|
|
||||||
|
int used = 0;
|
||||||
|
char scratch[64];
|
||||||
|
for (;;) {
|
||||||
|
int64 n = (used < outCap - 1)
|
||||||
|
? pipeRead(handle, outBuf + used, (uint64)(outCap - 1 - used))
|
||||||
|
: pipeRead(handle, scratch, sizeof(scratch));
|
||||||
|
if (n <= 0)
|
||||||
|
break;
|
||||||
|
if (used < outCap - 1)
|
||||||
|
used += (int)n;
|
||||||
|
}
|
||||||
|
outBuf[used] = 0;
|
||||||
|
|
||||||
|
pipeCloseRead(handle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user