diff options
author | Freya Murphy <freya@freyacat.org> | 2025-03-25 17:36:52 -0400 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2025-03-25 17:38:22 -0400 |
commit | 6af21e6a4f2251e71353562d5df7f376fdffc270 (patch) | |
tree | de20c7afc9878422c81e34f30c6b010075e9e69a /user/shell.c | |
download | comus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.gz comus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.bz2 comus-6af21e6a4f2251e71353562d5df7f376fdffc270.zip |
initial checkout from wrc
Diffstat (limited to 'user/shell.c')
-rw-r--r-- | user/shell.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/user/shell.c b/user/shell.c new file mode 100644 index 0000000..5412033 --- /dev/null +++ b/user/shell.c @@ -0,0 +1,343 @@ +#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[MAX_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 ); +} |