feat: add onUsr1 and printErr
This commit is contained in:
1 parent
9480af603f
commit
0f4437ed80
3 files changed
+203
No files matched your search
@@ -0,0 +1,90 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Spawned directly by kmain.c at boot, alongside pgroupfg.c. Deliberately
|
||||||
|
* NEVER told to keyboardSetForegroundPgid -- every boot-time spawnUser call
|
||||||
|
* becomes its own new process group leader by default (sched.c's
|
||||||
|
* initialPgid), so this process's pgid already differs from pgroupfg's
|
||||||
|
* without any extra call, the same way two independently-launched
|
||||||
|
* background jobs in a real shell would each get their own group. This is
|
||||||
|
* the CONTROL side of the pgroupfg/pgroupbg pair: pgroupfg proves the
|
||||||
|
* injected Ctrl-C reaches the foreground group and kills it; this file
|
||||||
|
* proves that exact same event does NOT reach a DIFFERENT group at all --
|
||||||
|
* if keyboard.c's scoping were ever broken to deliver sigInt/sigTstp to
|
||||||
|
* every live thread (the bug this whole subsystem exists to prevent), this
|
||||||
|
* process would already be Dead by the time kmain.c's injector later tries
|
||||||
|
* to prove it alive, and every needle below would simply never appear.
|
||||||
|
*
|
||||||
|
* Along the way, exercises the actual syscall boundary (sysSetpgid/
|
||||||
|
* sysGetpgid, syscall.c) that sched.c's own KTEST coverage cannot reach on
|
||||||
|
* its own (ktestRunAll runs before schedStart(), with no int 0x80 path at
|
||||||
|
* all -- see schedTest.c's file comment): userGetpgid(0) must already
|
||||||
|
* equal this process's own pid (new-group-leader default), and an
|
||||||
|
* explicit, real setpgid(0, 0) self-affirmation (the same defensive call a
|
||||||
|
* real shell places a background job's own PID with, even when it is
|
||||||
|
* already its own leader) must be a harmless no-op.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static volatile int handlerFired = 0;
|
||||||
|
|
||||||
|
static void onUsr1(int signum)
|
||||||
|
{
|
||||||
|
(void)signum;
|
||||||
|
handlerFired = 1;
|
||||||
|
print("pgroupbg: still-alive-after-ctrlc-ok\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
long myPid = userGetpid();
|
||||||
|
print("pgroupbg: start, pid=");
|
||||||
|
printDec(myPid);
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
long pgid = userGetpgid(0);
|
||||||
|
if (pgid != myPid) {
|
||||||
|
print("pgroupbg: FAIL expected to be its own group leader\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (userSetpgid(0, 0) != 0) {
|
||||||
|
print("pgroupbg: FAIL self setpgid(0,0) rejected\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (userGetpgid(0) != myPid) {
|
||||||
|
print("pgroupbg: FAIL pgid changed after self-affirming setpgid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("pgroupbg: own-process-group-ok\n");
|
||||||
|
|
||||||
|
if (userSigAction(sigUsr1, onUsr1, 0, 0) == (long)sigSetFailed) {
|
||||||
|
print("pgroupbg: FAIL install handler\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long victimHandle = userPipeCreate();
|
||||||
|
if (victimHandle < 0) {
|
||||||
|
print("pgroupbg: FAIL create victim pipe\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
long victimFd = userPipeAttach(victimHandle, 0);
|
||||||
|
if (victimFd < 0) {
|
||||||
|
print("pgroupbg: FAIL attach victim pipe\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("pgroupbg: ready, blocking on go pipe\n");
|
||||||
|
char buffer[4];
|
||||||
|
userRead((int)victimFd, buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
if (!handlerFired) {
|
||||||
|
print("pgroupbg: FAIL read returned without being signaled\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reached only if the read was genuinely interrupted (errInterrupted,
|
||||||
|
no sigFlagRestart) by onUsr1 above, itself only ever sent by
|
||||||
|
kmain.c's own injector thread well AFTER the Ctrl-C event -- proving
|
||||||
|
this process was never touched by it at all. */
|
||||||
|
print("pgroupbg: resumed-after-signal-ok\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Spawned directly by kmain.c at boot, alongside pgroupbg.c -- the live
|
||||||
|
* ring-3 proof that sched.h's new Thread.pgid and keyboard.c's new real
|
||||||
|
* Ctrl-C actually scope a terminal-generated signal to the FOREGROUND
|
||||||
|
* process group, not to "every process reading a keyboard" or "one single
|
||||||
|
* hardcoded target". kmain.c makes this process's own pgid (its default,
|
||||||
|
* unchanged: every boot-time spawnUser call becomes its own new group
|
||||||
|
* leader, sched.c's initialPgid) the foreground via
|
||||||
|
* keyboardSetForegroundPgid right after spawning it.
|
||||||
|
*
|
||||||
|
* Installs NO handler for sigInt at all -- the real-world default
|
||||||
|
* disposition (signal.c's sigDefaultIsIgnore does not list sigInt) is
|
||||||
|
* Terminate, exactly like a real un-caught Ctrl-C killing a foreground
|
||||||
|
* program with no signal handling of its own. The read below blocks on a
|
||||||
|
* pipe no writer will ever attach to (the same "blocked forever" setup
|
||||||
|
* eintrtarget.c/killvictim.c/alarmdefault.c already use), so the only way
|
||||||
|
* this process ever leaves it is the kernel-injected Ctrl-C's early-wake
|
||||||
|
* (signal.c's sigWouldDeliver/sched.c's threadWakeForSignal, the same
|
||||||
|
* mechanism eintrtarget.c's own sigTerm already proves) actually reaching
|
||||||
|
* a genuinely Blocked thread and terminating it. If keyboard.c's group
|
||||||
|
* scoping were ever broken such that this process were somehow excluded
|
||||||
|
* from its own foreground group, this process would simply never return
|
||||||
|
* from the read below, hanging the whole boot gate to its 25s QEMU timeout
|
||||||
|
* instead of merely failing a check -- the same fault-injection-visible
|
||||||
|
* shape eintrtest.c's own file comment already establishes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
print("pgroupfg: start, pid=");
|
||||||
|
printDec(userGetpid());
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
long victimHandle = userPipeCreate();
|
||||||
|
if (victimHandle < 0) {
|
||||||
|
print("pgroupfg: FAIL create victim pipe\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
long victimFd = userPipeAttach(victimHandle, 0);
|
||||||
|
if (victimFd < 0) {
|
||||||
|
print("pgroupfg: FAIL attach victim pipe\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("pgroupfg: ready, blocking on go pipe\n");
|
||||||
|
char buffer[4];
|
||||||
|
userRead((int)victimFd, buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
/* Only reached if userRead somehow returned on its own -- nothing ever
|
||||||
|
writes to the victim pipe, so the foreground Ctrl-C never arriving
|
||||||
|
(or arriving but not actually terminating this process) is the only
|
||||||
|
way to get here. */
|
||||||
|
print("pgroupfg: FAIL read returned without being interrupted\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Middle stage of shell.c's new N-stage pipeline live demo
|
||||||
|
* (`pipesrc | pipecat | pipesink`) -- the first process in this kernel with
|
||||||
|
* BOTH ends of two DIFFERENT real inter-process pipes baked into its fds at
|
||||||
|
* once (fd 0 from jobctlSpawnPiped's stdinHandle, fd 1 from its
|
||||||
|
* stdoutHandle). user/hcat.c already proved a piped stdout alongside a
|
||||||
|
* piped stdin, but that stdin was always a shell-fed here-string
|
||||||
|
* (jobctlSpawnPipedHereString), never a second real process-to-process
|
||||||
|
* pipe the way this one's is -- this program is the actual proof that
|
||||||
|
* shell.c's runPipelineStages can wire a genuine middle pipeline stage
|
||||||
|
* where NEITHER end is the console. 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, the same
|
||||||
|
* shape as hcat.c's/pipesink.c's own read loops.
|
||||||
|
*
|
||||||
|
* Diagnostics go to fd 2 (never fd 1), same reason as every other filter in
|
||||||
|
* this pipeline family: fd 1 here is the real payload pipesink.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("pipecat: reading piped stdin\n");
|
||||||
|
|
||||||
|
char buffer[64];
|
||||||
|
unsigned long total = 0;
|
||||||
|
for (;;) {
|
||||||
|
long got = userRead(0, buffer + total, sizeof(buffer) - 1 - total);
|
||||||
|
if (got < 0) {
|
||||||
|
printErr("pipecat: 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("pipecat: FAIL short/failed write\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printErr("pipecat: forwarded payload\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user