Prex Home / Browse Source - Prex Version: 0.9.0

root/usr/bin/sh/sh.c

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

DEFINITIONS

This source file includes following definitions.
  1. error
  2. showsignal
  3. showprompt
  4. execute
  5. redirect
  6. findcmd
  7. parsecmd
  8. parsepipe
  9. parseline
  10. readline
  11. cmdloop
  12. main

   1 /*
   2  * Copyright (c) 2005-2009, Kohsuke Ohtani
   3  * All rights reserved.
   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 author nor the names of any co-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 AUTHOR 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 AUTHOR 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 <sys/prex.h>
  31 #include <sys/keycode.h>
  32 #include <sys/syslog.h>
  33 #include <sys/wait.h>
  34 #include <sys/fcntl.h>
  35 
  36 #include <limits.h>
  37 #include <dirent.h>
  38 #include <termios.h>
  39 #include <ctype.h>
  40 #include <unistd.h>
  41 #include <string.h>
  42 #include <stdio.h>
  43 #include <stdlib.h>
  44 #include <errno.h>
  45 #include <setjmp.h>
  46 #include <libgen.h>     /* for basename() */
  47 
  48 #include "sh.h"
  49 
  50 #define ARGMAX          32
  51 
  52 #define CMD_PIPE        1
  53 #define CMD_BACKGND     2
  54 #define CMD_BUILTIN     4
  55 
  56 extern const struct cmdentry shell_cmds[];
  57 #ifdef CMDBOX
  58 extern const struct cmdentry builtin_cmds[];
  59 #define main(argc, argv)        sh_main(argc, argv)
  60 #endif
  61 
  62 static pid_t    shpid = 0;      /* pid of shell */
  63 char            retval;         /* return value for shell */
  64 unsigned        interact;       /* if shell reads from stdin */
  65 
  66 jmp_buf jmpbuf;
  67 
  68 static void
  69 error(const char *msg, ...)
  70 {
  71         va_list ap;
  72 
  73         va_start(ap, msg);
  74         if (msg != NULL) {
  75                 vfprintf(stderr, msg, ap);
  76                 fprintf(stderr, "\n");
  77         }
  78         va_end(ap);
  79         if (getpid() != shpid)
  80                 _exit(1);
  81 
  82         if (!interact)
  83                 _exit(1);
  84         retval = 1;
  85 }
  86 
  87 static void
  88 showsignal(int pid, int s)
  89 {
  90         int signo = WTERMSIG(s);
  91 
  92         signo &= 0x7f;
  93         if (signo < NSIG && sys_siglist[signo])
  94                 error(" %d: %s", pid, sys_siglist[signo]);
  95         else
  96                 error(" %d: Signal %d", pid, signo);
  97 
  98         retval = signo + 0200;
  99 }
 100 
 101 static void
 102 showprompt(void)
 103 {
 104         static char cwd[PATH_MAX];
 105         static char prompt[PATH_MAX+20];
 106 
 107         getcwd(cwd, PATH_MAX);
 108         sprintf(prompt, "\033[32m[prex:%s]\033[0m# ", cwd);
 109         write(1, prompt, strlen(prompt));
 110 }
 111 
 112 static void
 113 execute(int argc, char *argv[], int *redir, int flags, cmdfn_t cmdfn)
 114 {
 115         int pid, i;
 116         int status;
 117         static char **arg;
 118         char *file;
 119         char spid[20];
 120 
 121         arg = argc > 1 ? &argv[1] : NULL;
 122         file = argv[0];
 123         pid = vfork();
 124         if (pid == -1) {
 125                 for (i = 0; i < 2; i++)
 126                         if (redir[i] != -1)
 127                                 close(redir[i]);
 128                 error("Cannot fork");
 129                 return;
 130         }
 131         if (pid == 0) {
 132                 /* Child only */
 133                 setpgid(0, 0);
 134                 tcsetpgrp(2, getpgrp());
 135 
 136                 for (i = 0; i < 2; i++) {
 137                         if (redir[i] != -1) {
 138                                 if (dup2(redir[i], i) == -1)
 139                                         error("Cannot redirect %d", i);
 140                                 close(redir[i]);
 141                         }
 142                 }
 143                 signal(SIGINT, SIG_DFL);
 144                 signal(SIGQUIT, SIG_DFL);
 145                 signal(SIGTERM, SIG_DFL);
 146 
 147                 if (flags & CMD_BACKGND) {
 148                         signal(SIGINT, SIG_IGN);
 149                         signal(SIGQUIT, SIG_IGN);
 150                         if (redir[0] == -1) {
 151                                 close(0);
 152                                 open("/dev/null", O_RDWR);
 153                         }
 154                 }
 155                 errno = 0;
 156                 if (cmdfn) {
 157                         task_setname(task_self(), basename(file));
 158                         if (cmdfn(argc, argv) != 0)
 159                                 fprintf(stderr, "%s: %s\n", argv[0],
 160                                         strerror(errno));
 161                 } else {
 162                         execv(file, arg);
 163                         /* Try $PATH */
 164                         if (errno == ENOENT)
 165                                 execvp(file, arg);
 166                         if (errno == ENOENT || errno == ENOTDIR)
 167                                 error("%s: command not found", argv[0]);
 168                         else if (errno == EACCES)
 169                                 error("Permission denied");
 170                         else
 171                                 error("%s cannot execute", argv[0]);
 172                 }
 173                 exit(1);
 174                 /* NOTREACHED */
 175         }
 176         /* Parent */
 177         for (i = 0; i < 2; i++) {
 178                 if (redir[i] != -1)
 179                         close(redir[i]);
 180         }
 181         if (flags & CMD_PIPE)
 182                 return;
 183         if (flags & CMD_BACKGND) {
 184                 sprintf(spid, "%u\n", pid);
 185                 write(1, spid, strlen(spid));
 186                 return;
 187         }
 188 
 189         while (wait(&status) != pid);
 190         if (status) {
 191                 if (WIFSIGNALED(status))
 192                         showsignal(pid, status);
 193                 else if (WIFEXITED(status))
 194                         retval = WEXITSTATUS(status);
 195         } else
 196                 retval = 0;
 197         return;
 198 }
 199 
 200 static int
 201 redirect(char **args, int *redir)
 202 {
 203         unsigned int i, io, append = 0;
 204         int fd, argc;
 205         char *p, *file;
 206 
 207         for (i = 0; args[i] != NULL; i++) {
 208                 p = args[i];
 209                 switch (*p) {
 210                 case '<':
 211                         io = 0;
 212                         break;
 213                 case '>':
 214                         io = 1;
 215                         if (*(p + 1) == '>') {
 216                                 append = 1;
 217                                 p++;
 218                         }
 219                         break;
 220                 default:
 221                         continue;
 222                 }
 223 
 224                 /* get file name */
 225                 args[i] = (char *)-1;
 226                 if (*(p + 1) == '\0') {
 227                         file = args[++i];
 228                         args[i] = (char *)-1;
 229                 } else
 230                         file = p + 1;
 231 
 232                 /* if redirected from pipe, ignore */
 233                 if (redir[io] == -1) {
 234                         if (io == 1) {
 235                                 if (append)
 236                                         fd = open(file, O_WRONLY | O_APPEND);
 237                                 else
 238                                         fd = creat(file, 0666);
 239                         } else
 240                                 fd = open(file, O_RDONLY);
 241 
 242                         if (fd == -1) {
 243                                 error("%s: cannot open", file);
 244                                 return -1;
 245                         }
 246                         redir[io] = fd;
 247                 }
 248         }
 249 
 250         /* strip redirection info */
 251         argc = 0;
 252         for (i = 0; args[i]; i++) {
 253                 if (args[i] != (char *)-1)
 254                         args[argc++] = args[i];
 255         }
 256         args[argc] = NULL;
 257         return argc;
 258 }
 259 
 260 static cmdfn_t
 261 findcmd(const struct cmdentry cmds[], char *cmd)
 262 {
 263         int i = 0;
 264 
 265         while (cmds[i].cmd != NULL) {
 266                 if (!strcmp(cmd, cmds[i].cmd))
 267                         return cmds[i].func;
 268                 i++;
 269         }
 270         return 0;
 271 }
 272 
 273 static void
 274 parsecmd(char *cmds, int *redir, int flags)
 275 {
 276         static char cmdbox[] = "cmdbox";
 277         static char *args[ARGMAX];
 278         char *p, *word = NULL;
 279         cmdfn_t fn;
 280         int i, argc = 0;
 281 
 282         optind = 1;     /* for nommu */
 283 
 284         if (cmds[0] != ' ' && cmds[0] != '\t')
 285                 word = cmds;
 286 
 287         p = cmds;
 288         while (*p) {
 289                 if (word == NULL) {
 290                         /* Skip white space. */
 291                         if (*p != ' ' && *p != '\t')
 292                                 word = p;
 293                 } else {
 294                         if (*p == ' ' || *p == '\t') {
 295                                 *p = '\0';
 296                                 args[argc++] = word;
 297                                 word = NULL;
 298                                 if (argc >= ARGMAX - 1) {
 299                                         error("Too many args");
 300                                         return;
 301                                 }
 302                         }
 303                 }
 304                 p++;
 305         }
 306         if (argc == 0 && word == NULL)
 307                 return;
 308 
 309         if (word)
 310                 args[argc++] = word;
 311         args[argc] = NULL;
 312 
 313         /* Handle variable */
 314         if ((p = strchr(args[0], '=')) != NULL) {
 315                 *p++ = '\0';
 316                 if (*p == '\0')
 317                         unsetvar(args[0]);
 318                 else
 319                         setvar(args[0], p);
 320                 return;
 321         }
 322 
 323         fn = findcmd(shell_cmds, args[0]);
 324         if (fn) {
 325                 /* Run as shell internal command */
 326                 if ((*fn)(argc, args) != 0)
 327                         error("%s: %s", args[0], strerror(errno));
 328                 return;
 329         }
 330         argc = redirect(args, redir);
 331         if (argc == -1)
 332                 return;
 333 
 334         fn = findcmd(builtin_cmds, args[0]);
 335 
 336         /*
 337          * Alias: 'sh' => 'cmdbox sh'
 338          */
 339         if (fn == NULL && !strcmp(args[0], "sh")) {
 340                 for (i = argc; i >= 0; i--)
 341                         args[i + 1] = args[i];
 342                 args[0] = cmdbox;
 343                 argc++;
 344         }
 345         execute(argc, args, redir, flags, fn);
 346 }
 347 
 348 static void
 349 parsepipe(char *str, int flags)
 350 {
 351         int pip[2] = { -1, -1 };
 352         int redir[2] = { -1, -1 };
 353         char *p, *cmds;
 354 
 355         p = cmds = str;
 356         while (*cmds) {
 357                 switch (*p) {
 358                 case '|':
 359                         *p = '\0';
 360                         redir[0] = pip[0];
 361                         if (pipe(pip) == -1) {
 362                                 error("Cannot pipe");
 363                                 return;
 364                         }
 365                         redir[1] = pip[1];
 366                         parsecmd(cmds, redir, flags | CMD_PIPE);
 367                         cmds = p + 1;
 368                         break;
 369                 case '\0':
 370                         redir[0] = pip[0];
 371                         redir[1] = -1;
 372                         parsecmd(cmds, redir, flags);
 373                         return;
 374                 }
 375                 p++;
 376         }
 377 }
 378 
 379 static void
 380 parseline(char *line)
 381 {
 382         char *p, *cmds;
 383 
 384         p = cmds = line;
 385         while (*cmds) {
 386                 switch (*p) {
 387                 case ';':
 388                         *p = '\0';
 389                         parsepipe(cmds, 0);
 390                         cmds = p + 1;
 391                         break;
 392                 case '&':
 393                         *p = '\0';
 394                         parsepipe(cmds, CMD_BACKGND);
 395                         cmds = p + 1;
 396                         break;
 397                 case '\0':
 398                 case '\n':
 399                 case '#':
 400                         *p = '\0';
 401                         parsepipe(cmds, 0);
 402                         return;
 403                 }
 404                 p++;
 405         }
 406 }
 407 
 408 static char *
 409 readline(int fd, char *line, int len)
 410 {
 411         char *p = line;
 412         int nleft = len;
 413         int cnt;
 414 
 415         while (--nleft > 0) {
 416                 cnt = read(fd, p, 1);
 417                 if (cnt == -1)
 418                         return (char *)-1;      /* error */
 419                 if (cnt == 0) {
 420                         if (p == line)
 421                                 return NULL;    /* EOF */
 422                         break;
 423                 }
 424                 if (*p == '\n')
 425                         break;
 426                 p++;
 427         }
 428         *p = '\0';
 429         return line;
 430 }
 431 
 432 static void
 433 cmdloop(int fd)
 434 {
 435         static char line[LINE_MAX];
 436         char *p;
 437 
 438         for (;;) {
 439                 if (interact)
 440                         showprompt();
 441 
 442                 line[0] = '\0';
 443                 p = readline(fd, line, sizeof(line));
 444                 if (p == (char *)-1)
 445                         continue;
 446                 if (p == NULL)
 447                         break;
 448                 parseline(line);
 449                 tcsetpgrp(2, shpid);
 450         }
 451 }
 452 
 453 int
 454 main(int argc, char **argv)
 455 {
 456         int input;
 457 
 458         if (shpid == 0)
 459                 shpid = getpid();
 460 
 461         if (setjmp(jmpbuf)) {
 462                 argv = (char **)0;
 463                 argc = 1;
 464         }
 465         interact = 1;
 466         initvar();
 467 
 468         if (argc == 1) {
 469                 input = 0;
 470                 if (isatty(0) && isatty(1)) {
 471                         interact = 1;
 472                         signal(SIGINT, SIG_IGN);
 473                         signal(SIGQUIT, SIG_IGN);
 474                         signal(SIGTERM, SIG_IGN);
 475                 }
 476         } else {
 477                 interact = 0;
 478                 close(0);
 479                 input = open(argv[1], O_RDONLY);
 480                 if (input < 0) {
 481                         fprintf(stderr, "%s: cannot open\n", argv[1]);
 482                         interact = 1;
 483                         exit(1);
 484                 }
 485         }
 486         cmdloop(input);
 487         exit(retval);
 488         return 0;
 489 }

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