123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /*
- * 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.
- */
- /*
- * sh - shell
- *
- * Usage:
- * sh
- * sh -c command
- */
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <assert.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <limits.h>
- #include <errno.h>
- #include <err.h>
- #ifdef HOST
- #include "hostcompat.h"
- #endif
- #ifndef NARG_MAX
- /* no NARG_MAX on most unixes */
- #define NARG_MAX 1024
- #endif
- /* avoid making this unreasonably large; causes problems under dumbvm */
- #if ARG_MAX > 4096
- #define CMDLINE_MAX 4096
- #else
- #define CMDLINE_MAX ARG_MAX
- #endif
- /* set to nonzero if __time syscall seems to work */
- static int timing = 0;
- /* array of backgrounded jobs (allows "foregrounding") */
- #define MAXBG 128
- static pid_t bgpids[MAXBG];
- /*
- * can_bg
- * just checks for an open slot.
- */
- static
- int
- can_bg(void)
- {
- int i;
-
- for (i = 0; i < MAXBG; i++) {
- if (bgpids[i] == 0) {
- return 1;
- }
- }
-
- return 0;
- }
- /*
- * remember_bg
- * sticks the pid in an open slot in the background array. note the assert --
- * better check can_bg before calling this.
- */
- static
- void
- remember_bg(pid_t pid)
- {
- int i;
- for (i = 0; i < MAXBG; i++) {
- if (bgpids[i] == 0) {
- bgpids[i] = pid;
- return;
- }
- }
- assert(0);
- }
- /*
- * printstatus
- * print results from wait
- */
- static
- void
- printstatus(int status)
- {
- if (WIFEXITED(status)) {
- printf("Exit %d", WEXITSTATUS(status));
- }
- else if (WIFSIGNALED(status) && WCOREDUMP(status)) {
- printf("Signal %d (core dumped)", WTERMSIG(status));
- }
- else if (WIFSIGNALED(status)) {
- printf("Signal %d", WTERMSIG(status));
- }
- else if (WIFSTOPPED(status)) {
- printf("Stopped on signal %d", WSTOPSIG(status));
- }
- else {
- printf("Invalid status code %d", status);
- }
- }
- /*
- * dowait
- * just does a waitpid.
- */
- static
- void
- dowait(pid_t pid)
- {
- int status;
- if (waitpid(pid, &status, 0)<0) {
- warn("pid %d", pid);
- }
- else {
- printf("pid %d: ", pid);
- printstatus(status);
- printf("\n");
- }
- }
- #ifdef WNOHANG
- /*
- * dowaitpoll
- * like dowait, but uses WNOHANG. returns true if we got something.
- */
- static
- int
- dowaitpoll(pid_t pid)
- {
- int status;
- pid_t result;
- result = waitpid(pid, &status, WNOHANG);
- if (result<0) {
- warn("pid %d", pid);
- }
- else if (result!=0) {
- printf("pid %d: ", pid);
- printstatus(status);
- printf("\n");
- return 1;
- }
- return 0;
- }
- /*
- * waitpoll
- * poll all background jobs for having exited.
- */
- static
- void
- waitpoll(void)
- {
- int i;
- for (i=0; i < MAXBG; i++) {
- if (bgpids[i] != 0) {
- if (dowaitpoll(bgpids[i])) {
- bgpids[i] = 0;
- }
- }
- }
- }
- #endif /* WNOHANG */
- /*
- * wait
- * allows the user to "foreground" a process by waiting on it. without ps to
- * know the pids, this is a little tough to use with an arg, but without an
- * arg it will wait for all the background jobs.
- */
- static
- int
- cmd_wait(int ac, char *av[])
- {
- int i;
- pid_t pid;
- if (ac == 2) {
- pid = atoi(av[1]);
- dowait(pid);
- for (i = 0; i < MAXBG; i++) {
- if (bgpids[i]==pid) {
- bgpids[i] = 0;
- }
- }
- return 0;
- }
- else if (ac == 1) {
- for (i=0; i < MAXBG; i++) {
- if (bgpids[i] != 0) {
- dowait(bgpids[i]);
- bgpids[i] = 0;
- }
- }
- return 0;
- }
- printf("Usage: wait [pid]\n");
- return 1;
- }
- /*
- * chdir
- * just an interface to the system call. no concept of home directory, so
- * require the directory.
- */
- static
- int
- cmd_chdir(int ac, char *av[])
- {
- if (ac == 2) {
- if (chdir(av[1])) {
- warn("chdir");
- return 1;
- }
- return 0;
- }
- printf("Usage: chdir dir\n");
- return 1;
- }
- /*
- * exit
- * pretty simple. allow the user to choose the exit code if they want,
- * otherwise default to 0 (success).
- */
- static
- int
- cmd_exit(int ac, char *av[])
- {
- int code;
- if (ac == 1) {
- code = 0;
- }
- else if (ac == 2) {
- code = atoi(av[1]);
- }
- else {
- printf("Usage: exit [code]\n");
- return 1;
- }
-
- exit(code);
-
- return 0; /* quell the compiler warning */
- }
- /*
- * a struct of the builtins associates the builtin name with the function that
- * executes it. they must all take an argc and argv.
- */
- static struct {
- const char *name;
- int (*func)(int, char **);
- } builtins[] = {
- { "cd", cmd_chdir },
- { "chdir", cmd_chdir },
- { "exit", cmd_exit },
- { "wait", cmd_wait },
- { NULL, NULL }
- };
- /*
- * docommand
- * tokenizes the command line using strtok. if there aren't any commands,
- * simply returns. checks to see if it's a builtin, running it if it is.
- * otherwise, it's a standard command. check for the '&', try to background
- * the job if possible, otherwise just run it and wait on it.
- */
- static
- int
- docommand(char *buf)
- {
- char *args[NARG_MAX + 1];
- int nargs, i;
- char *s;
- pid_t pid;
- int status;
- int bg=0;
- time_t startsecs, endsecs;
- unsigned long startnsecs, endnsecs;
- nargs = 0;
- for (s = strtok(buf, " \t\r\n"); s; s = strtok(NULL, " \t\r\n")) {
- if (nargs >= NARG_MAX) {
- printf("%s: Too many arguments "
- "(exceeds system limit)\n",
- args[0]);
- return 1;
- }
- args[nargs++] = s;
- }
- args[nargs] = NULL;
- if (nargs==0) {
- /* empty line */
- return 0;
- }
- for (i=0; builtins[i].name; i++) {
- if (!strcmp(builtins[i].name, args[0])) {
- return builtins[i].func(nargs, args);
- }
- }
- /* Not a builtin; run it */
- if (nargs > 0 && !strcmp(args[nargs-1], "&")) {
- /* background */
- if (!can_bg()) {
- printf("%s: Too many background jobs; wait for "
- "some to finish before starting more\n",
- args[0]);
- return -1;
- }
- nargs--;
- args[nargs] = NULL;
- bg = 1;
- }
- if (timing) {
- __time(&startsecs, &startnsecs);
- }
- pid = fork();
- switch (pid) {
- case -1:
- /* error */
- warn("fork");
- return _MKWAIT_EXIT(255);
- case 0:
- /* child */
- execv(args[0], args);
- warn("%s", args[0]);
- /*
- * Use _exit() instead of exit() in the child
- * process to avoid calling atexit() functions,
- * which would cause hostcompat (if present) to
- * reset the tty state and mess up our input
- * handling.
- */
- _exit(1);
- default:
- break;
- }
- /* parent */
- if (bg) {
- /* background this command */
- remember_bg(pid);
- printf("[%d] %s ... &\n", pid, args[0]);
- return 0;
- }
- if (waitpid(pid, &status, 0) < 0) {
- warn("waitpid");
- status = -1;
- }
- if (timing) {
- __time(&endsecs, &endnsecs);
- if (endnsecs < startnsecs) {
- endnsecs += 1000000000;
- endsecs--;
- }
- endnsecs -= startnsecs;
- endsecs -= startsecs;
- warnx("subprocess time: %lu.%09lu seconds",
- (unsigned long) endsecs, (unsigned long) endnsecs);
- }
- return status;
- }
- /*
- * getcmd
- * pulls valid characters off the console, filling the buffer.
- * backspace deletes a character, simply by moving the position back.
- * a newline or carriage return breaks the loop, which terminates
- * the string and returns.
- *
- * if there's an invalid character or a backspace when there's nothing
- * in the buffer, putchars an alert (bell).
- */
- static
- void
- getcmd(char *buf, size_t len)
- {
- size_t pos = 0;
- int done=0, ch;
- /*
- * In the absence of a <ctype.h>, assume input is 7-bit ASCII.
- */
- while (!done) {
- ch = getchar();
- if ((ch == '\b' || ch == 127) && pos > 0) {
- putchar('\b');
- putchar(' ');
- putchar('\b');
- pos--;
- }
- else if (ch == '\r' || ch == '\n') {
- putchar('\r');
- putchar('\n');
- done = 1;
- }
- else if (ch >= 32 && ch < 127 && pos < len-1) {
- buf[pos++] = ch;
- putchar(ch);
- }
- else {
- /* alert (bell) character */
- putchar('\a');
- }
- }
- buf[pos] = 0;
- }
- /*
- * interactive
- * runs the interactive shell. basically, just infinitely loops, grabbing
- * commands and running them (and printing the exit status if it's not
- * success.)
- */
- static
- void
- interactive(void)
- {
- char buf[CMDLINE_MAX];
- int status;
- while (1) {
- printf("OS/161$ ");
- getcmd(buf, sizeof(buf));
- status = docommand(buf);
- if (status) {
- printstatus(status);
- printf("\n");
- }
- #ifdef WNOHANG
- waitpoll();
- #endif
- }
- }
- static
- void
- check_timing(void)
- {
- time_t secs;
- unsigned long nsecs;
- if (__time(&secs, &nsecs) != -1) {
- timing = 1;
- warnx("Timing enabled.");
- }
- }
- /*
- * main
- * if there are no arguments, run interactively, otherwise, run a program
- * from within the shell, but immediately exit.
- */
- int
- main(int argc, char *argv[])
- {
- #ifdef HOST
- hostcompat_init(argc, argv);
- #endif
- check_timing();
- /*
- * Allow argc to be 0 in case we're running on a broken kernel,
- * or one that doesn't set argv when starting the first shell.
- */
- if (argc == 0 || argc == 1) {
- interactive();
- }
- else if (argc == 3 && !strcmp(argv[1], "-c")) {
- return docommand(argv[2]);
- }
- else {
- errx(1, "Usage: sh [-c command]");
- }
- return 0;
- }
|