feat: add printErr, streq and onUsr1

This commit is contained in:
dualfroz committed 2026-09-08 23:25:00 +02:00
1 parent b4acf5d4d2
commit 7f4eb8bfa4
3 files changed
+192

No files matched your search

+57
View File
@@ -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;
}
+55
View File
@@ -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;
}
+80
View File
@@ -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;
}