feat: add onUsr1, streq and printOut
This commit is contained in:
1 parent
35e5dad2f3
commit
2709025a74
4 files changed
+201
No files matched your search
@@ -0,0 +1,95 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only ever spawned by sigtest.c, with a shared pipe handle as argv[1]
|
||||||
|
* (decToString'd, exactly like fifomaster.c's children -- see that file).
|
||||||
|
* Proves real, catchable signal delivery end to end: install a handler for
|
||||||
|
* sigUsr1 through userSigAction, wait to actually be interrupted by one
|
||||||
|
* arriving from a completely separate process (sigtest.c, via
|
||||||
|
* userSigSend -- not the kernel injecting it directly), and keep running
|
||||||
|
* normally afterwards -- the handler firing AND the loop below correctly
|
||||||
|
* resuming (not corrupting registers, not re-entering main, not looping
|
||||||
|
* forever) is the round trip through signal.c's sigConsumePending, the
|
||||||
|
* shared trampoline page (sigTrampoline.S), and sysSigReturn all actually
|
||||||
|
* working together, not just each piece compiling.
|
||||||
|
*
|
||||||
|
* The pipe exists purely to close a real race sigtest.c originally papered
|
||||||
|
* over with a fixed userSleep: sigUsr1's default disposition (no handler
|
||||||
|
* installed) is Terminate, not Ignore, so a signal that arrives before
|
||||||
|
* userSigAction below has actually run would kill this process instead of
|
||||||
|
* being caught -- observed for real under host load (sigtarget killed by
|
||||||
|
* "uncaught signal 10" before it ever printed "waiting for signal").
|
||||||
|
* Writing one byte AFTER installing the handler, and having sigtest.c
|
||||||
|
* block on reading it before ever calling userSigSend, makes "handler
|
||||||
|
* installed" happen-before "signal sent" for real, the same guarantee
|
||||||
|
* fifomaster.c's children get from their own shared pipe.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static volatile int handlerFired = 0;
|
||||||
|
static volatile int handlerSignum = 0;
|
||||||
|
|
||||||
|
static void onUsr1(int signum)
|
||||||
|
{
|
||||||
|
handlerFired = 1;
|
||||||
|
handlerSignum = signum;
|
||||||
|
print("sigtarget: handler invoked signum=");
|
||||||
|
printDec(signum);
|
||||||
|
print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
print("sigtarget: start, pid=");
|
||||||
|
printDec(userGetpid());
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
if (argc != 2) {
|
||||||
|
print("sigtarget: FAIL expected one argv (pipe handle)\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSigAction(sigUsr1, onUsr1, 0, 0) == (long)sigSetFailed) {
|
||||||
|
print("sigtarget: FAIL install handler\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long handle = parseDec(argv[1]);
|
||||||
|
long readyFd = userPipeAttach(handle, 1);
|
||||||
|
if (readyFd < 0) {
|
||||||
|
print("sigtarget: FAIL attach ready pipe\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (userWrite((int)readyFd, "r", 1) != 1) {
|
||||||
|
print("sigtarget: FAIL write ready byte\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
userClose((int)readyFd);
|
||||||
|
|
||||||
|
print("sigtarget: waiting for signal\n");
|
||||||
|
|
||||||
|
/* Several short sleeps rather than one long one: this kernel only
|
||||||
|
checks for a pending signal when the target thread actually returns
|
||||||
|
to ring 3 (isr.c's deliverPendingSignals) -- a thread that is still
|
||||||
|
genuinely Sleeping is not scheduled at all, so a shorter sleep gives
|
||||||
|
sigtest.c's userSigSend more chances to land promptly, without
|
||||||
|
depending on exact timing between the two independent processes. */
|
||||||
|
for (int i = 0; i < 40 && !handlerFired; i++)
|
||||||
|
userSleep(20);
|
||||||
|
|
||||||
|
if (!handlerFired) {
|
||||||
|
print("sigtarget: FAIL handler never fired\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (handlerSignum != sigUsr1) {
|
||||||
|
print("sigtarget: FAIL wrong signum delivered\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("sigtarget: handler-fired-ok\n");
|
||||||
|
|
||||||
|
/* Reached only if sysSigReturn correctly restored the exact
|
||||||
|
pre-signal context: a corrupted resume (wrong rip/rsp/registers)
|
||||||
|
would have crashed, hung, or skipped straight past this line
|
||||||
|
instead of returning here from the interrupted sleep loop above. */
|
||||||
|
print("sigtarget: resumed-after-handler-ok\n");
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/* A plain child program: only ever launched via proctest.c's userSpawn, so
|
||||||
|
its pid is a genuinely different, freshly created thread rather than the
|
||||||
|
caller's own (unlike exectarget.c, which replaces the caller in place). */
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
print("spawntarget: child running, pid=");
|
||||||
|
printDec(userGetpid());
|
||||||
|
print("\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Companion to stderrwriter.c -- reads fd 0 (redirected by the shell to a
|
||||||
|
* REAL file via `<`, jobctlApplyRedirects) in a loop until a real EOF,
|
||||||
|
* exactly like redirreader.c does, then compares byte-for-byte against two
|
||||||
|
* back-to-back copies of stderrwriter.c's own payload -- proof that `2>`
|
||||||
|
* (create/truncate) followed by `2>>` (append) on the SAME path produced
|
||||||
|
* exactly that file, not a truncated/duplicated/reordered one.
|
||||||
|
*
|
||||||
|
* This process's own fd 1 (stdout) is NOT redirected -- print() (ulib.h)
|
||||||
|
* reaches the real console exactly like any unredirected demo, so the
|
||||||
|
* needle below is exactly as observable to ci-test.sh as every other one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int streq(const char* a, const char* b)
|
||||||
|
{
|
||||||
|
while (*a && *a == *b) {
|
||||||
|
a++;
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
return *a == *b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
print("stderrreader: reading redirected stdin\n");
|
||||||
|
|
||||||
|
char buffer[80];
|
||||||
|
unsigned long total = 0;
|
||||||
|
for (;;) {
|
||||||
|
long got = userRead(0, buffer + total, sizeof(buffer) - 1 - total);
|
||||||
|
if (got < 0) {
|
||||||
|
print("stderrreader: FAIL read\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (got == 0)
|
||||||
|
break;
|
||||||
|
total += (unsigned long)got;
|
||||||
|
if (total >= sizeof(buffer) - 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer[total] = 0;
|
||||||
|
|
||||||
|
print("stderrreader: got: ");
|
||||||
|
print(buffer);
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
int ok = streq(buffer, "stderr-payload-okstderr-payload-ok");
|
||||||
|
print(ok ? "stderrreader: payload-ok\n" : "stderrreader: payload-FAIL\n");
|
||||||
|
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Live ring-3 proof for shell.c's new `cmd 2> file` / `cmd 2>> file` stderr
|
||||||
|
* redirection (jobctlApplyRedirects/jobctlSpawnForegroundRedirected,
|
||||||
|
* jobctl.h): the exact mirror image of redirwriter.c's own `>`/`>>` proof,
|
||||||
|
* with fd 1 and fd 2's roles swapped -- this program writes its payload to
|
||||||
|
* fd 2 (the fd the shell redirects here) and its own diagnostics to fd 1
|
||||||
|
* (which stays on the real console in this demo, since only fd 2 is ever
|
||||||
|
* redirected). Writing the diagnostics through fd 2 would corrupt the exact
|
||||||
|
* bytes stderrreader.c later compares, the same hazard redirwriter.c avoids
|
||||||
|
* by keeping its own diagnostics off fd 1.
|
||||||
|
*
|
||||||
|
* kmain.c's stderrDriverThread runs this program TWICE against the same
|
||||||
|
* path -- once through `2>` (truncate/create) and once through `2>>`
|
||||||
|
* (append) -- so the resulting file holds two back-to-back copies of this
|
||||||
|
* same payload with no separator, proving `2>>` genuinely continues from
|
||||||
|
* the end of what `2>` already wrote rather than overwriting it.
|
||||||
|
*/
|
||||||
|
#define stderrPayload "stderr-payload-ok"
|
||||||
|
|
||||||
|
static void printOut(const char* text)
|
||||||
|
{
|
||||||
|
userWrite(1, text, stringLength(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printOut("stderrwriter: writing payload to stderr (redirected)\n");
|
||||||
|
|
||||||
|
unsigned long length = stringLength(stderrPayload);
|
||||||
|
long written = userWrite(2, stderrPayload, length);
|
||||||
|
if (written != (long)length) {
|
||||||
|
printOut("stderrwriter: FAIL short/failed write\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printOut("stderrwriter: wrote payload\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user