feat: add printErr, streq and onUsr1
This commit is contained in:
1 parent
b4acf5d4d2
commit
7f4eb8bfa4
3 files changed
+192
No files matched your search
+57
@@ -0,0 +1,57 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Left half of shell.c's new `cmd1 <<< TEXT | cmd2` here-string-into-
|
||||
* pipeline live demo -- jobctl.c's new jobctlSpawnPipedHereString wires
|
||||
* THIS process's fd 0 to a real pipe the shell itself writes the typed
|
||||
* `<<<` text into, AND its fd 1 to the pipeline's shared pipe (right
|
||||
* half's fd 0), both BEFORE this process ever runs a single instruction.
|
||||
* From this program's own point of view there is nothing special about
|
||||
* either fd: it is simply "cat" -- read stdin to real EOF, then write
|
||||
* exactly those bytes to stdout -- with no idea either end is a pipe
|
||||
* rather than a real file or console. Named "hcat" (not "cat") purely so
|
||||
* the driver thread's typed line (binary name + `<<<` + text + `|` +
|
||||
* binary name) stays clearly under keyboard.c's 32-byte
|
||||
* consoleReaderBufferSize per line -- see kmain.c's FALA 31 lesson,
|
||||
* documented right where stderrDriverThread's own path length once
|
||||
* crossed that exact boundary.
|
||||
*
|
||||
* Diagnostics go to fd 2 (never fd 1) for the same reason pipesrc.c's own
|
||||
* printErr does: fd 1 here is the real pipeline payload hsink.c compares
|
||||
* byte-for-byte, and print()/printDec() (ulib.h) are both hardcoded to
|
||||
* fd 1.
|
||||
*/
|
||||
|
||||
static void printErr(const char* text)
|
||||
{
|
||||
userWrite(2, text, stringLength(text));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printErr("hcat: reading here-string stdin\n");
|
||||
|
||||
char buffer[64];
|
||||
unsigned long total = 0;
|
||||
for (;;) {
|
||||
long got = userRead(0, buffer + total, sizeof(buffer) - 1 - total);
|
||||
if (got < 0) {
|
||||
printErr("hcat: FAIL read\n");
|
||||
return 1;
|
||||
}
|
||||
if (got == 0)
|
||||
break;
|
||||
total += (unsigned long)got;
|
||||
if (total >= sizeof(buffer) - 1)
|
||||
break;
|
||||
}
|
||||
|
||||
long written = userWrite(1, buffer, total);
|
||||
if (written != (long)total) {
|
||||
printErr("hcat: FAIL short/failed write\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printErr("hcat: forwarded payload\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Right half of shell.c's new `cmd1 <<< TEXT | cmd2` here-string-into-
|
||||
* pipeline live demo -- see hcat.c's own header comment for the full
|
||||
* picture. jobctlSpawnPiped bakes the pipeline's shared pipe's read end
|
||||
* into this process's own fd 0 exactly like pipesink.c's, so the blocking
|
||||
* userRead below has to genuinely retry across timer ticks until hcat.c's
|
||||
* own forwarded write actually lands. Compares the received bytes
|
||||
* byte-for-byte against the exact text kmain.c's driver thread types after
|
||||
* `<<<` plus the single trailing newline jobctlSpawnPipedHereString always
|
||||
* appends (matching a real shell's own `<<<` behavior) -- unlike hcat.c,
|
||||
* this process's own fd 1 (stdout) is NOT part of the pipeline, so
|
||||
* print()/printDec() (ulib.h) reach the real console exactly like
|
||||
* pipesink.c's own needle.
|
||||
*/
|
||||
#define hsinkPayload "hpipe-ok\n"
|
||||
|
||||
static int streq(const char* a, const char* b)
|
||||
{
|
||||
while (*a && *a == *b) {
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("hsink: blocking on read, waiting for hcat\n");
|
||||
|
||||
char buffer[32];
|
||||
unsigned long total = 0;
|
||||
for (;;) {
|
||||
long got = userRead(0, buffer + total, sizeof(buffer) - 1 - total);
|
||||
if (got < 0) {
|
||||
print("hsink: FAIL read\n");
|
||||
return 1;
|
||||
}
|
||||
if (got == 0)
|
||||
break;
|
||||
total += (unsigned long)got;
|
||||
if (total >= sizeof(buffer) - 1)
|
||||
break;
|
||||
}
|
||||
buffer[total] = 0;
|
||||
|
||||
print("hsink: got: ");
|
||||
print(buffer);
|
||||
|
||||
int ok = streq(buffer, hsinkPayload);
|
||||
print(ok ? "hsink: payload-ok\n" : "hsink: payload-FAIL\n");
|
||||
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Companion to jobctlfg.c: loaded the same way, on demand, when kmain.c's
|
||||
* jobctlDriverThread types "jobctlbg &" at the real interactive shell.
|
||||
* The trailing "&" (shell.c's runProgram) routes this through
|
||||
* jobctlRunBackground (jobctl.c) instead of jobctlSpawnForeground -- own
|
||||
* new process group leader exactly like jobctlfg.c, but the shell never
|
||||
* calls keyboardSetForegroundPgid for it and never blocks waiting for it
|
||||
* either, a real shell's `&` job semantics. This is the CONTROL side of
|
||||
* the jobctlfg/jobctlbg pair: jobctlfg proves the shell's foreground
|
||||
* grant actually reaches an injected Ctrl-C; this file proves that exact
|
||||
* same event does NOT reach a job the shell deliberately left in the
|
||||
* background -- if shell.c's `&` handling or jobctl.c's group-leader
|
||||
* assignment were ever broken such that this process shared jobctlfg's
|
||||
* group (or somehow ended up foreground itself), this process would
|
||||
* already be Dead by the time jobctlDriverThread later tries to prove it
|
||||
* alive, and its own needles below would simply never appear.
|
||||
*
|
||||
* Also exercises the actual sysGetpgid syscall boundary (unreachable from
|
||||
* KTEST, which runs before schedStart() with no int 0x80 path at all) to
|
||||
* confirm jobctl.c's threadSetPgid(pid, pid) call genuinely took effect
|
||||
* from ring3's own point of view, not just internally.
|
||||
*/
|
||||
|
||||
static volatile int handlerFired = 0;
|
||||
|
||||
static void onUsr1(int signum)
|
||||
{
|
||||
(void)signum;
|
||||
handlerFired = 1;
|
||||
print("jobctlbg: still-alive-after-ctrlc-ok\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
long myPid = userGetpid();
|
||||
print("jobctlbg: start, pid=");
|
||||
printDec(myPid);
|
||||
print("\n");
|
||||
|
||||
long pgid = userGetpgid(0);
|
||||
if (pgid != myPid) {
|
||||
print("jobctlbg: FAIL expected to be its own group leader\n");
|
||||
return 1;
|
||||
}
|
||||
print("jobctlbg: own-process-group-ok\n");
|
||||
|
||||
if (userSigAction(sigUsr1, onUsr1, 0, 0) == (long)sigSetFailed) {
|
||||
print("jobctlbg: FAIL install handler\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
long victimHandle = userPipeCreate();
|
||||
if (victimHandle < 0) {
|
||||
print("jobctlbg: FAIL create victim pipe\n");
|
||||
return 1;
|
||||
}
|
||||
long victimFd = userPipeAttach(victimHandle, 0);
|
||||
if (victimFd < 0) {
|
||||
print("jobctlbg: FAIL attach victim pipe\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
print("jobctlbg: ready, blocking on go pipe\n");
|
||||
char buffer[4];
|
||||
userRead((int)victimFd, buffer, sizeof(buffer));
|
||||
|
||||
if (!handlerFired) {
|
||||
print("jobctlbg: FAIL read returned without being signaled\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Reached only if the read was genuinely interrupted (errInterrupted,
|
||||
no sigFlagRestart) by onUsr1 above, sent by kmain.c's own driver
|
||||
thread well AFTER the Ctrl-C injected for jobctlfg -- proving this
|
||||
backgrounded process was never touched by it. */
|
||||
print("jobctlbg: resumed-after-signal-ok\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user