root/user/bin/sh/sh.c

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

DEFINITIONS

This source file includes following definitions.
  1. can_bg
  2. remember_bg
  3. printstatus
  4. dowait
  5. dowaitpoll
  6. waitpoll
  7. cmd_wait
  8. cmd_chdir
  9. cmd_exit
  10. docommand
  11. getcmd
  12. interactive
  13. check_timing
  14. main

   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 /*
  31  * sh - shell
  32  *
  33  * Usage:
  34  *     sh
  35  *     sh -c command
  36  */
  37 
  38 #include <sys/types.h>
  39 #include <sys/wait.h>
  40 #include <assert.h>
  41 #include <unistd.h>
  42 #include <stdlib.h>
  43 #include <stdio.h>
  44 #include <string.h>
  45 #include <limits.h>
  46 #include <errno.h>
  47 #include <err.h>
  48 
  49 #ifdef HOST
  50 #include "hostcompat.h"
  51 #endif
  52 
  53 #ifndef NARG_MAX
  54 /* no NARG_MAX on most unixes */
  55 #define NARG_MAX 1024
  56 #endif
  57 
  58 /* avoid making this unreasonably large; causes problems under dumbvm */
  59 #if ARG_MAX > 4096
  60 #define CMDLINE_MAX 4096
  61 #else
  62 #define CMDLINE_MAX ARG_MAX
  63 #endif
  64 
  65 /* set to nonzero if __time syscall seems to work */
  66 static int timing = 0;
  67 
  68 /* array of backgrounded jobs (allows "foregrounding") */
  69 #define MAXBG 128
  70 static pid_t bgpids[MAXBG];
  71 
  72 /*
  73  * can_bg
  74  * just checks for an open slot.
  75  */
  76 static
  77 int
  78 can_bg(void)
  79 {
  80         int i;
  81         
  82         for (i = 0; i < MAXBG; i++) {
  83                 if (bgpids[i] == 0) {
  84                         return 1;
  85                 }
  86         }
  87         
  88         return 0;
  89 }
  90 
  91 /* 
  92  * remember_bg
  93  * sticks the pid in an open slot in the background array.  note the assert --
  94  * better check can_bg before calling this.
  95  */
  96 static
  97 void
  98 remember_bg(pid_t pid)
  99 {
 100         int i;
 101         for (i = 0; i < MAXBG; i++) {
 102                 if (bgpids[i] == 0) {
 103                         bgpids[i] = pid;
 104                         return;
 105                 }
 106         }
 107         assert(0);
 108 }
 109 
 110 /*
 111  * printstatus
 112  * print results from wait
 113  */
 114 static
 115 void
 116 printstatus(int status)
 117 {
 118         if (WIFEXITED(status)) {
 119                 printf("Exit %d", WEXITSTATUS(status));
 120         }
 121         else if (WIFSIGNALED(status) && WCOREDUMP(status)) {
 122                 printf("Signal %d (core dumped)", WTERMSIG(status));
 123         }
 124         else if (WIFSIGNALED(status)) {
 125                 printf("Signal %d", WTERMSIG(status));
 126         }
 127         else if (WIFSTOPPED(status)) {
 128                 printf("Stopped on signal %d", WSTOPSIG(status));
 129         }
 130         else {
 131                 printf("Invalid status code %d", status);
 132         }
 133 }
 134 
 135 /*
 136  * dowait
 137  * just does a waitpid.
 138  */
 139 static
 140 void
 141 dowait(pid_t pid)
 142 {
 143         int status;
 144         if (waitpid(pid, &status, 0)<0) {
 145                 warn("pid %d", pid);
 146         }
 147         else {
 148                 printf("pid %d: ", pid);
 149                 printstatus(status);
 150                 printf("\n");
 151         }
 152 }
 153 
 154 #ifdef WNOHANG
 155 /*
 156  * dowaitpoll
 157  * like dowait, but uses WNOHANG. returns true if we got something.
 158  */
 159 static
 160 int
 161 dowaitpoll(pid_t pid)
 162 {
 163         int status;
 164         pid_t result;
 165         result = waitpid(pid, &status, WNOHANG);
 166         if (result<0) {
 167                 warn("pid %d", pid);
 168         }
 169         else if (result!=0) {
 170                 printf("pid %d: ", pid);
 171                 printstatus(status);
 172                 printf("\n");
 173                 return 1;
 174         }
 175         return 0;
 176 }
 177 
 178 /*
 179  * waitpoll
 180  * poll all background jobs for having exited.
 181  */
 182 static
 183 void
 184 waitpoll(void)
 185 {
 186         int i;
 187         for (i=0; i < MAXBG; i++) {
 188                 if (bgpids[i] != 0) {
 189                         if (dowaitpoll(bgpids[i])) {
 190                                 bgpids[i] = 0;
 191                         }
 192                 }
 193         }
 194 }
 195 #endif /* WNOHANG */
 196 
 197 /*
 198  * wait
 199  * allows the user to "foreground" a process by waiting on it.  without ps to
 200  * know the pids, this is a little tough to use with an arg, but without an
 201  * arg it will wait for all the background jobs.
 202  */
 203 static
 204 int
 205 cmd_wait(int ac, char *av[])
 206 {
 207         int i;
 208         pid_t pid;
 209 
 210         if (ac == 2) {
 211                 pid = atoi(av[1]);
 212                 dowait(pid);
 213                 for (i = 0; i < MAXBG; i++) {
 214                         if (bgpids[i]==pid) {
 215                                 bgpids[i] = 0;
 216                         }
 217                 }
 218                 return 0;
 219         }
 220         else if (ac == 1) {
 221                 for (i=0; i < MAXBG; i++) {
 222                         if (bgpids[i] != 0) {
 223                                 dowait(bgpids[i]);
 224                                 bgpids[i] = 0;
 225                         }
 226                 }
 227                 return 0;
 228         }
 229         printf("Usage: wait [pid]\n");
 230         return 1;
 231 }
 232 
 233 /*
 234  * chdir
 235  * just an interface to the system call.  no concept of home directory, so
 236  * require the directory.
 237  */
 238 static
 239 int
 240 cmd_chdir(int ac, char *av[])
 241 {
 242         if (ac == 2) {
 243                 if (chdir(av[1])) {
 244                         warn("chdir");
 245                         return 1;
 246                 }
 247                 return 0;
 248         }
 249         printf("Usage: chdir dir\n");
 250         return 1;
 251 }
 252 
 253 /*
 254  * exit
 255  * pretty simple.  allow the user to choose the exit code if they want,
 256  * otherwise default to 0 (success).
 257  */
 258 static
 259 int
 260 cmd_exit(int ac, char *av[])
 261 {
 262         int code;
 263 
 264         if (ac == 1) {
 265                 code = 0;
 266         }
 267         else if (ac == 2) {
 268                 code = atoi(av[1]);
 269         }
 270         else {
 271                 printf("Usage: exit [code]\n");
 272                 return 1;
 273         }
 274         
 275         exit(code);
 276         
 277         return 0; /* quell the compiler warning */
 278 }
 279 
 280 /*
 281  * a struct of the builtins associates the builtin name with the function that
 282  * executes it.  they must all take an argc and argv.
 283  */
 284 static struct {
 285         const char *name;
 286         int (*func)(int, char **);
 287 } builtins[] = {
 288         { "cd",    cmd_chdir },
 289         { "chdir", cmd_chdir },
 290         { "exit",  cmd_exit },
 291         { "wait",  cmd_wait },
 292         { NULL, NULL }
 293 };
 294 
 295 /*
 296  * docommand
 297  * tokenizes the command line using strtok.  if there aren't any commands,
 298  * simply returns.  checks to see if it's a builtin, running it if it is.
 299  * otherwise, it's a standard command.  check for the '&', try to background
 300  * the job if possible, otherwise just run it and wait on it.
 301  */
 302 static
 303 int
 304 docommand(char *buf)
 305 {
 306         char *args[NARG_MAX + 1];
 307         int nargs, i;
 308         char *s;
 309         pid_t pid;
 310         int status;
 311         int bg=0;
 312         time_t startsecs, endsecs;
 313         unsigned long startnsecs, endnsecs;
 314 
 315         nargs = 0;
 316         for (s = strtok(buf, " \t\r\n"); s; s = strtok(NULL, " \t\r\n")) {
 317                 if (nargs >= NARG_MAX) {
 318                         printf("%s: Too many arguments "
 319                                "(exceeds system limit)\n",
 320                                args[0]);
 321                         return 1;
 322                 }
 323                 args[nargs++] = s;
 324         }
 325         args[nargs] = NULL;
 326 
 327         if (nargs==0) {
 328                 /* empty line */
 329                 return 0;
 330         }
 331 
 332         for (i=0; builtins[i].name; i++) {
 333                 if (!strcmp(builtins[i].name, args[0])) {
 334                         return builtins[i].func(nargs, args);
 335                 }
 336         }
 337 
 338         /* Not a builtin; run it */
 339 
 340         if (nargs > 0 && !strcmp(args[nargs-1], "&")) {
 341                 /* background */
 342                 if (!can_bg()) {
 343                         printf("%s: Too many background jobs; wait for "
 344                                "some to finish before starting more\n",
 345                                args[0]);
 346                         return -1;
 347                 }
 348                 nargs--;
 349                 args[nargs] = NULL;
 350                 bg = 1;
 351         }
 352 
 353         if (timing) {
 354                 __time(&startsecs, &startnsecs);
 355         }
 356 
 357         pid = fork();
 358         switch (pid) {
 359                 case -1:
 360                         /* error */
 361                         warn("fork");
 362                         return _MKWAIT_EXIT(255);
 363                 case 0:
 364                         /* child */
 365                         execv(args[0], args);
 366                         warn("%s", args[0]);
 367                         /*
 368                          * Use _exit() instead of exit() in the child
 369                          * process to avoid calling atexit() functions,
 370                          * which would cause hostcompat (if present) to
 371                          * reset the tty state and mess up our input
 372                          * handling.
 373                          */
 374                         _exit(1);
 375                 default:
 376                         break;
 377         }
 378 
 379         /* parent */
 380         if (bg) {
 381                 /* background this command */
 382                 remember_bg(pid);
 383                 printf("[%d] %s ... &\n", pid, args[0]);
 384                 return 0;
 385         }
 386 
 387         if (waitpid(pid, &status, 0) < 0) {
 388                 warn("waitpid");
 389                 status = -1;
 390         }
 391 
 392         if (timing) {
 393                 __time(&endsecs, &endnsecs);
 394                 if (endnsecs < startnsecs) {
 395                         endnsecs += 1000000000;
 396                         endsecs--;
 397                 }
 398                 endnsecs -= startnsecs;
 399                 endsecs -= startsecs;
 400                 warnx("subprocess time: %lu.%09lu seconds",
 401                       (unsigned long) endsecs, (unsigned long) endnsecs);
 402         }
 403 
 404         return status;
 405 }
 406 
 407 /*
 408  * getcmd
 409  * pulls valid characters off the console, filling the buffer.  
 410  * backspace deletes a character, simply by moving the position back.
 411  * a newline or carriage return breaks the loop, which terminates
 412  * the string and returns.
 413  *
 414  * if there's an invalid character or a backspace when there's nothing 
 415  * in the buffer, putchars an alert (bell).
 416  */
 417 static
 418 void
 419 getcmd(char *buf, size_t len)
 420 {
 421         size_t pos = 0;
 422         int done=0, ch;
 423 
 424         /*
 425          * In the absence of a <ctype.h>, assume input is 7-bit ASCII.
 426          */
 427 
 428         while (!done) {
 429                 ch = getchar();
 430                 if ((ch == '\b' || ch == 127) && pos > 0) {
 431                         putchar('\b');
 432                         putchar(' ');
 433                         putchar('\b');
 434                         pos--;
 435                 }
 436                 else if (ch == '\r' || ch == '\n') {
 437                         putchar('\r');
 438                         putchar('\n');
 439                         done = 1;
 440                 }
 441                 else if (ch >= 32 && ch < 127 && pos < len-1) {
 442                         buf[pos++] = ch;
 443                         putchar(ch);
 444                 }
 445                 else {
 446                         /* alert (bell) character */
 447                         putchar('\a');
 448                 }
 449         }
 450         buf[pos] = 0;
 451 }       
 452 
 453 /*
 454  * interactive
 455  * runs the interactive shell.  basically, just infinitely loops, grabbing
 456  * commands and running them (and printing the exit status if it's not
 457  * success.)
 458  */
 459 static
 460 void
 461 interactive(void)
 462 {
 463         char buf[CMDLINE_MAX];
 464         int status;
 465 
 466         while (1) {
 467                 printf("OS/161$ ");
 468                 getcmd(buf, sizeof(buf));
 469                 status = docommand(buf);
 470                 if (status) {
 471                         printstatus(status);
 472                         printf("\n");
 473                 }
 474 #ifdef WNOHANG
 475                 waitpoll();
 476 #endif
 477         }
 478 }
 479 
 480 static
 481 void
 482 check_timing(void)
 483 {
 484         time_t secs;
 485         unsigned long nsecs;
 486         if (__time(&secs, &nsecs) != -1) {
 487                 timing = 1;
 488                 warnx("Timing enabled.");
 489         }
 490 }
 491 
 492 /* 
 493  * main
 494  * if there are no arguments, run interactively, otherwise, run a program
 495  * from within the shell, but immediately exit.
 496  */
 497 int
 498 main(int argc, char *argv[])
 499 {
 500 #ifdef HOST
 501         hostcompat_init(argc, argv);
 502 #endif
 503         check_timing();
 504 
 505         /*
 506          * Allow argc to be 0 in case we're running on a broken kernel,
 507          * or one that doesn't set argv when starting the first shell.
 508          */
 509         if (argc == 0 || argc == 1) {
 510                 interactive();
 511         }
 512         else if (argc == 3 && !strcmp(argv[1], "-c")) {
 513                 return docommand(argv[2]);
 514         }
 515         else {
 516                 errx(1, "Usage: sh [-c command]");
 517         }
 518         return 0;
 519 }

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