mirror of
https://github.com/kenshineto/kern.git
synced 2025-04-10 12:37:26 +00:00
346 lines
8 KiB
C
346 lines
8 KiB
C
#include <common.h>
|
|
|
|
// should we keep going?
|
|
static bool_t time_to_stop = false;
|
|
|
|
// number of spawned but uncollected children
|
|
static int children = 0;
|
|
|
|
/*
|
|
** For the test programs in the baseline system, command-line arguments
|
|
** follow these rules. The first two entries are as follows:
|
|
**
|
|
** argv[0] the name used to "invoke" this process
|
|
** argv[1] the "character to print" (identifies the process)
|
|
**
|
|
** Most user programs have one or more additional arguments.
|
|
**
|
|
** See the comment at the beginning of each user-code source file for
|
|
** information on the argument list that code expects.
|
|
*/
|
|
|
|
/*
|
|
** "Spawn table" process entry. Similar to that in init.c,
|
|
** except this one has no place to store the PID of the child.
|
|
*/
|
|
typedef struct proc_s {
|
|
uint_t index; // process table index
|
|
int8_t 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, 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
|
|
|
|
/*
|
|
** The spawn table contains entries for processes that are started
|
|
** by the shell.
|
|
*/
|
|
static proc_t spawn_table[] = {
|
|
|
|
// Users A-C each run ProgABC, which loops printing its character
|
|
#if defined(SPAWN_A)
|
|
PROCENT(ProgABC, PRIO_STD, "A", "userA", "A", "30"),
|
|
#endif
|
|
#if defined(SPAWN_B)
|
|
PROCENT(ProgABC, PRIO_STD, "B", "userB", "B", "30"),
|
|
#endif
|
|
#if defined(SPAWN_C)
|
|
PROCENT(ProgABC, PRIO_STD, "C", "userC", "C", "30"),
|
|
#endif
|
|
|
|
// Users D and E run ProgDE, which is like ProgABC but doesn't exit()
|
|
#if defined(SPAWN_D)
|
|
PROCENT(ProgDE, PRIO_STD, "D", "userD", "D", "20"),
|
|
#endif
|
|
#if defined(SPAWN_E)
|
|
PROCENT(ProgDE, PRIO_STD, "E", "userE", "E", "20"),
|
|
#endif
|
|
|
|
// Users F and G run ProgFG, which sleeps between write() calls
|
|
#if defined(SPAWN_F)
|
|
PROCENT(ProgFG, PRIO_STD, "F", "userF", "F", "20"),
|
|
#endif
|
|
#if defined(SPAWN_G)
|
|
PROCENT(ProgFG, PRIO_STD, "G", "userG", "G", "10"),
|
|
#endif
|
|
|
|
// User H tests reparenting of orphaned children
|
|
#if defined(SPAWN_H)
|
|
PROCENT(ProgH, PRIO_STD, "H", "userH", "H", "4"),
|
|
#endif
|
|
|
|
// User I spawns several children, kills one, and waits for all
|
|
#if defined(SPAWN_I)
|
|
PROCENT(ProgI, PRIO_STD, "I", "userI", "I"),
|
|
#endif
|
|
|
|
// User J tries to spawn 2 * N_PROCS children
|
|
#if defined(SPAWN_J)
|
|
PROCENT(ProgJ, PRIO_STD, "J", "userJ", "J"),
|
|
#endif
|
|
|
|
// Users K and L iterate spawning userX and sleeping
|
|
#if defined(SPAWN_K)
|
|
PROCENT(ProgKL, PRIO_STD, "K", "userK", "K", "8"),
|
|
#endif
|
|
#if defined(SPAWN_L)
|
|
PROCENT(ProgKL, PRIO_STD, "L", "userL", "L", "5"),
|
|
#endif
|
|
|
|
// Users M and N spawn copies of userW and userZ via ProgMN
|
|
#if defined(SPAWN_M)
|
|
PROCENT(ProgMN, PRIO_STD, "M", "userM", "M", "5", "f"),
|
|
#endif
|
|
#if defined(SPAWN_N)
|
|
PROCENT(ProgMN, PRIO_STD, "N", "userN", "N", "5", "t"),
|
|
#endif
|
|
|
|
// There is no user O
|
|
|
|
// User P iterates, reporting system time and stats, and sleeping
|
|
#if defined(SPAWN_P)
|
|
PROCENT(ProgP, PRIO_STD, "P", "userP", "P", "3", "2"),
|
|
#endif
|
|
|
|
// User Q tries to execute a bad system call
|
|
#if defined(SPAWN_Q)
|
|
PROCENT(ProgQ, PRIO_STD, "Q", "userQ", "Q"),
|
|
#endif
|
|
|
|
// User R reports its PID, PPID, and sequence number; it
|
|
// calls fork() but not exec(), with each child getting the
|
|
// next sequence number, to a total of five copies
|
|
#if defined(SPAWN_R)
|
|
PROCENT(ProgR, PRIO_STD, "R", "userR", "R", "20", "1"),
|
|
#endif
|
|
|
|
// User S loops forever, sleeping 13 sec. on each iteration
|
|
#if defined(SPAWN_S)
|
|
PROCENT(ProgS, PRIO_STD, "S", "userS", "S", "13"),
|
|
#endif
|
|
|
|
// Users T-V run ProgTUV(); they spawn copies of userW
|
|
// User T waits for any child
|
|
// User U waits for each child by PID
|
|
// User V kills each child
|
|
#if defined(SPAWN_T)
|
|
PROCENT(ProgTUV, PRIO_STD, "T", "userT", "T", "6", "w"),
|
|
#endif
|
|
#if defined(SPAWN_U)
|
|
PROCENT(ProgTUV, PRIO_STD, "U", "userU", "U", "6", "W"),
|
|
#endif
|
|
#if defined(SPAWN_V)
|
|
PROCENT(ProgTUV, PRIO_STD, "V", "userV", "V", "6", "k"),
|
|
#endif
|
|
|
|
// a dummy entry to use as a sentinel
|
|
{ TBLEND }
|
|
|
|
// these processes are spawned by the ones above, and are never
|
|
// spawned directly.
|
|
|
|
// PROCENT( ProgW, PRIO_STD, "?", "userW", "W", "20", "3" ),
|
|
// PROCENT( ProgX, PRIO_STD, "?", "userX", "X", "20" ),
|
|
// PROCENT( ProgY, PRIO_STD, "?", "userY", "Y", "10" ),
|
|
// PROCENT( ProgZ, PRIO_STD, "?", "userZ", "Z", "10" )
|
|
};
|
|
|
|
/*
|
|
** usage function
|
|
*/
|
|
static void usage(void)
|
|
{
|
|
swrites("\nTests - run with '@x', where 'x' is one or more of:\n ");
|
|
proc_t *p = spawn_table;
|
|
while (p->index != TBLEND) {
|
|
swritech(' ');
|
|
swritech(p->select[0]);
|
|
}
|
|
swrites("\nOther commands: @* (all), @h (help), @x (exit)\n");
|
|
}
|
|
|
|
/*
|
|
** run a program from the program table, or a builtin command
|
|
*/
|
|
static int run(char which)
|
|
{
|
|
char buf[128];
|
|
register proc_t *p;
|
|
|
|
if (which == 'h') {
|
|
// builtin "help" command
|
|
usage();
|
|
|
|
} else if (which == 'x') {
|
|
// builtin "exit" command
|
|
time_to_stop = true;
|
|
|
|
} else if (which == '*') {
|
|
// torture test! run everything!
|
|
for (p = spawn_table; p->index != TBLEND; ++p) {
|
|
int status = spawn(p->index, p->args);
|
|
if (status > 0) {
|
|
++children;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// must be a single test; find and run it
|
|
for (p = spawn_table; p->index != TBLEND; ++p) {
|
|
if (p->select[0] == which) {
|
|
// found it!
|
|
int status = spawn(p->index, p->args);
|
|
if (status > 0) {
|
|
++children;
|
|
}
|
|
return status;
|
|
}
|
|
}
|
|
|
|
// uh-oh, made it through the table without finding the program
|
|
sprint(buf, "shell: unknown cmd '%c'\n", which);
|
|
swrites(buf);
|
|
usage();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
** edit - perform any command-line editing we need to do
|
|
**
|
|
** @param line Input line buffer
|
|
** @param n Number of valid bytes in the buffer
|
|
*/
|
|
static int edit(char line[], int n)
|
|
{
|
|
char *ptr = line + n - 1; // last char in buffer
|
|
|
|
// strip the EOLN sequence
|
|
while (n > 0) {
|
|
if (*ptr == '\n' || *ptr == '\r') {
|
|
--n;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// add a trailing NUL byte
|
|
if (n > 0) {
|
|
line[n] = '\0';
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
** shell - extremely simple shell for spawning test programs
|
|
**
|
|
** Scheduled by _kshell() when the character 'u' is typed on
|
|
** the console keyboard.
|
|
*/
|
|
USERMAIN(main)
|
|
{
|
|
// keep the compiler happy
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
// report that we're up and running
|
|
swrites("Shell is ready\n");
|
|
|
|
// print a summary of the commands we'll accept
|
|
usage();
|
|
|
|
// loop forever
|
|
while (!time_to_stop) {
|
|
char line[128];
|
|
char *ptr;
|
|
|
|
// the shell reads one line from the keyboard, parses it,
|
|
// and performs whatever command it requests.
|
|
|
|
swrites("\n> ");
|
|
int n = read(CHAN_SIO, line, sizeof(line));
|
|
|
|
// shortest valid command is "@?", so must have 3+ chars here
|
|
if (n < 3) {
|
|
// ignore it
|
|
continue;
|
|
}
|
|
|
|
// edit it as needed; new shortest command is 2+ chars
|
|
if ((n = edit(line, n)) < 2) {
|
|
continue;
|
|
}
|
|
|
|
// find the '@'
|
|
int i = 0;
|
|
for (ptr = line; i < n; ++i, ++ptr) {
|
|
if (*ptr == '@') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// did we find an '@'?
|
|
if (i < n) {
|
|
// yes; process any commands that follow it
|
|
++ptr;
|
|
|
|
for (; *ptr != '\0'; ++ptr) {
|
|
char buf[128];
|
|
int pid = run(*ptr);
|
|
|
|
if (pid < 0) {
|
|
// spawn() failed
|
|
sprint(buf, "+++ Shell spawn %c failed, code %d\n", *ptr,
|
|
pid);
|
|
cwrites(buf);
|
|
}
|
|
|
|
// should we end it all?
|
|
if (time_to_stop) {
|
|
break;
|
|
}
|
|
} // for
|
|
|
|
// now, wait for all the spawned children
|
|
while (children > 0) {
|
|
// wait for the child
|
|
int32_t status;
|
|
char buf[128];
|
|
int whom = waitpid(0, &status);
|
|
|
|
// figure out the result
|
|
if (whom == E_NO_CHILDREN) {
|
|
break;
|
|
} else if (whom < 1) {
|
|
sprint(buf, "shell: waitpid() returned %d\n", whom);
|
|
} else {
|
|
--children;
|
|
sprint(buf, "shell: PID %d exit status %d\n", whom, status);
|
|
}
|
|
// report it
|
|
swrites(buf);
|
|
}
|
|
} // if i < n
|
|
} // while
|
|
|
|
cwrites("!!! shell exited loop???\n");
|
|
exit(1);
|
|
}
|