os161-1.99
 All Data Structures
menu.c
00001 /*
00002  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
00003  *      The President and Fellows of Harvard College.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the University nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  */
00029 
00030 #include <types.h>
00031 #include <kern/errno.h>
00032 #include <kern/reboot.h>
00033 #include <kern/unistd.h>
00034 #include <limits.h>
00035 #include <lib.h>
00036 #include <uio.h>
00037 #include <clock.h>
00038 #include <thread.h>
00039 #include <proc.h>
00040 #include <synch.h>
00041 #include <vfs.h>
00042 #include <sfs.h>
00043 #include <syscall.h>
00044 #include <test.h>
00045 #include "opt-synchprobs.h"
00046 #include "opt-sfs.h"
00047 #include "opt-net.h"
00048 
00049 /*
00050  * In-kernel menu and command dispatcher.
00051  */
00052 
00053 #define _PATH_SHELL "/bin/sh"
00054 
00055 #define MAXMENUARGS  16
00056 
00057 // XXX this should not be in this file
00058 void
00059 getinterval(time_t s1, uint32_t ns1, time_t s2, uint32_t ns2,
00060             time_t *rs, uint32_t *rns)
00061 {
00062         if (ns2 < ns1) {
00063                 ns2 += 1000000000;
00064                 s2--;
00065         }
00066 
00067         *rns = ns2 - ns1;
00068         *rs = s2 - s1;
00069 }
00070 
00071 ////////////////////////////////////////////////////////////
00072 //
00073 // Command menu functions 
00074 
00075 /*
00076  * Function for a thread that runs an arbitrary userlevel program by
00077  * name.
00078  *
00079  * Note: this cannot pass arguments to the program. You may wish to 
00080  * change it so it can, because that will make testing much easier
00081  * in the future.
00082  *
00083  * It copies the program name because runprogram destroys the copy
00084  * it gets by passing it to vfs_open(). 
00085  */
00086 static
00087 void
00088 cmd_progthread(void *ptr, unsigned long nargs)
00089 {
00090         char **args = ptr;
00091         char progname[128];
00092         int result;
00093 
00094         KASSERT(nargs >= 1);
00095 
00096         if (nargs > 2) {
00097                 kprintf("Warning: argument passing from menu not supported\n");
00098         }
00099 
00100         /* Hope we fit. */
00101         KASSERT(strlen(args[0]) < sizeof(progname));
00102 
00103         strcpy(progname, args[0]);
00104 
00105         result = runprogram(progname);
00106         if (result) {
00107                 kprintf("Running program %s failed: %s\n", args[0],
00108                         strerror(result));
00109                 return;
00110         }
00111 
00112         /* NOTREACHED: runprogram only returns on error. */
00113 }
00114 
00115 /*
00116  * Common code for cmd_prog and cmd_shell.
00117  *
00118  * Note that this does not wait for the subprogram to finish, but
00119  * returns immediately to the menu. This is usually not what you want,
00120  * so you should have it call your system-calls-assignment waitpid
00121  * code after forking.
00122  *
00123  * Also note that because the subprogram's thread uses the "args"
00124  * array and strings, until you do this a race condition exists
00125  * between that code and the menu input code.
00126  */
00127 static
00128 int
00129 common_prog(int nargs, char **args)
00130 {
00131         struct proc *proc;
00132         int result;
00133 
00134 #if OPT_SYNCHPROBS
00135         kprintf("Warning: this probably won't work with a "
00136                 "synchronization-problems kernel.\n");
00137 #endif
00138 
00139         /* Create a process for the new program to run in. */
00140         proc = proc_create_runprogram(args[0] /* name */);
00141         if (proc == NULL) {
00142                 return ENOMEM;
00143         }
00144 
00145         result = thread_fork(args[0] /* thread name */,
00146                         proc /* new process */,
00147                         cmd_progthread /* thread function */,
00148                         args /* thread arg */, nargs /* thread arg */);
00149         if (result) {
00150                 kprintf("thread_fork failed: %s\n", strerror(result));
00151                 proc_destroy(proc);
00152                 return result;
00153         }
00154 
00155 #ifdef UW
00156         /* wait until the process we have just launched - and any others that it 
00157            may fork - is finished before proceeding */
00158         P(no_proc_sem);
00159 #endif // UW
00160 
00161         return 0;
00162 }
00163 
00164 /*
00165  * Command for running an arbitrary userlevel program.
00166  */
00167 static
00168 int
00169 cmd_prog(int nargs, char **args)
00170 {
00171         if (nargs < 2) {
00172                 kprintf("Usage: p program [arguments]\n");
00173                 return EINVAL;
00174         }
00175 
00176         /* drop the leading "p" */
00177         args++;
00178         nargs--;
00179 
00180         return common_prog(nargs, args);
00181 }
00182 
00183 /*
00184  * Command for starting the system shell.
00185  */
00186 static
00187 int
00188 cmd_shell(int nargs, char **args)
00189 {
00190         (void)args;
00191         if (nargs != 1) {
00192                 kprintf("Usage: s\n");
00193                 return EINVAL;
00194         }
00195 
00196         args[0] = (char *)_PATH_SHELL;
00197 
00198         return common_prog(nargs, args);
00199 }
00200 
00201 /*
00202  * Command for changing directory.
00203  */
00204 static
00205 int
00206 cmd_chdir(int nargs, char **args)
00207 {
00208         if (nargs != 2) {
00209                 kprintf("Usage: cd directory\n");
00210                 return EINVAL;
00211         }
00212 
00213         return vfs_chdir(args[1]);
00214 }
00215 
00216 /*
00217  * Command for printing the current directory.
00218  */
00219 static
00220 int
00221 cmd_pwd(int nargs, char **args)
00222 {
00223         char buf[PATH_MAX+1];
00224         int result;
00225         struct iovec iov;
00226         struct uio ku;
00227 
00228         (void)nargs;
00229         (void)args;
00230 
00231         uio_kinit(&iov, &ku, buf, sizeof(buf)-1, 0, UIO_READ);
00232         result = vfs_getcwd(&ku);
00233         if (result) {
00234                 kprintf("vfs_getcwd failed (%s)\n", strerror(result));
00235                 return result;
00236         }
00237 
00238         /* null terminate */
00239         buf[sizeof(buf)-1-ku.uio_resid] = 0;
00240 
00241         /* print it */
00242         kprintf("%s\n", buf);
00243 
00244         return 0;
00245 }
00246 
00247 /*
00248  * Command for running sync.
00249  */
00250 static
00251 int
00252 cmd_sync(int nargs, char **args)
00253 {
00254         (void)nargs;
00255         (void)args;
00256 
00257         vfs_sync();
00258 
00259         return 0;
00260 }
00261 
00262 /*
00263  * Command for doing an intentional panic.
00264  */
00265 static
00266 int
00267 cmd_panic(int nargs, char **args)
00268 {
00269         (void)nargs;
00270         (void)args;
00271 
00272         panic("User requested panic\n");
00273         return 0;
00274 }
00275 
00276 /*
00277  * Command for shutting down.
00278  */
00279 static
00280 int
00281 cmd_quit(int nargs, char **args)
00282 {
00283         (void)nargs;
00284         (void)args;
00285 
00286         vfs_sync();
00287         sys_reboot(RB_POWEROFF);
00288         thread_exit();
00289         return 0;
00290 }
00291 
00292 /*
00293  * Command for mounting a filesystem.
00294  */
00295 
00296 /* Table of mountable filesystem types. */
00297 static const struct {
00298         const char *name;
00299         int (*func)(const char *device);
00300 } mounttable[] = {
00301 #if OPT_SFS
00302         { "sfs", sfs_mount },
00303 #endif
00304         { NULL, NULL }
00305 };
00306 
00307 static
00308 int
00309 cmd_mount(int nargs, char **args)
00310 {
00311         char *fstype;
00312         char *device;
00313         int i;
00314 
00315         if (nargs != 3) {
00316                 kprintf("Usage: mount fstype device:\n");
00317                 return EINVAL;
00318         }
00319 
00320         fstype = args[1];
00321         device = args[2];
00322 
00323         /* Allow (but do not require) colon after device name */
00324         if (device[strlen(device)-1]==':') {
00325                 device[strlen(device)-1] = 0;
00326         }
00327 
00328         for (i=0; mounttable[i].name; i++) {
00329                 if (!strcmp(mounttable[i].name, fstype)) {
00330                         return mounttable[i].func(device);
00331                 }
00332         }
00333         kprintf("Unknown filesystem type %s\n", fstype);
00334         return EINVAL;
00335 }
00336 
00337 static
00338 int
00339 cmd_unmount(int nargs, char **args)
00340 {
00341         char *device;
00342 
00343         if (nargs != 2) {
00344                 kprintf("Usage: unmount device:\n");
00345                 return EINVAL;
00346         }
00347 
00348         device = args[1];
00349 
00350         /* Allow (but do not require) colon after device name */
00351         if (device[strlen(device)-1]==':') {
00352                 device[strlen(device)-1] = 0;
00353         }
00354 
00355         return vfs_unmount(device);
00356 }
00357 
00358 /*
00359  * Command to set the "boot fs". 
00360  *
00361  * The boot filesystem is the one that pathnames like /bin/sh with
00362  * leading slashes refer to.
00363  *
00364  * The default bootfs is "emu0".
00365  */
00366 static
00367 int
00368 cmd_bootfs(int nargs, char **args)
00369 {
00370         char *device;
00371 
00372         if (nargs != 2) {
00373                 kprintf("Usage: bootfs device\n");
00374                 return EINVAL;
00375         }
00376 
00377         device = args[1];
00378 
00379         /* Allow (but do not require) colon after device name */
00380         if (device[strlen(device)-1]==':') {
00381                 device[strlen(device)-1] = 0;
00382         }
00383 
00384         return vfs_setbootfs(device);
00385 }
00386 
00387 static
00388 int
00389 cmd_kheapstats(int nargs, char **args)
00390 {
00391         (void)nargs;
00392         (void)args;
00393 
00394         kheap_printstats();
00395         
00396         return 0;
00397 }
00398 
00399 ////////////////////////////////////////
00400 //
00401 // Menus.
00402 
00403 static
00404 void
00405 showmenu(const char *name, const char *x[])
00406 {
00407         int ct, half, i;
00408 
00409         kprintf("\n");
00410         kprintf("%s\n", name);
00411         
00412         for (i=ct=0; x[i]; i++) {
00413                 ct++;
00414         }
00415         half = (ct+1)/2;
00416 
00417         for (i=0; i<half; i++) {
00418                 kprintf("    %-36s", x[i]);
00419                 if (i+half < ct) {
00420                         kprintf("%s", x[i+half]);
00421                 }
00422                 kprintf("\n");
00423         }
00424 
00425         kprintf("\n");
00426 }
00427 
00428 static const char *opsmenu[] = {
00429         "[s]       Shell                     ",
00430         "[p]       Other program             ",
00431         "[mount]   Mount a filesystem        ",
00432         "[unmount] Unmount a filesystem      ",
00433         "[bootfs]  Set \"boot\" filesystem     ",
00434         "[pf]      Print a file              ",
00435         "[cd]      Change directory          ",
00436         "[pwd]     Print current directory   ",
00437         "[sync]    Sync filesystems          ",
00438         "[panic]   Intentional panic         ",
00439         "[q]       Quit and shut down        ",
00440         NULL
00441 };
00442 
00443 static
00444 int
00445 cmd_opsmenu(int n, char **a)
00446 {
00447         (void)n;
00448         (void)a;
00449 
00450         showmenu("OS/161 operations menu", opsmenu);
00451         return 0;
00452 }
00453 
00454 static const char *testmenu[] = {
00455         "[at]  Array test                    ",
00456         "[bt]  Bitmap test                   ",
00457         "[km1] Kernel malloc test            ",
00458         "[km2] kmalloc stress test           ",
00459         "[tt1] Thread test 1                 ",
00460         "[tt2] Thread test 2                 ",
00461         "[tt3] Thread test 3                 ",
00462 #if OPT_NET
00463         "[net] Network test                  ",
00464 #endif
00465         "[sy1] Semaphore test                ",
00466         "[sy2] Lock test             (1)     ",
00467         "[sy3] CV test               (1)     ",
00468 #ifdef UW
00469         "[uw1] UW lock test          (1)     ",
00470         "[uw2] UW vmstats test       (3)     ",
00471 #endif // UW
00472         "[fs1] Filesystem test               ",
00473         "[fs2] FS read stress        (4)     ",
00474         "[fs3] FS write stress       (4)     ",
00475         "[fs4] FS write stress 2     (4)     ",
00476         "[fs5] FS create stress      (4)     ",
00477         NULL
00478 };
00479 
00480 static
00481 int
00482 cmd_testmenu(int n, char **a)
00483 {
00484         (void)n;
00485         (void)a;
00486 
00487         showmenu("OS/161 tests menu", testmenu);
00488         kprintf("    (1) These tests will fail until you finish the "
00489                 "synch assignment.\n");
00490         kprintf("    (4) These tests may fail until you finish the "
00491                 "file system assignment.\n");
00492         kprintf("\n");
00493 
00494         return 0;
00495 }
00496 
00497 static const char *mainmenu[] = {
00498         "[?o] Operations menu                ",
00499         "[?t] Tests menu                     ",
00500 #if OPT_SYNCHPROBS
00501         "[sp1] Whale Mating                  ",
00502 #ifdef UW
00503         "[sp2] Cat/mouse                     ",
00504 #endif /* UW */
00505 #endif
00506         "[kh] Kernel heap stats              ",
00507         "[q] Quit and shut down              ",
00508         NULL
00509 };
00510 
00511 static
00512 int
00513 cmd_mainmenu(int n, char **a)
00514 {
00515         (void)n;
00516         (void)a;
00517 
00518         showmenu("OS/161 kernel menu", mainmenu);
00519         return 0;
00520 }
00521 
00522 ////////////////////////////////////////
00523 //
00524 // Command table.
00525 
00526 static struct {
00527         const char *name;
00528         int (*func)(int nargs, char **args);
00529 } cmdtable[] = {
00530         /* menus */
00531         { "?",          cmd_mainmenu },
00532         { "h",          cmd_mainmenu },
00533         { "help",       cmd_mainmenu },
00534         { "?o",         cmd_opsmenu },
00535         { "?t",         cmd_testmenu },
00536 
00537         /* operations */
00538         { "s",          cmd_shell },
00539         { "p",          cmd_prog },
00540         { "mount",      cmd_mount },
00541         { "unmount",    cmd_unmount },
00542         { "bootfs",     cmd_bootfs },
00543         { "pf",         printfile },
00544         { "cd",         cmd_chdir },
00545         { "pwd",        cmd_pwd },
00546         { "sync",       cmd_sync },
00547         { "panic",      cmd_panic },
00548         { "q",          cmd_quit },
00549         { "exit",       cmd_quit },
00550         { "halt",       cmd_quit },
00551 
00552 #if OPT_SYNCHPROBS
00553         /* in-kernel synchronization problem(s) */
00554         { "sp1",        whalemating },
00555 #ifdef UW
00556         { "sp2",        catmouse },
00557 #endif /* UW */
00558 #endif
00559 
00560         /* stats */
00561         { "kh",         cmd_kheapstats },
00562 
00563         /* base system tests */
00564         { "at",         arraytest },
00565         { "bt",         bitmaptest },
00566         { "km1",        malloctest },
00567         { "km2",        mallocstress },
00568 #if OPT_NET
00569         { "net",        nettest },
00570 #endif
00571         { "tt1",        threadtest },
00572         { "tt2",        threadtest2 },
00573         { "tt3",        threadtest3 },
00574         { "sy1",        semtest },
00575 
00576         /* synchronization assignment tests */
00577         { "sy2",        locktest },
00578         { "sy3",        cvtest },
00579 #ifdef UW
00580         { "uw1",        uwlocktest1 },
00581         { "uw2",        uwvmstatstest },
00582 #endif
00583 
00584         /* file system assignment tests */
00585         { "fs1",        fstest },
00586         { "fs2",        readstress },
00587         { "fs3",        writestress },
00588         { "fs4",        writestress2 },
00589         { "fs5",        createstress },
00590 
00591         { NULL, NULL }
00592 };
00593 
00594 /*
00595  * Process a single command.
00596  */
00597 static
00598 int
00599 cmd_dispatch(char *cmd)
00600 {
00601         time_t beforesecs, aftersecs, secs;
00602         uint32_t beforensecs, afternsecs, nsecs;
00603         char *args[MAXMENUARGS];
00604         int nargs=0;
00605         char *word;
00606         char *context;
00607         int i, result;
00608 
00609         for (word = strtok_r(cmd, " \t", &context);
00610              word != NULL;
00611              word = strtok_r(NULL, " \t", &context)) {
00612 
00613                 if (nargs >= MAXMENUARGS) {
00614                         kprintf("Command line has too many words\n");
00615                         return E2BIG;
00616                 }
00617                 args[nargs++] = word;
00618         }
00619 
00620         if (nargs==0) {
00621                 return 0;
00622         }
00623 
00624         for (i=0; cmdtable[i].name; i++) {
00625                 if (*cmdtable[i].name && !strcmp(args[0], cmdtable[i].name)) {
00626                         KASSERT(cmdtable[i].func!=NULL);
00627 
00628                         gettime(&beforesecs, &beforensecs);
00629 
00630                         result = cmdtable[i].func(nargs, args);
00631 
00632                         gettime(&aftersecs, &afternsecs);
00633                         getinterval(beforesecs, beforensecs,
00634                                     aftersecs, afternsecs,
00635                                     &secs, &nsecs);
00636 
00637                         kprintf("Operation took %lu.%09lu seconds\n",
00638                                 (unsigned long) secs,
00639                                 (unsigned long) nsecs);
00640 
00641                         return result;
00642                 }
00643         }
00644 
00645         kprintf("%s: Command not found\n", args[0]);
00646         return EINVAL;
00647 }
00648 
00649 /*
00650  * Evaluate a command line that may contain multiple semicolon-delimited
00651  * commands.
00652  *
00653  * If "isargs" is set, we're doing command-line processing; print the
00654  * comamnds as we execute them and panic if the command is invalid or fails.
00655  */
00656 static
00657 void
00658 menu_execute(char *line, int isargs)
00659 {
00660         char *command;
00661         char *context;
00662         int result;
00663 
00664         for (command = strtok_r(line, ";", &context);
00665              command != NULL;
00666              command = strtok_r(NULL, ";", &context)) {
00667 
00668                 if (isargs) {
00669                         kprintf("OS/161 kernel: %s\n", command);
00670                 }
00671 
00672                 result = cmd_dispatch(command);
00673                 if (result) {
00674                         kprintf("Menu command failed: %s\n", strerror(result));
00675                         if (isargs) {
00676                                 panic("Failure processing kernel arguments\n");
00677                         }
00678                 }
00679         }
00680 }
00681 
00682 /*
00683  * Command menu main loop.
00684  *
00685  * First, handle arguments passed on the kernel's command line from
00686  * the bootloader. Then loop prompting for commands.
00687  *
00688  * The line passed in from the bootloader is treated as if it had been
00689  * typed at the prompt. Semicolons separate commands; spaces and tabs
00690  * separate words (command names and arguments).
00691  *
00692  * So, for instance, to mount an SFS on lhd0 and make it the boot
00693  * filesystem, and then boot directly into the shell, one would use
00694  * the kernel command line
00695  *
00696  *      "mount sfs lhd0; bootfs lhd0; s"
00697  */
00698 
00699 void
00700 menu(char *args)
00701 {
00702         char buf[64];
00703 
00704         menu_execute(args, 1);
00705 
00706         while (1) {
00707                 kprintf("OS/161 kernel [? for menu]: ");
00708                 kgets(buf, sizeof(buf));
00709                 menu_execute(buf, 0);
00710         }
00711 }
 All Data Structures