Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
script3r
GitHub Repository: script3r/os161
Path: blob/master/kern/startup/menu.c
2092 views
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 <vfs.h>
40
#include <sfs.h>
41
#include <syscall.h>
42
#include <kern/fcntl.h>
43
#include <test.h>
44
#include <proc.h>
45
#include <file.h>
46
#include <current.h>
47
48
#include "opt-synchprobs.h"
49
#include "opt-sfs.h"
50
#include "opt-net.h"
51
52
struct proc *p0;
53
54
struct cmd_progthread_args {
55
char **args;
56
struct proc *p;
57
};
58
59
/*
60
* In-kernel menu and command dispatcher.
61
*/
62
63
#define _PATH_SHELL "/bin/sh"
64
65
#define MAXMENUARGS 16
66
67
// XXX this should not be in this file
68
void
69
getinterval(time_t s1, uint32_t ns1, time_t s2, uint32_t ns2,
70
time_t *rs, uint32_t *rns)
71
{
72
if (ns2 < ns1) {
73
ns2 += 1000000000;
74
s2--;
75
}
76
77
*rns = ns2 - ns1;
78
*rs = s2 - s1;
79
}
80
81
//open the standard files.
82
static
83
int
84
open_standard_files( struct proc *p ) {
85
int err = 0;
86
int retval;
87
char buf[32];
88
89
KASSERT( p != NULL );
90
91
strcpy( buf, "con:" );
92
err = ___open( p, buf, O_RDONLY, &retval );
93
if( err )
94
return err;
95
96
strcpy( buf, "con:" );
97
err = ___open( p, buf, O_WRONLY, &retval );
98
if( err ) {
99
file_close_all( p );
100
return err;
101
}
102
103
strcpy( buf, "con:" );
104
err = ___open( p, buf, O_WRONLY, &retval );
105
if( err ) {
106
file_close_all( p );
107
return err;
108
}
109
110
return 0;
111
}
112
113
114
115
////////////////////////////////////////////////////////////
116
//
117
// Command menu functions
118
119
/*
120
* Function for a thread that runs an arbitrary userlevel program by
121
* name.
122
*
123
* Note: this cannot pass arguments to the program. You may wish to
124
* change it so it can, because that will make testing much easier
125
* in the future.
126
*
127
* It copies the program name because runprogram destroys the copy
128
* it gets by passing it to vfs_open().
129
*/
130
static
131
void
132
cmd_progthread(void *ptr, unsigned long nargs)
133
{
134
struct cmd_progthread_args *cargs = ptr;
135
char **args = cargs->args;
136
char progname[128];
137
int result;
138
139
KASSERT(nargs >= 1);
140
141
if (nargs > 2) {
142
kprintf("Warning: argument passing from menu not supported\n");
143
}
144
145
/* Hope we fit. */
146
KASSERT(strlen(args[0]) < sizeof(progname));
147
148
strcpy(progname, args[0]);
149
150
//attach the process to the current thread
151
curthread->td_proc = cargs->p;
152
153
//destroy the cargs struct
154
kfree( cargs );
155
156
result = runprogram(progname);
157
if (result) {
158
kprintf("Running program %s failed: %s\n", args[0],
159
strerror(result));
160
161
//exit with a failure.
162
sys__exit( -1 );
163
return;
164
}
165
166
/* NOTREACHED: runprogram only returns on error. */
167
}
168
169
/*
170
* Common code for cmd_prog and cmd_shell.
171
*
172
* Note that this does not wait for the subprogram to finish, but
173
* returns immediately to the menu. This is usually not what you want,
174
* so you should have it call your system-calls-assignment waitpid
175
* code after forking.
176
*
177
* Also note that because the subprogram's thread uses the "args"
178
* array and strings, until you do this a race condition exists
179
* between that code and the menu input code.
180
*/
181
static
182
int
183
common_prog(int nargs, char **args)
184
{
185
int result;
186
struct proc *p = NULL;
187
struct cmd_progthread_args *cargs = NULL;
188
pid_t pid;
189
int err;
190
191
#if OPT_SYNCHPROBS
192
kprintf("Warning: this probably won't work with a "
193
"synchronization-problems kernel.\n");
194
#endif
195
196
//attempt to create the first process.
197
result = proc_create( &p );
198
if( result ) {
199
kprintf( "common_prog: failed creating the first process." );
200
return result;
201
}
202
203
//store the pid.
204
pid = p->p_pid;
205
206
//open the standard files.
207
err = open_standard_files( p );
208
if( err ) {
209
proc_destroy( p );
210
return err;
211
}
212
213
//create the arguments
214
cargs = kmalloc( sizeof( struct cmd_progthread_args ) );
215
if( cargs == NULL ) {
216
file_close_all( p );
217
proc_destroy( p );
218
return ENOMEM;
219
}
220
221
//adjust the parent to reflect p0.
222
p->p_proc = p0;
223
224
cargs->args = args;
225
cargs->p = p;
226
227
result = thread_fork(args[0] /* thread name */,
228
cmd_progthread /* thread function */,
229
cargs /* thread arg */, nargs /* thread arg */,
230
NULL);
231
if (result) {
232
kprintf("thread_fork failed: %s\n", strerror(result));
233
234
file_close_all( p );
235
proc_destroy( p );
236
return result;
237
}
238
239
//wait for our chid to die.
240
___waitpid( pid, &result, 0 );
241
242
return 0;
243
}
244
245
/*
246
* Command for running an arbitrary userlevel program.
247
*/
248
static
249
int
250
cmd_prog(int nargs, char **args)
251
{
252
if (nargs < 2) {
253
kprintf("Usage: p program [arguments]\n");
254
return EINVAL;
255
}
256
257
/* drop the leading "p" */
258
args++;
259
nargs--;
260
261
return common_prog(nargs, args);
262
}
263
264
/*
265
* Command for starting the system shell.
266
*/
267
static
268
int
269
cmd_shell(int nargs, char **args)
270
{
271
(void)args;
272
if (nargs != 1) {
273
kprintf("Usage: s\n");
274
return EINVAL;
275
}
276
277
args[0] = (char *)_PATH_SHELL;
278
279
return common_prog(nargs, args);
280
}
281
282
/*
283
* Command for changing directory.
284
*/
285
static
286
int
287
cmd_chdir(int nargs, char **args)
288
{
289
if (nargs != 2) {
290
kprintf("Usage: cd directory\n");
291
return EINVAL;
292
}
293
294
return vfs_chdir(args[1]);
295
}
296
297
/*
298
* Command for printing the current directory.
299
*/
300
static
301
int
302
cmd_pwd(int nargs, char **args)
303
{
304
char buf[PATH_MAX+1];
305
int result;
306
struct iovec iov;
307
struct uio ku;
308
309
(void)nargs;
310
(void)args;
311
312
uio_kinit(&iov, &ku, buf, sizeof(buf)-1, 0, UIO_READ);
313
result = vfs_getcwd(&ku);
314
if (result) {
315
kprintf("vfs_getcwd failed (%s)\n", strerror(result));
316
return result;
317
}
318
319
/* null terminate */
320
buf[sizeof(buf)-1-ku.uio_resid] = 0;
321
322
/* print it */
323
kprintf("%s\n", buf);
324
325
return 0;
326
}
327
328
/*
329
* Command for running sync.
330
*/
331
static
332
int
333
cmd_sync(int nargs, char **args)
334
{
335
(void)nargs;
336
(void)args;
337
338
vfs_sync();
339
340
return 0;
341
}
342
343
/*
344
* Command for doing an intentional panic.
345
*/
346
static
347
int
348
cmd_panic(int nargs, char **args)
349
{
350
(void)nargs;
351
(void)args;
352
353
panic("User requested panic\n");
354
return 0;
355
}
356
357
/*
358
* Command for shutting down.
359
*/
360
static
361
int
362
cmd_quit(int nargs, char **args)
363
{
364
(void)nargs;
365
(void)args;
366
367
vfs_sync();
368
sys_reboot(RB_POWEROFF);
369
thread_exit();
370
return 0;
371
}
372
373
/*
374
* Command for mounting a filesystem.
375
*/
376
377
/* Table of mountable filesystem types. */
378
static const struct {
379
const char *name;
380
int (*func)(const char *device);
381
} mounttable[] = {
382
#if OPT_SFS
383
{ "sfs", sfs_mount },
384
#endif
385
{ NULL, NULL }
386
};
387
388
static
389
int
390
cmd_mount(int nargs, char **args)
391
{
392
char *fstype;
393
char *device;
394
int i;
395
396
if (nargs != 3) {
397
kprintf("Usage: mount fstype device:\n");
398
return EINVAL;
399
}
400
401
fstype = args[1];
402
device = args[2];
403
404
/* Allow (but do not require) colon after device name */
405
if (device[strlen(device)-1]==':') {
406
device[strlen(device)-1] = 0;
407
}
408
409
for (i=0; mounttable[i].name; i++) {
410
if (!strcmp(mounttable[i].name, fstype)) {
411
return mounttable[i].func(device);
412
}
413
}
414
kprintf("Unknown filesystem type %s\n", fstype);
415
return EINVAL;
416
}
417
418
static
419
int
420
cmd_unmount(int nargs, char **args)
421
{
422
char *device;
423
424
if (nargs != 2) {
425
kprintf("Usage: unmount device:\n");
426
return EINVAL;
427
}
428
429
device = args[1];
430
431
/* Allow (but do not require) colon after device name */
432
if (device[strlen(device)-1]==':') {
433
device[strlen(device)-1] = 0;
434
}
435
436
return vfs_unmount(device);
437
}
438
439
/*
440
* Command to set the "boot fs".
441
*
442
* The boot filesystem is the one that pathnames like /bin/sh with
443
* leading slashes refer to.
444
*
445
* The default bootfs is "emu0".
446
*/
447
static
448
int
449
cmd_bootfs(int nargs, char **args)
450
{
451
char *device;
452
453
if (nargs != 2) {
454
kprintf("Usage: bootfs device\n");
455
return EINVAL;
456
}
457
458
device = args[1];
459
460
/* Allow (but do not require) colon after device name */
461
if (device[strlen(device)-1]==':') {
462
device[strlen(device)-1] = 0;
463
}
464
465
return vfs_setbootfs(device);
466
}
467
468
static
469
int
470
cmd_kheapstats(int nargs, char **args)
471
{
472
(void)nargs;
473
(void)args;
474
475
kheap_printstats();
476
477
return 0;
478
}
479
480
////////////////////////////////////////
481
//
482
// Menus.
483
484
static
485
void
486
showmenu(const char *name, const char *x[])
487
{
488
int ct, half, i;
489
490
kprintf("\n");
491
kprintf("%s\n", name);
492
493
for (i=ct=0; x[i]; i++) {
494
ct++;
495
}
496
half = (ct+1)/2;
497
498
for (i=0; i<half; i++) {
499
kprintf(" %-36s", x[i]);
500
if (i+half < ct) {
501
kprintf("%s", x[i+half]);
502
}
503
kprintf("\n");
504
}
505
506
kprintf("\n");
507
}
508
509
static const char *opsmenu[] = {
510
"[s] Shell ",
511
"[p] Other program ",
512
"[mount] Mount a filesystem ",
513
"[unmount] Unmount a filesystem ",
514
"[bootfs] Set \"boot\" filesystem ",
515
"[pf] Print a file ",
516
"[cd] Change directory ",
517
"[pwd] Print current directory ",
518
"[sync] Sync filesystems ",
519
"[panic] Intentional panic ",
520
"[q] Quit and shut down ",
521
NULL
522
};
523
524
static
525
int
526
cmd_opsmenu(int n, char **a)
527
{
528
(void)n;
529
(void)a;
530
531
showmenu("OS/161 operations menu", opsmenu);
532
return 0;
533
}
534
535
static const char *testmenu[] = {
536
"[at] Array test ",
537
"[bt] Bitmap test ",
538
"[km1] Kernel malloc test ",
539
"[km2] kmalloc stress test ",
540
"[tt1] Thread test 1 ",
541
"[tt2] Thread test 2 ",
542
"[tt3] Thread test 3 ",
543
#if OPT_NET
544
"[net] Network test ",
545
#endif
546
"[sy1] Semaphore test ",
547
"[sy2] Lock test (1) ",
548
"[sy3] CV test (1) ",
549
"[sy5] CV test 2 (1) ",
550
"[sp1] Whalematching Driver (1) ",
551
"[sp2] Stoplight Driver (1) ",
552
"[fs1] Filesystem test ",
553
"[fs2] FS read stress (4) ",
554
"[fs3] FS write stress (4) ",
555
"[fs4] FS write stress 2 (4) ",
556
"[fs5] FS create stress (4) ",
557
NULL
558
};
559
560
static
561
int
562
cmd_testmenu(int n, char **a)
563
{
564
(void)n;
565
(void)a;
566
567
showmenu("OS/161 tests menu", testmenu);
568
kprintf(" (1) These tests will fail until you finish the "
569
"synch assignment.\n");
570
kprintf(" (4) These tests may fail until you finish the "
571
"file system assignment.\n");
572
kprintf("\n");
573
574
return 0;
575
}
576
577
static const char *mainmenu[] = {
578
"[?o] Operations menu ",
579
"[?t] Tests menu ",
580
"[kh] Kernel heap stats ",
581
"[q] Quit and shut down ",
582
NULL
583
};
584
585
static
586
int
587
cmd_mainmenu(int n, char **a)
588
{
589
(void)n;
590
(void)a;
591
592
showmenu("OS/161 kernel menu", mainmenu);
593
return 0;
594
}
595
596
////////////////////////////////////////
597
//
598
// Command table.
599
600
static struct {
601
const char *name;
602
int (*func)(int nargs, char **args);
603
} cmdtable[] = {
604
/* menus */
605
{ "?", cmd_mainmenu },
606
{ "h", cmd_mainmenu },
607
{ "help", cmd_mainmenu },
608
{ "?o", cmd_opsmenu },
609
{ "?t", cmd_testmenu },
610
611
/* operations */
612
{ "s", cmd_shell },
613
{ "p", cmd_prog },
614
{ "mount", cmd_mount },
615
{ "unmount", cmd_unmount },
616
{ "bootfs", cmd_bootfs },
617
{ "pf", printfile },
618
{ "cd", cmd_chdir },
619
{ "pwd", cmd_pwd },
620
{ "sync", cmd_sync },
621
{ "panic", cmd_panic },
622
{ "q", cmd_quit },
623
{ "exit", cmd_quit },
624
{ "halt", cmd_quit },
625
626
627
/* stats */
628
{ "kh", cmd_kheapstats },
629
630
/* base system tests */
631
{ "at", arraytest },
632
{ "bt", bitmaptest },
633
{ "km1", malloctest },
634
{ "km2", mallocstress },
635
#if OPT_NET
636
{ "net", nettest },
637
#endif
638
{ "tt1", threadtest },
639
{ "tt2", threadtest2 },
640
{ "tt3", threadtest3 },
641
{ "sy1", semtest },
642
643
/* synchronization assignment tests */
644
{ "sy2", locktest },
645
{ "sy3", cvtest },
646
{ "sy5", cvtest2 },
647
648
#if OPT_SYNCHPROBS
649
/* synchronization problem tests */
650
{ "sp1", whalemating },
651
{ "sp2", stoplight },
652
#endif
653
654
/* file system assignment tests */
655
{ "fs1", fstest },
656
{ "fs2", readstress },
657
{ "fs3", writestress },
658
{ "fs4", writestress2 },
659
{ "fs5", createstress },
660
661
{ NULL, NULL }
662
};
663
664
/*
665
* Process a single command.
666
*/
667
static
668
int
669
cmd_dispatch(char *cmd)
670
{
671
time_t beforesecs, aftersecs, secs;
672
uint32_t beforensecs, afternsecs, nsecs;
673
char *args[MAXMENUARGS];
674
int nargs=0;
675
char *word;
676
char *context;
677
int i, result;
678
679
for (word = strtok_r(cmd, " \t", &context);
680
word != NULL;
681
word = strtok_r(NULL, " \t", &context)) {
682
683
if (nargs >= MAXMENUARGS) {
684
kprintf("Command line has too many words\n");
685
return E2BIG;
686
}
687
args[nargs++] = word;
688
}
689
690
if (nargs==0) {
691
return 0;
692
}
693
694
for (i=0; cmdtable[i].name; i++) {
695
if (*cmdtable[i].name && !strcmp(args[0], cmdtable[i].name)) {
696
KASSERT(cmdtable[i].func!=NULL);
697
698
gettime(&beforesecs, &beforensecs);
699
700
result = cmdtable[i].func(nargs, args);
701
702
gettime(&aftersecs, &afternsecs);
703
getinterval(beforesecs, beforensecs,
704
aftersecs, afternsecs,
705
&secs, &nsecs);
706
707
kprintf("Operation took %lu.%09lu seconds\n",
708
(unsigned long) secs,
709
(unsigned long) nsecs);
710
711
return result;
712
}
713
}
714
715
kprintf("%s: Command not found\n", args[0]);
716
return EINVAL;
717
}
718
719
/*
720
* Evaluate a command line that may contain multiple semicolon-delimited
721
* commands.
722
*
723
* If "isargs" is set, we're doing command-line processing; print the
724
* comamnds as we execute them and panic if the command is invalid or fails.
725
*/
726
static
727
void
728
menu_execute(char *line, int isargs)
729
{
730
char *command;
731
char *context;
732
int result;
733
734
for (command = strtok_r(line, ";", &context);
735
command != NULL;
736
command = strtok_r(NULL, ";", &context)) {
737
738
if (isargs) {
739
kprintf("OS/161 kernel: %s\n", command);
740
}
741
742
result = cmd_dispatch(command);
743
if (result) {
744
kprintf("Menu command failed: %s\n", strerror(result));
745
if (isargs) {
746
panic("Failure processing kernel arguments\n");
747
}
748
}
749
}
750
}
751
752
static
753
int
754
proc0( struct proc **p0 ) {
755
struct proc *p = NULL;
756
int err;
757
758
//create the new process.
759
err = proc_create( &p );
760
if( err )
761
return err;
762
763
//open the standard files.
764
err = open_standard_files( p );
765
if( err ) {
766
proc_destroy( p );
767
return err;
768
}
769
770
//associate it with the current thread.
771
curthread->td_proc = p;
772
773
//we are good to go.
774
*p0 = p;
775
return 0;
776
}
777
778
/*
779
* Command menu main loop.
780
*
781
* First, handle arguments passed on the kernel's command line from
782
* the bootloader. Then loop prompting for commands.
783
*
784
* The line passed in from the bootloader is treated as if it had been
785
* typed at the prompt. Semicolons separate commands; spaces and tabs
786
* separate words (command names and arguments).
787
*
788
* So, for instance, to mount an SFS on lhd0 and make it the boot
789
* filesystem, and then boot directly into the shell, one would use
790
* the kernel command line
791
*
792
* "mount sfs lhd0; bootfs lhd0; s"
793
*/
794
795
void
796
menu(char *args)
797
{
798
char buf[64];
799
int err;
800
801
proc_system_init();
802
803
804
//kickstart proc0.
805
err = proc0( &p0 );
806
if( err )
807
panic( "failed creating proc0." );
808
809
menu_execute(args, 1);
810
811
//test the proc allocation mechanism.
812
//proc_test_pid_allocation();
813
814
while (1) {
815
kprintf("OS/161 kernel [? for menu]: ");
816
kgets(buf, sizeof(buf));
817
menu_execute(buf, 0);
818
}
819
}
820
821