84 lines
3.2 KiB
C
84 lines
3.2 KiB
C
#include "ulib.h"
|
|
|
|
/*
|
|
* Exercises the process-management syscalls added this wave: sysSpawn
|
|
* (posix_spawn()-lite -- loads a binary into a brand new thread) and
|
|
* sysWait (threadJoin, sched.c/sched.h) together, then sysExec (execve()-
|
|
* lite -- replaces this very process's image in place, see syscall.c
|
|
* doExec). Split across two helper binaries deliberately: spawntarget.c is
|
|
* only ever spawned (never exec'd), exectarget.c is only ever exec'd
|
|
* (never spawned) -- frostbyte has one shared address space system-wide
|
|
* (paging.c has a single page table, no per-process CR3), so two binaries
|
|
* whose fixed link addresses collide can never be genuinely live at the
|
|
* same time. Keeping "the spawn target" and "the exec target" as distinct
|
|
* binaries, and waiting for the spawned child before ever calling exec,
|
|
* means this test never asks the kernel to do something the architecture
|
|
* cannot support.
|
|
*
|
|
* This function never returns on its own success path: the final call is
|
|
* userExec("/bin/exectarget"), which -- if the syscall does its job --
|
|
* replaces this thread's own code/stack/pid identity with exectarget's, so
|
|
* "proctest" never gets to print anything past that call. kmain.c's
|
|
* execReaperThread separately threadJoin()s this same pid and checks it
|
|
* eventually reports exectarget's exit code, which is the only way to
|
|
* observe that the exec() actually happened from outside this process.
|
|
*/
|
|
|
|
int main(void)
|
|
{
|
|
print("proctest: start, pid=");
|
|
printDec(userGetpid());
|
|
print("\n");
|
|
|
|
long child = userSpawn("/bin/spawntarget", 0);
|
|
if (child < 0) {
|
|
print("proctest: FAIL spawn /bin/spawntarget\n");
|
|
return 1;
|
|
}
|
|
|
|
long status = userWait(child);
|
|
if (status != 3) {
|
|
print("proctest: FAIL wait returned unexpected status\n");
|
|
return 1;
|
|
}
|
|
print("proctest: spawn+wait ok\n");
|
|
|
|
if (userSpawn("/bin/does-not-exist", 0) != -1) {
|
|
print("proctest: FAIL spawn of missing binary should fail\n");
|
|
return 1;
|
|
}
|
|
print("proctest: spawn bad path rejected\n");
|
|
|
|
if (userWait(999999) != -1) {
|
|
print("proctest: FAIL wait on bogus pid should fail immediately\n");
|
|
return 1;
|
|
}
|
|
print("proctest: wait bad pid rejected\n");
|
|
|
|
/* argtest.c checks its own argc/argv against exactly this list --
|
|
proves the whole chain (copyUserArgv copying these out of THIS
|
|
process's memory, elfBuildInitialStack laying them onto the child's
|
|
brand new stack, crt.S's _start reading them back off of it) rather
|
|
than just that spawn/wait still works with a null argv. */
|
|
char* argtestArgv[] = { "/bin/argtest", "alpha", "beta", "gamma", 0 };
|
|
long argChild = userSpawn("/bin/argtest", argtestArgv);
|
|
if (argChild < 0) {
|
|
print("proctest: FAIL spawn /bin/argtest\n");
|
|
return 1;
|
|
}
|
|
|
|
long argStatus = userWait(argChild);
|
|
if (argStatus != 0) {
|
|
print("proctest: FAIL argtest reported bad argv\n");
|
|
return 1;
|
|
}
|
|
print("proctest: spawn with argv ok\n");
|
|
|
|
userExec("/bin/exectarget", 0);
|
|
|
|
/* Only reached if userExec failed outright (bad path, load failure) --
|
|
a successful exec never returns here at all. */
|
|
print("proctest: FAIL exec did not take over\n");
|
|
return 1;
|
|
}
|