root/kern/startup/menu.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. getinterval
  2. cmd_progthread
  3. common_prog
  4. cmd_prog
  5. cmd_shell
  6. cmd_chdir
  7. cmd_pwd
  8. cmd_sync
  9. cmd_panic
  10. cmd_quit
  11. cmd_mount
  12. cmd_unmount
  13. cmd_bootfs
  14. cmd_kheapstats
  15. showmenu
  16. cmd_opsmenu
  17. cmd_testmenu
  18. cmd_mainmenu
  19. cmd_dispatch
  20. menu_execute
  21. menu

   1 /*
   2  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
   3  *      The President and Fellows of Harvard College.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the University nor the names of its contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 #include <types.h>
  31 #include <kern/errno.h>
  32 #include <kern/reboot.h>
  33 #include <kern/unistd.h>
  34 #include <limits.h>
  35 #include <lib.h>
  36 #include <uio.h>
  37 #include <clock.h>
  38 #include <thread.h>
  39 #include <proc.h>
  40 #include <synch.h>
  41 #include <vfs.h>
  42 #include <sfs.h>
  43 #include <syscall.h>
  44 #include <test.h>
  45 #include "opt-synchprobs.h"
  46 #include "opt-sfs.h"
  47 #include "opt-net.h"
  48 
  49 /*
  50  * In-kernel menu and command dispatcher.
  51  */
  52 
  53 #define _PATH_SHELL "/bin/sh"
  54 
  55 #define MAXMENUARGS  16
  56 
  57 // XXX this should not be in this file
  58 void
  59 getinterval(time_t s1, uint32_t ns1, time_t s2, uint32_t ns2,
  60             time_t *rs, uint32_t *rns)
  61 {
  62         if (ns2 < ns1) {
  63                 ns2 += 1000000000;
  64                 s2--;
  65         }
  66 
  67         *rns = ns2 - ns1;
  68         *rs = s2 - s1;
  69 }
  70 
  71 ////////////////////////////////////////////////////////////
  72 //
  73 // Command menu functions 
  74 
  75 /*
  76  * Function for a thread that runs an arbitrary userlevel program by
  77  * name.
  78  *
  79  * Note: this cannot pass arguments to the program. You may wish to 
  80  * change it so it can, because that will make testing much easier
  81  * in the future.
  82  *
  83  * It copies the program name because runprogram destroys the copy
  84  * it gets by passing it to vfs_open(). 
  85  */
  86 static
  87 void
  88 cmd_progthread(void *ptr, unsigned long nargs)
  89 {
  90         char **args = ptr;
  91         char progname[128];
  92         int result;
  93 
  94         KASSERT(nargs >= 1);
  95 
  96         if (nargs > 2) {
  97                 kprintf("Warning: argument passing from menu not supported\n");
  98         }
  99 
 100         /* Hope we fit. */
 101         KASSERT(strlen(args[0]) < sizeof(progname));
 102 
 103         strcpy(progname, args[0]);
 104 
 105         result = runprogram(progname);
 106         if (result) {
 107                 kprintf("Running program %s failed: %s\n", args[0],
 108                         strerror(result));
 109                 return;
 110         }
 111 
 112         /* NOTREACHED: runprogram only returns on error. */
 113 }
 114 
 115 /*
 116  * Common code for cmd_prog and cmd_shell.
 117  *
 118  * Note that this does not wait for the subprogram to finish, but
 119  * returns immediately to the menu. This is usually not what you want,
 120  * so you should have it call your system-calls-assignment waitpid
 121  * code after forking.
 122  *
 123  * Also note that because the subprogram's thread uses the "args"
 124  * array and strings, until you do this a race condition exists
 125  * between that code and the menu input code.
 126  */
 127 static
 128 int
 129 common_prog(int nargs, char **args)
 130 {
 131         struct proc *proc;
 132         int result;
 133 
 134 #if OPT_SYNCHPROBS
 135         kprintf("Warning: this probably won't work with a "
 136                 "synchronization-problems kernel.\n");
 137 #endif
 138 
 139         /* Create a process for the new program to run in. */
 140         proc = proc_create_runprogram(args[0] /* name */);
 141         if (proc == NULL) {
 142                 return ENOMEM;
 143         }
 144 
 145         result = thread_fork(args[0] /* thread name */,
 146                         proc /* new process */,
 147                         cmd_progthread /* thread function */,
 148                         args /* thread arg */, nargs /* thread arg */);
 149         if (result) {
 150                 kprintf("thread_fork failed: %s\n", strerror(result));
 151                 proc_destroy(proc);
 152                 return result;
 153         }
 154 
 155 #ifdef UW
 156         /* wait until the process we have just launched - and any others that it 
 157            may fork - is finished before proceeding */
 158         P(no_proc_sem);
 159 #endif // UW
 160 
 161         return 0;
 162 }
 163 
 164 /*
 165  * Command for running an arbitrary userlevel program.
 166  */
 167 static
 168 int
 169 cmd_prog(int nargs, char **args)
 170 {
 171         if (nargs < 2) {
 172                 kprintf("Usage: p program [arguments]\n");
 173                 return EINVAL;
 174         }
 175 
 176         /* drop the leading "p" */
 177         args++;
 178         nargs--;
 179 
 180         return common_prog(nargs, args);
 181 }
 182 
 183 /*
 184  * Command for starting the system shell.
 185  */
 186 static
 187 int
 188 cmd_shell(int nargs, char **args)
 189 {
 190         (void)args;
 191         if (nargs != 1) {
 192                 kprintf("Usage: s\n");
 193                 return EINVAL;
 194         }
 195 
 196         args[0] = (char *)_PATH_SHELL;
 197 
 198         return common_prog(nargs, args);
 199 }
 200 
 201 /*
 202  * Command for changing directory.
 203  */
 204 static
 205 int
 206 cmd_chdir(int nargs, char **args)
 207 {
 208         if (nargs != 2) {
 209                 kprintf("Usage: cd directory\n");
 210                 return EINVAL;
 211         }
 212 
 213         return vfs_chdir(args[1]);
 214 }
 215 
 216 /*
 217  * Command for printing the current directory.
 218  */
 219 static
 220 int
 221 cmd_pwd(int nargs, char **args)
 222 {
 223         char buf[PATH_MAX+1];
 224         int result;
 225         struct iovec iov;
 226         struct uio ku;
 227 
 228         (void)nargs;
 229         (void)args;
 230 
 231         uio_kinit(&iov, &ku, buf, sizeof(buf)-1, 0, UIO_READ);
 232         result = vfs_getcwd(&ku);
 233         if (result) {
 234                 kprintf("vfs_getcwd failed (%s)\n", strerror(result));
 235                 return result;
 236         }
 237 
 238         /* null terminate */
 239         buf[sizeof(buf)-1-ku.uio_resid] = 0;
 240 
 241         /* print it */
 242         kprintf("%s\n", buf);
 243 
 244         return 0;
 245 }
 246 
 247 /*
 248  * Command for running sync.
 249  */
 250 static
 251 int
 252 cmd_sync(int nargs, char **args)
 253 {
 254         (void)nargs;
 255         (void)args;
 256 
 257         vfs_sync();
 258 
 259         return 0;
 260 }
 261 
 262 /*
 263  * Command for doing an intentional panic.
 264  */
 265 static
 266 int
 267 cmd_panic(int nargs, char **args)
 268 {
 269         (void)nargs;
 270         (void)args;
 271 
 272         panic("User requested panic\n");
 273         return 0;
 274 }
 275 
 276 /*
 277  * Command for shutting down.
 278  */
 279 static
 280 int
 281 cmd_quit(int nargs, char **args)
 282 {
 283         (void)nargs;
 284         (void)args;
 285 
 286         vfs_sync();
 287         sys_reboot(RB_POWEROFF);
 288         thread_exit();
 289         return 0;
 290 }
 291 
 292 /*
 293  * Command for mounting a filesystem.
 294  */
 295 
 296 /* Table of mountable filesystem types. */
 297 static const struct {
 298         const char *name;
 299         int (*func)(const char *device);
 300 } mounttable[] = {
 301 #if OPT_SFS
 302         { "sfs", sfs_mount },
 303 #endif
 304         { NULL, NULL }
 305 };
 306 
 307 static
 308 int
 309 cmd_mount(int nargs, char **args)
 310 {
 311         char *fstype;
 312         char *device;
 313         int i;
 314 
 315         if (nargs != 3) {
 316                 kprintf("Usage: mount fstype device:\n");
 317                 return EINVAL;
 318         }
 319 
 320         fstype = args[1];
 321         device = args[2];
 322 
 323         /* Allow (but do not require) colon after device name */
 324         if (device[strlen(device)-1]==':') {
 325                 device[strlen(device)-1] = 0;
 326         }
 327 
 328         for (i=0; mounttable[i].name; i++) {
 329                 if (!strcmp(mounttable[i].name, fstype)) {
 330                         return mounttable[i].func(device);
 331                 }
 332         }
 333         kprintf("Unknown filesystem type %s\n", fstype);
 334         return EINVAL;
 335 }
 336 
 337 static
 338 int
 339 cmd_unmount(int nargs, char **args)
 340 {
 341         char *device;
 342 
 343         if (nargs != 2) {
 344                 kprintf("Usage: unmount device:\n");
 345                 return EINVAL;
 346         }
 347 
 348         device = args[1];
 349 
 350         /* Allow (but do not require) colon after device name */
 351         if (device[strlen(device)-1]==':') {
 352                 device[strlen(device)-1] = 0;
 353         }
 354 
 355         return vfs_unmount(device);
 356 }
 357 
 358 /*
 359  * Command to set the "boot fs". 
 360  *
 361  * The boot filesystem is the one that pathnames like /bin/sh with
 362  * leading slashes refer to.
 363  *
 364  * The default bootfs is "emu0".
 365  */
 366 static
 367 int
 368 cmd_bootfs(int nargs, char **args)
 369 {
 370         char *device;
 371 
 372         if (nargs != 2) {
 373                 kprintf("Usage: bootfs device\n");
 374                 return EINVAL;
 375         }
 376 
 377         device = args[1];
 378 
 379         /* Allow (but do not require) colon after device name */
 380         if (device[strlen(device)-1]==':') {
 381                 device[strlen(device)-1] = 0;
 382         }
 383 
 384         return vfs_setbootfs(device);
 385 }
 386 
 387 static
 388 int
 389 cmd_kheapstats(int nargs, char **args)
 390 {
 391         (void)nargs;
 392         (void)args;
 393 
 394         kheap_printstats();
 395         
 396         return 0;
 397 }
 398 
 399 ////////////////////////////////////////
 400 //
 401 // Menus.
 402 
 403 static
 404 void
 405 showmenu(const char *name, const char *x[])
 406 {
 407         int ct, half, i;
 408 
 409         kprintf("\n");
 410         kprintf("%s\n", name);
 411         
 412         for (i=ct=0; x[i]; i++) {
 413                 ct++;
 414         }
 415         half = (ct+1)/2;
 416 
 417         for (i=0; i<half; i++) {
 418                 kprintf("    %-36s", x[i]);
 419                 if (i+half < ct) {
 420                         kprintf("%s", x[i+half]);
 421                 }
 422                 kprintf("\n");
 423         }
 424 
 425         kprintf("\n");
 426 }
 427 
 428 static const char *opsmenu[] = {
 429         "[s]       Shell                     ",
 430         "[p]       Other program             ",
 431         "[mount]   Mount a filesystem        ",
 432         "[unmount] Unmount a filesystem      ",
 433         "[bootfs]  Set \"boot\" filesystem     ",
 434         "[pf]      Print a file              ",
 435         "[cd]      Change directory          ",
 436         "[pwd]     Print current directory   ",
 437         "[sync]    Sync filesystems          ",
 438         "[panic]   Intentional panic         ",
 439         "[q]       Quit and shut down        ",
 440         NULL
 441 };
 442 
 443 static
 444 int
 445 cmd_opsmenu(int n, char **a)
 446 {
 447         (void)n;
 448         (void)a;
 449 
 450         showmenu("OS/161 operations menu", opsmenu);
 451         return 0;
 452 }
 453 
 454 static const char *testmenu[] = {
 455         "[at]  Array test                    ",
 456         "[bt]  Bitmap test                   ",
 457         "[km1] Kernel malloc test            ",
 458         "[km2] kmalloc stress test           ",
 459         "[tt1] Thread test 1                 ",
 460         "[tt2] Thread test 2                 ",
 461         "[tt3] Thread test 3                 ",
 462 #if OPT_NET
 463         "[net] Network test                  ",
 464 #endif
 465         "[sy1] Semaphore test                ",
 466         "[sy2] Lock test             (1)     ",
 467         "[sy3] CV test               (1)     ",
 468 #ifdef UW
 469         "[uw1] UW lock test          (1)     ",
 470         "[uw2] UW vmstats test       (3)     ",
 471 #endif // UW
 472         "[fs1] Filesystem test               ",
 473         "[fs2] FS read stress        (4)     ",
 474         "[fs3] FS write stress       (4)     ",
 475         "[fs4] FS write stress 2     (4)     ",
 476         "[fs5] FS create stress      (4)     ",
 477         NULL
 478 };
 479 
 480 static
 481 int
 482 cmd_testmenu(int n, char **a)
 483 {
 484         (void)n;
 485         (void)a;
 486 
 487         showmenu("OS/161 tests menu", testmenu);
 488         kprintf("    (1) These tests will fail until you finish the "
 489                 "synch assignment.\n");
 490         kprintf("    (4) These tests may fail until you finish the "
 491                 "file system assignment.\n");
 492         kprintf("\n");
 493 
 494         return 0;
 495 }
 496 
 497 static const char *mainmenu[] = {
 498         "[?o] Operations menu                ",
 499         "[?t] Tests menu                     ",
 500 #if OPT_SYNCHPROBS
 501         "[sp1] Whale Mating                  ",
 502 #ifdef UW
 503         "[sp2] Cat/mouse                     ",
 504 #endif /* UW */
 505 #endif
 506         "[kh] Kernel heap stats              ",
 507         "[q] Quit and shut down              ",
 508         NULL
 509 };
 510 
 511 static
 512 int
 513 cmd_mainmenu(int n, char **a)
 514 {
 515         (void)n;
 516         (void)a;
 517 
 518         showmenu("OS/161 kernel menu", mainmenu);
 519         return 0;
 520 }
 521 
 522 ////////////////////////////////////////
 523 //
 524 // Command table.
 525 
 526 static struct {
 527         const char *name;
 528         int (*func)(int nargs, char **args);
 529 } cmdtable[] = {
 530         /* menus */
 531         { "?",          cmd_mainmenu },
 532         { "h",          cmd_mainmenu },
 533         { "help",       cmd_mainmenu },
 534         { "?o",         cmd_opsmenu },
 535         { "?t",         cmd_testmenu },
 536 
 537         /* operations */
 538         { "s",          cmd_shell },
 539         { "p",          cmd_prog },
 540         { "mount",      cmd_mount },
 541         { "unmount",    cmd_unmount },
 542         { "bootfs",     cmd_bootfs },
 543         { "pf",         printfile },
 544         { "cd",         cmd_chdir },
 545         { "pwd",        cmd_pwd },
 546         { "sync",       cmd_sync },
 547         { "panic",      cmd_panic },
 548         { "q",          cmd_quit },
 549         { "exit",       cmd_quit },
 550         { "halt",       cmd_quit },
 551 
 552 #if OPT_SYNCHPROBS
 553         /* in-kernel synchronization problem(s) */
 554         { "sp1",        whalemating },
 555 #ifdef UW
 556         { "sp2",        catmouse },
 557 #endif /* UW */
 558 #endif
 559 
 560         /* stats */
 561         { "kh",         cmd_kheapstats },
 562 
 563         /* base system tests */
 564         { "at",         arraytest },
 565         { "bt",         bitmaptest },
 566         { "km1",        malloctest },
 567         { "km2",        mallocstress },
 568 #if OPT_NET
 569         { "net",        nettest },
 570 #endif
 571         { "tt1",        threadtest },
 572         { "tt2",        threadtest2 },
 573         { "tt3",        threadtest3 },
 574         { "sy1",        semtest },
 575 
 576         /* synchronization assignment tests */
 577         { "sy2",        locktest },
 578         { "sy3",        cvtest },
 579 #ifdef UW
 580         { "uw1",        uwlocktest1 },
 581         { "uw2",        uwvmstatstest },
 582 #endif
 583 
 584         /* file system assignment tests */
 585         { "fs1",        fstest },
 586         { "fs2",        readstress },
 587         { "fs3",        writestress },
 588         { "fs4",        writestress2 },
 589         { "fs5",        createstress },
 590 
 591         { NULL, NULL }
 592 };
 593 
 594 /*
 595  * Process a single command.
 596  */
 597 static
 598 int
 599 cmd_dispatch(char *cmd)
 600 {
 601         time_t beforesecs, aftersecs, secs;
 602         uint32_t beforensecs, afternsecs, nsecs;
 603         char *args[MAXMENUARGS];
 604         int nargs=0;
 605         char *word;
 606         char *context;
 607         int i, result;
 608 
 609         for (word = strtok_r(cmd, " \t", &context);
 610              word != NULL;
 611              word = strtok_r(NULL, " \t", &context)) {
 612 
 613                 if (nargs >= MAXMENUARGS) {
 614                         kprintf("Command line has too many words\n");
 615                         return E2BIG;
 616                 }
 617                 args[nargs++] = word;
 618         }
 619 
 620         if (nargs==0) {
 621                 return 0;
 622         }
 623 
 624         for (i=0; cmdtable[i].name; i++) {
 625                 if (*cmdtable[i].name && !strcmp(args[0], cmdtable[i].name)) {
 626                         KASSERT(cmdtable[i].func!=NULL);
 627 
 628                         gettime(&beforesecs, &beforensecs);
 629 
 630                         result = cmdtable[i].func(nargs, args);
 631 
 632                         gettime(&aftersecs, &afternsecs);
 633                         getinterval(beforesecs, beforensecs,
 634                                     aftersecs, afternsecs,
 635                                     &secs, &nsecs);
 636 
 637                         kprintf("Operation took %lu.%09lu seconds\n",
 638                                 (unsigned long) secs,
 639                                 (unsigned long) nsecs);
 640 
 641                         return result;
 642                 }
 643         }
 644 
 645         kprintf("%s: Command not found\n", args[0]);
 646         return EINVAL;
 647 }
 648 
 649 /*
 650  * Evaluate a command line that may contain multiple semicolon-delimited
 651  * commands.
 652  *
 653  * If "isargs" is set, we're doing command-line processing; print the
 654  * comamnds as we execute them and panic if the command is invalid or fails.
 655  */
 656 static
 657 void
 658 menu_execute(char *line, int isargs)
 659 {
 660         char *command;
 661         char *context;
 662         int result;
 663 
 664         for (command = strtok_r(line, ";", &context);
 665              command != NULL;
 666              command = strtok_r(NULL, ";", &context)) {
 667 
 668                 if (isargs) {
 669                         kprintf("OS/161 kernel: %s\n", command);
 670                 }
 671 
 672                 result = cmd_dispatch(command);
 673                 if (result) {
 674                         kprintf("Menu command failed: %s\n", strerror(result));
 675                         if (isargs) {
 676                                 panic("Failure processing kernel arguments\n");
 677                         }
 678                 }
 679         }
 680 }
 681 
 682 /*
 683  * Command menu main loop.
 684  *
 685  * First, handle arguments passed on the kernel's command line from
 686  * the bootloader. Then loop prompting for commands.
 687  *
 688  * The line passed in from the bootloader is treated as if it had been
 689  * typed at the prompt. Semicolons separate commands; spaces and tabs
 690  * separate words (command names and arguments).
 691  *
 692  * So, for instance, to mount an SFS on lhd0 and make it the boot
 693  * filesystem, and then boot directly into the shell, one would use
 694  * the kernel command line
 695  *
 696  *      "mount sfs lhd0; bootfs lhd0; s"
 697  */
 698 
 699 void
 700 menu(char *args)
 701 {
 702         char buf[64];
 703 
 704         menu_execute(args, 1);
 705 
 706         while (1) {
 707                 kprintf("OS/161 kernel [? for menu]: ");
 708                 kgets(buf, sizeof(buf));
 709                 menu_execute(buf, 0);
 710         }
 711 }

/* [<][>][^][v][top][bottom][index][help] */