/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- getinterval
- cmd_progthread
- common_prog
- cmd_prog
- cmd_shell
- cmd_chdir
- cmd_pwd
- cmd_sync
- cmd_panic
- cmd_quit
- cmd_mount
- cmd_unmount
- cmd_bootfs
- cmd_kheapstats
- showmenu
- cmd_opsmenu
- cmd_testmenu
- cmd_mainmenu
- cmd_dispatch
- menu_execute
- 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 }