128 lines
4.6 KiB
C
128 lines
4.6 KiB
C
#include "ulib.h"
|
|
|
|
/*
|
|
* Boot-time driver for the whole signal-delivery demo (signal.h). Spawns
|
|
* two independent children -- sigtarget.c (installs a real handler) and
|
|
* sigdefault.c (installs none) -- and sends each a real signal through the
|
|
* full syscall path (userSigSend, not a kernel-internal shortcut), proving
|
|
* both outcomes sigConsumePending can produce: caught-and-resumed, and
|
|
* uncaught-and-terminated.
|
|
*
|
|
* sigtarget needs a real happens-before guarantee, not a sleep guess:
|
|
* sigUsr1's default disposition is Terminate, so a signal that arrives
|
|
* before sigtarget's userSigAction has actually run would kill it instead
|
|
* of being caught -- this was originally "give it 100ms and hope", which
|
|
* really did fail under host load (sigtarget killed by the uncaught
|
|
* default before it ever installed its handler). See sigtarget.c for the
|
|
* shared-pipe rendezvous (fifomaster.c's pattern) that fixes it: this
|
|
* process blocks on a real read for sigtarget's own "handler installed"
|
|
* byte before ever calling userSigSend.
|
|
*
|
|
* sigdefault has no such window -- its default disposition applies from
|
|
* the moment it exists, whether or not it has run any code yet -- so a
|
|
* plain sleep-then-send (mirroring killtest.c's own "give the victim time
|
|
* to reach its blocking read") is genuinely sufficient there.
|
|
*/
|
|
|
|
int main(void)
|
|
{
|
|
print("sigtest: start, pid=");
|
|
printDec(userGetpid());
|
|
print("\n");
|
|
|
|
if (userSigSend(999999, sigUsr1) != -1) {
|
|
print("sigtest: FAIL signal to bogus pid should fail\n");
|
|
return 1;
|
|
}
|
|
print("sigtest: signal to bad pid rejected\n");
|
|
|
|
if (userSigAction(sigKill, (SigHandler)0, 0, 0) != (long)sigSetFailed) {
|
|
print("sigtest: FAIL installing a sigKill handler should be rejected\n");
|
|
return 1;
|
|
}
|
|
print("sigtest: sigkill-handler-rejected-ok\n");
|
|
|
|
long readyHandle = userPipeCreate();
|
|
if (readyHandle < 0) {
|
|
print("sigtest: FAIL create ready pipe\n");
|
|
return 1;
|
|
}
|
|
|
|
/* Attach as reader BEFORE sigtarget even exists, let alone before it
|
|
can possibly attach+write+close its own end: pipeCloseWrite (pipe.c)
|
|
reclaims a detached pipe's slot the moment readers AND writers both
|
|
read 0, which they still would right after sigtarget's write+close
|
|
if this process had not attached yet -- observed for real (sigtarget
|
|
raced ahead, wrote its ready byte, closed, and freed the slot before
|
|
this process's own attach below ever ran, which then failed outright
|
|
on the now-invalid handle). Attaching first makes readers==1 from
|
|
the very start, so that race window never opens. */
|
|
long readyFd = userPipeAttach(readyHandle, 0);
|
|
if (readyFd < 0) {
|
|
print("sigtest: FAIL attach ready pipe\n");
|
|
return 1;
|
|
}
|
|
|
|
char readyHandleStr[24];
|
|
decToString(readyHandle, readyHandleStr);
|
|
|
|
char* targetArgv[] = { "/bin/sigtarget", readyHandleStr, 0 };
|
|
long target = userSpawn("/bin/sigtarget", targetArgv);
|
|
if (target < 0) {
|
|
print("sigtest: FAIL spawn sigtarget\n");
|
|
return 1;
|
|
}
|
|
|
|
char readyByte;
|
|
/* Real blocking read: does not return until sigtarget has actually
|
|
installed its handler and written this byte (see sigtarget.c) --
|
|
the happens-before guarantee a fixed userSleep could not give. */
|
|
if (userRead((int)readyFd, &readyByte, 1) != 1) {
|
|
print("sigtest: FAIL read ready byte\n");
|
|
return 1;
|
|
}
|
|
userClose((int)readyFd);
|
|
|
|
if (userSigSend(target, sigUsr1) != 0) {
|
|
print("sigtest: FAIL send sigUsr1 to sigtarget\n");
|
|
return 1;
|
|
}
|
|
print("sigtest: sent sigUsr1 to sigtarget\n");
|
|
|
|
long status = userWait(target);
|
|
print("sigtest: sigtarget exit status=");
|
|
printDec(status);
|
|
print("\n");
|
|
if (status != 42) {
|
|
print("sigtest: FAIL expected sigtarget exit status 42\n");
|
|
return 1;
|
|
}
|
|
print("sigtest: handler-roundtrip-ok\n");
|
|
|
|
long defaultTarget = userSpawn("/bin/sigdefault", 0);
|
|
if (defaultTarget < 0) {
|
|
print("sigtest: FAIL spawn sigdefault\n");
|
|
return 1;
|
|
}
|
|
|
|
userSleep(100);
|
|
|
|
if (userSigSend(defaultTarget, sigTerm) != 0) {
|
|
print("sigtest: FAIL send sigTerm to sigdefault\n");
|
|
return 1;
|
|
}
|
|
print("sigtest: sent sigTerm to sigdefault\n");
|
|
|
|
long defaultStatus = userWait(defaultTarget);
|
|
print("sigtest: sigdefault exit status=");
|
|
printDec(defaultStatus);
|
|
print("\n");
|
|
if (defaultStatus != 128 + sigTerm) {
|
|
print("sigtest: FAIL expected sigdefault exit status 128+sigTerm\n");
|
|
return 1;
|
|
}
|
|
print("sigtest: default-terminate-ok\n");
|
|
|
|
return 0;
|
|
}
|