/* * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009 * The President and Fellows of Harvard College. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt-sfs.h" #include "opt-net.h" #include /* * In-kernel menu and command dispatcher. */ #define _PATH_SHELL "/bin/sh" #define MAXMENUARGS 16 //////////////////////////////////////////////////////////// // // Command menu functions /* * Function for a thread that runs an arbitrary userlevel program by * name. * * Note: this cannot pass arguments to the program. You may wish to * change it so it can, because that will make testing much easier * in the future. * * It copies the program name because runprogram destroys the copy * it gets by passing it to vfs_open(). */ static void cmd_progthread(void *ptr, unsigned long nargs) { char **args = ptr; char progname[128]; int result; KASSERT(nargs >= 1); if (nargs > 2) { kprintf("Warning: argument passing from menu not supported\n"); } /* Hope we fit. */ KASSERT(strlen(args[0]) < sizeof(progname)); strcpy(progname, args[0]); result = runprogram(progname); if (result) { kprintf("Running program %s failed: %s\n", args[0], strerror(result)); return; } /* NOTREACHED: runprogram only returns on error. */ } /* * Common code for cmd_prog and cmd_shell. * * Note that this does not wait for the subprogram to finish, but * returns immediately to the menu. This is usually not what you want, * so you should have it call your system-calls-assignment waitpid * code after forking. * * Also note that because the subprogram's thread uses the "args" * array and strings, until you do this a race condition exists * between that code and the menu input code. */ static int common_prog(int nargs, char **args) { struct proc *proc; int result; /* Create a process for the new program to run in. */ proc = proc_create_runprogram(args[0] /* name */); if (proc == NULL) { return ENOMEM; } result = thread_fork(args[0] /* thread name */, proc /* new process */, cmd_progthread /* thread function */, args /* thread arg */, nargs /* thread arg */); if (result) { kprintf("thread_fork failed: %s\n", strerror(result)); proc_destroy(proc); return result; } int status; wait_pid(proc->pid, &status, 0); (void)status; //TODO: Interpret status /* * The new process will be destroyed when the program exits... * once you write t`he code for handling that. */ return 0; } /* * Command for running an arbitrary userlevel program. */ static int cmd_prog(int nargs, char **args) { if (nargs < 2) { kprintf("Usage: p program [arguments]\n"); return EINVAL; } /* drop the leading "p" */ args++; nargs--; return common_prog(nargs, args); } /* * Command for starting the system shell. */ 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); } /* * Command for changing directory. */ static int cmd_chdir(int nargs, char **args) { if (nargs != 2) { kprintf("Usage: cd directory\n"); return EINVAL; } return vfs_chdir(args[1]); } /* * Command for printing the current directory. */ 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; } /* null terminate */ buf[sizeof(buf)-1-ku.uio_resid] = 0; /* print it */ kprintf("%s\n", buf); return 0; } /* * Command for running sync. */ static int cmd_sync(int nargs, char **args) { (void)nargs; (void)args; vfs_sync(); return 0; } /* * Command for doing an intentional panic. */ static int cmd_panic(int nargs, char **args) { (void)nargs; (void)args; panic("User requested panic\n"); return 0; } /* * Command for shutting down. */ static int cmd_quit(int nargs, char **args) { (void)nargs; (void)args; vfs_sync(); sys_reboot(RB_POWEROFF); thread_exit(); return 0; } /* * Command for mounting a filesystem. */ /* Table of mountable filesystem types. */ static const struct { const char *name; int (*func)(const char *device); } mounttable[] = { #if OPT_SFS { "sfs", sfs_mount }, #endif }; static int cmd_mount(int nargs, char **args) { char *fstype; char *device; unsigned i; if (nargs != 3) { kprintf("Usage: mount fstype device:\n"); return EINVAL; } fstype = args[1]; device = args[2]; /* Allow (but do not require) colon after device name */ if (device[strlen(device)-1]==':') { device[strlen(device)-1] = 0; } for (i=0; i= 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(&before); result = cmdtable[i].func(nargs, args); gettime(&after); timespec_sub(&after, &before, &duration); kprintf("Operation took %llu.%09lu seconds\n", (unsigned long long) duration.tv_sec, (unsigned long) duration.tv_nsec); return result; } } kprintf("%s: Command not found\n", args[0]); return EINVAL; } /* * Evaluate a command line that may contain multiple semicolon-delimited * commands. * * If "isargs" is set, we're doing command-line processing; print the * comamnds as we execute them and panic if the command is invalid or fails. */ 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"); } } } } /* * Command menu main loop. * * First, handle arguments passed on the kernel's command line from * the bootloader. Then loop prompting for commands. * * The line passed in from the bootloader is treated as if it had been * typed at the prompt. Semicolons separate commands; spaces and tabs * separate words (command names and arguments). * * So, for instance, to mount an SFS on lhd0 and make it the boot * filesystem, and then boot directly into the shell, one would use * the kernel command line * * "mount sfs lhd0; bootfs lhd0; s" */ void menu(char *args) { char buf[64]; menu_execute(args, 1); while (1) { kprintf("OS/161 kernel [? for menu]: "); kgets(buf, sizeof(buf)); menu_execute(buf, 0); } }