#include <types.h>
#include <kern/errno.h>
#include <kern/reboot.h>
#include <kern/unistd.h>
#include <limits.h>
#include <lib.h>
#include <uio.h>
#include <clock.h>
#include <thread.h>
#include <vfs.h>
#include <sfs.h>
#include <syscall.h>
#include <kern/fcntl.h>
#include <test.h>
#include <proc.h>
#include <file.h>
#include <current.h>
#include "opt-synchprobs.h"
#include "opt-sfs.h"
#include "opt-net.h"
struct proc *p0;
struct cmd_progthread_args {
char **args;
struct proc *p;
};
#define _PATH_SHELL "/bin/sh"
#define MAXMENUARGS 16
void
getinterval(time_t s1, uint32_t ns1, time_t s2, uint32_t ns2,
time_t *rs, uint32_t *rns)
{
if (ns2 < ns1) {
ns2 += 1000000000;
s2--;
}
*rns = ns2 - ns1;
*rs = s2 - s1;
}
static
int
open_standard_files( struct proc *p ) {
int err = 0;
int retval;
char buf[32];
KASSERT( p != NULL );
strcpy( buf, "con:" );
err = ___open( p, buf, O_RDONLY, &retval );
if( err )
return err;
strcpy( buf, "con:" );
err = ___open( p, buf, O_WRONLY, &retval );
if( err ) {
file_close_all( p );
return err;
}
strcpy( buf, "con:" );
err = ___open( p, buf, O_WRONLY, &retval );
if( err ) {
file_close_all( p );
return err;
}
return 0;
}
static
void
cmd_progthread(void *ptr, unsigned long nargs)
{
struct cmd_progthread_args *cargs = ptr;
char **args = cargs->args;
char progname[128];
int result;
KASSERT(nargs >= 1);
if (nargs > 2) {
kprintf("Warning: argument passing from menu not supported\n");
}
KASSERT(strlen(args[0]) < sizeof(progname));
strcpy(progname, args[0]);
curthread->td_proc = cargs->p;
kfree( cargs );
result = runprogram(progname);
if (result) {
kprintf("Running program %s failed: %s\n", args[0],
strerror(result));
sys__exit( -1 );
return;
}
}
static
int
common_prog(int nargs, char **args)
{
int result;
struct proc *p = NULL;
struct cmd_progthread_args *cargs = NULL;
pid_t pid;
int err;
#if OPT_SYNCHPROBS
kprintf("Warning: this probably won't work with a "
"synchronization-problems kernel.\n");
#endif
result = proc_create( &p );
if( result ) {
kprintf( "common_prog: failed creating the first process." );
return result;
}
pid = p->p_pid;
err = open_standard_files( p );
if( err ) {
proc_destroy( p );
return err;
}
cargs = kmalloc( sizeof( struct cmd_progthread_args ) );
if( cargs == NULL ) {
file_close_all( p );
proc_destroy( p );
return ENOMEM;
}
p->p_proc = p0;
cargs->args = args;
cargs->p = p;
result = thread_fork(args[0] ,
cmd_progthread ,
cargs , nargs ,
NULL);
if (result) {
kprintf("thread_fork failed: %s\n", strerror(result));
file_close_all( p );
proc_destroy( p );
return result;
}
___waitpid( pid, &result, 0 );
return 0;
}
static
int
cmd_prog(int nargs, char **args)
{
if (nargs < 2) {
kprintf("Usage: p program [arguments]\n");
return EINVAL;
}
args++;
nargs--;
return common_prog(nargs, args);
}
static
int
cmd_shell(int nargs, char **args)
{
(void)args;
if (nargs != 1) {
kprintf("Usage: s\n");
return EINVAL;
}
args[0] = (char *)_PATH_SHELL;
return common_prog(nargs, args);
}
static
int
cmd_chdir(int nargs, char **args)
{
if (nargs != 2) {
kprintf("Usage: cd directory\n");
return EINVAL;
}
return vfs_chdir(args[1]);
}
static
int
cmd_pwd(int nargs, char **args)
{
char buf[PATH_MAX+1];
int result;
struct iovec iov;
struct uio ku;
(void)nargs;
(void)args;
uio_kinit(&iov, &ku, buf, sizeof(buf)-1, 0, UIO_READ);
result = vfs_getcwd(&ku);
if (result) {
kprintf("vfs_getcwd failed (%s)\n", strerror(result));
return result;
}
buf[sizeof(buf)-1-ku.uio_resid] = 0;
kprintf("%s\n", buf);
return 0;
}
static
int
cmd_sync(int nargs, char **args)
{
(void)nargs;
(void)args;
vfs_sync();
return 0;
}
static
int
cmd_panic(int nargs, char **args)
{
(void)nargs;
(void)args;
panic("User requested panic\n");
return 0;
}
static
int
cmd_quit(int nargs, char **args)
{
(void)nargs;
(void)args;
vfs_sync();
sys_reboot(RB_POWEROFF);
thread_exit();
return 0;
}
static const struct {
const char *name;
int (*func)(const char *device);
} mounttable[] = {
#if OPT_SFS
{ "sfs", sfs_mount },
#endif
{ NULL, NULL }
};
static
int
cmd_mount(int nargs, char **args)
{
char *fstype;
char *device;
int i;
if (nargs != 3) {
kprintf("Usage: mount fstype device:\n");
return EINVAL;
}
fstype = args[1];
device = args[2];
if (device[strlen(device)-1]==':') {
device[strlen(device)-1] = 0;
}
for (i=0; mounttable[i].name; i++) {
if (!strcmp(mounttable[i].name, fstype)) {
return mounttable[i].func(device);
}
}
kprintf("Unknown filesystem type %s\n", fstype);
return EINVAL;
}
static
int
cmd_unmount(int nargs, char **args)
{
char *device;
if (nargs != 2) {
kprintf("Usage: unmount device:\n");
return EINVAL;
}
device = args[1];
if (device[strlen(device)-1]==':') {
device[strlen(device)-1] = 0;
}
return vfs_unmount(device);
}
static
int
cmd_bootfs(int nargs, char **args)
{
char *device;
if (nargs != 2) {
kprintf("Usage: bootfs device\n");
return EINVAL;
}
device = args[1];
if (device[strlen(device)-1]==':') {
device[strlen(device)-1] = 0;
}
return vfs_setbootfs(device);
}
static
int
cmd_kheapstats(int nargs, char **args)
{
(void)nargs;
(void)args;
kheap_printstats();
return 0;
}
static
void
showmenu(const char *name, const char *x[])
{
int ct, half, i;
kprintf("\n");
kprintf("%s\n", name);
for (i=ct=0; x[i]; i++) {
ct++;
}
half = (ct+1)/2;
for (i=0; i<half; i++) {
kprintf(" %-36s", x[i]);
if (i+half < ct) {
kprintf("%s", x[i+half]);
}
kprintf("\n");
}
kprintf("\n");
}
static const char *opsmenu[] = {
"[s] Shell ",
"[p] Other program ",
"[mount] Mount a filesystem ",
"[unmount] Unmount a filesystem ",
"[bootfs] Set \"boot\" filesystem ",
"[pf] Print a file ",
"[cd] Change directory ",
"[pwd] Print current directory ",
"[sync] Sync filesystems ",
"[panic] Intentional panic ",
"[q] Quit and shut down ",
NULL
};
static
int
cmd_opsmenu(int n, char **a)
{
(void)n;
(void)a;
showmenu("OS/161 operations menu", opsmenu);
return 0;
}
static const char *testmenu[] = {
"[at] Array test ",
"[bt] Bitmap test ",
"[km1] Kernel malloc test ",
"[km2] kmalloc stress test ",
"[tt1] Thread test 1 ",
"[tt2] Thread test 2 ",
"[tt3] Thread test 3 ",
#if OPT_NET
"[net] Network test ",
#endif
"[sy1] Semaphore test ",
"[sy2] Lock test (1) ",
"[sy3] CV test (1) ",
"[sy5] CV test 2 (1) ",
"[sp1] Whalematching Driver (1) ",
"[sp2] Stoplight Driver (1) ",
"[fs1] Filesystem test ",
"[fs2] FS read stress (4) ",
"[fs3] FS write stress (4) ",
"[fs4] FS write stress 2 (4) ",
"[fs5] FS create stress (4) ",
NULL
};
static
int
cmd_testmenu(int n, char **a)
{
(void)n;
(void)a;
showmenu("OS/161 tests menu", testmenu);
kprintf(" (1) These tests will fail until you finish the "
"synch assignment.\n");
kprintf(" (4) These tests may fail until you finish the "
"file system assignment.\n");
kprintf("\n");
return 0;
}
static const char *mainmenu[] = {
"[?o] Operations menu ",
"[?t] Tests menu ",
"[kh] Kernel heap stats ",
"[q] Quit and shut down ",
NULL
};
static
int
cmd_mainmenu(int n, char **a)
{
(void)n;
(void)a;
showmenu("OS/161 kernel menu", mainmenu);
return 0;
}
static struct {
const char *name;
int (*func)(int nargs, char **args);
} cmdtable[] = {
{ "?", cmd_mainmenu },
{ "h", cmd_mainmenu },
{ "help", cmd_mainmenu },
{ "?o", cmd_opsmenu },
{ "?t", cmd_testmenu },
{ "s", cmd_shell },
{ "p", cmd_prog },
{ "mount", cmd_mount },
{ "unmount", cmd_unmount },
{ "bootfs", cmd_bootfs },
{ "pf", printfile },
{ "cd", cmd_chdir },
{ "pwd", cmd_pwd },
{ "sync", cmd_sync },
{ "panic", cmd_panic },
{ "q", cmd_quit },
{ "exit", cmd_quit },
{ "halt", cmd_quit },
{ "kh", cmd_kheapstats },
{ "at", arraytest },
{ "bt", bitmaptest },
{ "km1", malloctest },
{ "km2", mallocstress },
#if OPT_NET
{ "net", nettest },
#endif
{ "tt1", threadtest },
{ "tt2", threadtest2 },
{ "tt3", threadtest3 },
{ "sy1", semtest },
{ "sy2", locktest },
{ "sy3", cvtest },
{ "sy5", cvtest2 },
#if OPT_SYNCHPROBS
{ "sp1", whalemating },
{ "sp2", stoplight },
#endif
{ "fs1", fstest },
{ "fs2", readstress },
{ "fs3", writestress },
{ "fs4", writestress2 },
{ "fs5", createstress },
{ NULL, NULL }
};
static
int
cmd_dispatch(char *cmd)
{
time_t beforesecs, aftersecs, secs;
uint32_t beforensecs, afternsecs, nsecs;
char *args[MAXMENUARGS];
int nargs=0;
char *word;
char *context;
int i, result;
for (word = strtok_r(cmd, " \t", &context);
word != NULL;
word = strtok_r(NULL, " \t", &context)) {
if (nargs >= MAXMENUARGS) {
kprintf("Command line has too many words\n");
return E2BIG;
}
args[nargs++] = word;
}
if (nargs==0) {
return 0;
}
for (i=0; cmdtable[i].name; i++) {
if (*cmdtable[i].name && !strcmp(args[0], cmdtable[i].name)) {
KASSERT(cmdtable[i].func!=NULL);
gettime(&beforesecs, &beforensecs);
result = cmdtable[i].func(nargs, args);
gettime(&aftersecs, &afternsecs);
getinterval(beforesecs, beforensecs,
aftersecs, afternsecs,
&secs, &nsecs);
kprintf("Operation took %lu.%09lu seconds\n",
(unsigned long) secs,
(unsigned long) nsecs);
return result;
}
}
kprintf("%s: Command not found\n", args[0]);
return EINVAL;
}
static
void
menu_execute(char *line, int isargs)
{
char *command;
char *context;
int result;
for (command = strtok_r(line, ";", &context);
command != NULL;
command = strtok_r(NULL, ";", &context)) {
if (isargs) {
kprintf("OS/161 kernel: %s\n", command);
}
result = cmd_dispatch(command);
if (result) {
kprintf("Menu command failed: %s\n", strerror(result));
if (isargs) {
panic("Failure processing kernel arguments\n");
}
}
}
}
static
int
proc0( struct proc **p0 ) {
struct proc *p = NULL;
int err;
err = proc_create( &p );
if( err )
return err;
err = open_standard_files( p );
if( err ) {
proc_destroy( p );
return err;
}
curthread->td_proc = p;
*p0 = p;
return 0;
}
void
menu(char *args)
{
char buf[64];
int err;
proc_system_init();
err = proc0( &p0 );
if( err )
panic( "failed creating proc0." );
menu_execute(args, 1);
while (1) {
kprintf("OS/161 kernel [? for menu]: ");
kgets(buf, sizeof(buf));
menu_execute(buf, 0);
}
}