kern/user/init.c
2025-03-31 12:41:04 -04:00

185 lines
4.3 KiB
C

#include <common.h>
/**
** Initial process; it starts the other top-level user processes.
**
** Prints a message at startup, '+' after each user process is spawned,
** and '!' before transitioning to wait() mode to the SIO, and
** startup and transition messages to the console. It also reports
** each child process it collects via wait() to the console along
** with that child's exit status.
*/
/*
** "Spawn table" process entry. Similar to the one in shell.c, but
** this version has a field to hold the PID of the spawned process
** to allow 'init' to respawn it when it terminates.
*/
typedef struct proc_s {
uint_t index; // process table index
uint_t pid; // its PID (when spawned)
uint8_t e_prio; // process priority
char select[3]; // identifying character, NUL, extra
char *args[N_ARGS]; // argument vector strings
} proc_t;
/*
** Create a spawn table entry for a process with a string literal
** as its argument buffer. We rely on the fact that the C standard
** ensures our array of pointers will be filled out with NULLs
*/
#define PROCENT(e, p, s, ...) \
{ \
e, 0, p, s, \
{ \
__VA_ARGS__, NULL \
} \
}
// sentinel value for the end of the table - must be updated
// if you have more than 90,210 user programs in the table
#define TBLEND 90210
/*
** This table contains one entry for each process that should be
** started by 'init'. Typically, this includes the 'idle' process
** and a 'shell' process.
*/
static proc_t spawn_table[] = {
// the idle process; it runs at Deferred priority,
// so it will only be dispatched when there is
// nothing else available to be dispatched
PROCENT(Idle, PRIO_DEFERRED, "!", "idle", "."),
// the user shell
PROCENT(Shell, PRIO_STD, "@", "shell"),
// PROCENT( 0, 0, 0, 0 )
{ TBLEND }
};
// character to be printed by init when it spawns a process
static char ch = '+';
/**
** process - spawn all user processes listed in the supplied table
**
** @param proc pointer to the spawn table entry to be used
*/
static void process(proc_t *proc)
{
char buf[128];
// kick off the process
int32_t p = fork();
if (p < 0) {
// error!
sprint(buf, "INIT: fork for #%d failed\n", (uint32_t)(proc->index));
cwrites(buf);
} else if (p == 0) {
// change child's priority
(void)setprio(proc->e_prio);
// now, send it on its way
exec(proc->index, proc->args);
// uh-oh - should never get here!
sprint(buf, "INIT: exec(0x%08x) failed\n", (uint32_t)(proc->index));
cwrites(buf);
} else {
// parent just reports that another one was started
swritech(ch);
proc->pid = p;
}
}
/*
** The initial user process. Should be invoked with zero or one
** argument; if provided, the first argument should be the ASCII
** character 'init' will print to indicate the spawning of a process.
*/
USERMAIN(main)
{
char buf[128];
// check to see if we got a non-standard "spawn" character
if (argc > 1) {
// maybe - check it to be sure it's printable
uint_t i = argv[1][0];
if (i > ' ' && i < 0x7f) {
ch = argv[1][0];
}
}
cwrites("Init started\n");
// home up, clear on a TVI 925
swritech('\x1a');
// wait a bit
DELAY(SHORT);
// a bit of Dante to set the mood :-)
swrites("\n\nSpem relinquunt qui huc intrasti!\n\n\r");
/*
** Start all the user processes
*/
cwrites("INIT: starting user processes\n");
proc_t *next;
for (next = spawn_table; next->index != TBLEND; ++next) {
process(next);
}
swrites(" !!!\r\n\n");
/*
** At this point, we go into an infinite loop waiting
** for our children (direct, or inherited) to exit.
*/
cwrites("INIT: transitioning to wait() mode\n");
for (;;) {
int32_t status;
int whom = waitpid(0, &status);
// PIDs must be positive numbers!
if (whom <= 0) {
sprint(buf, "INIT: waitpid() returned %d???\n", whom);
cwrites(buf);
} else {
// got one; report it
sprint(buf, "INIT: pid %d exit(%d)\n", whom, status);
cwrites(buf);
// figure out if this is one of ours
for (next = spawn_table; next->index != TBLEND; ++next) {
if (next->pid == whom) {
// one of ours - reset the PID field
// (in case the spawn attempt fails)
next->pid = 0;
// and restart it
process(next);
break;
}
}
}
}
/*
** SHOULD NEVER REACH HERE
*/
cwrites("*** INIT IS EXITING???\n");
exit(1);
return (1); // shut the compiler up
}