123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- #include <stdint.h>
- #include <stdarg.h>
- #include <sys/wait.h>
- #include <stdio.h>
- #include <string.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- // Parsed command representation
- #define EXEC 1
- #define REDIR 2
- #define PIPE 3
- #define LIST 4
- #define BACK 5
- #define MAXARGS 10
- struct cmd {
- int type;
- };
- struct execcmd {
- int type;
- char *argv[MAXARGS];
- char *eargv[MAXARGS];
- };
- struct redircmd {
- int type;
- struct cmd *cmd;
- char *file;
- char *efile;
- int mode;
- int fd;
- };
- struct pipecmd {
- int type;
- struct cmd *left;
- struct cmd *right;
- };
- struct listcmd {
- int type;
- struct cmd *left;
- struct cmd *right;
- };
- struct backcmd {
- int type;
- struct cmd *cmd;
- };
- int fork1(void); // Fork but panics on failure.
- void panic(char*);
- struct cmd *parsecmd(char*);
- #ifdef __clang__
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Winfinite-recursion"
- #else
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Winfinite-recursion"
- #endif
- #endif
- // Execute cmd. Never returns.
- void
- runcmd(struct cmd *cmd)
- {
- int p[2];
- int code;
- struct backcmd *bcmd;
- struct execcmd *ecmd;
- struct listcmd *lcmd;
- struct pipecmd *pcmd;
- struct redircmd *rcmd;
- if(cmd == 0)
- _exit(-1);
-
- switch(cmd->type){
- default:
- panic("runcmd");
- case EXEC:
- ecmd = (struct execcmd*)cmd;
- if(ecmd->argv[0] == 0)
- _exit(-1);
- execve(ecmd->argv[0], ecmd->argv, environ);
- printf("exec %s failed\n", ecmd->argv[0]);
- break;
- case REDIR:
- rcmd = (struct redircmd*)cmd;
- close(rcmd->fd);
- if(open(rcmd->file, rcmd->mode, 0666) < 0){
- printf("open %s failed\n", rcmd->file);
- _exit(-1);
- }
- runcmd(rcmd->cmd);
- break;
- case LIST:
- lcmd = (struct listcmd*)cmd;
- if(fork1() == 0)
- runcmd(lcmd->left);
- wait(&code);
- runcmd(lcmd->right);
- break;
- case PIPE:
- pcmd = (struct pipecmd*)cmd;
- if(pipe(p) < 0)
- panic("pipe");
- if(fork1() == 0){
- close(1);
- dup(p[1]);
- close(p[0]);
- close(p[1]);
- runcmd(pcmd->left);
- }
- if(fork1() == 0){
- close(0);
- dup(p[0]);
- close(p[0]);
- close(p[1]);
- runcmd(pcmd->right);
- }
- close(p[0]);
- close(p[1]);
- wait(&code);
- wait(&code);
- break;
-
- case BACK:
- bcmd = (struct backcmd*)cmd;
- if(fork1() == 0)
- runcmd(bcmd->cmd);
- break;
- }
- _exit(0);
- }
- #ifdef __clang__
- #pragma clang diagnostic pop
- #else
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
- #endif
- int
- getcmd(char *buf, int nbuf)
- {
- printf("[root@localhost] #\n");
- memset(buf, 0, nbuf);
- gets(buf);
- if(buf[0] == 0) // EOF
- return -1;
- return 0;
- }
- int
- main(void)
- {
- static char buf[100];
-
- int fd = 0;
- // Assumes three file descriptors open.
- while((fd = open("/dev/console", 0)) >= 0){
- if(fd >= 3){
- close(fd);
- break;
- }
- }
-
- // Read and run input commands.
- while(getcmd(buf, sizeof(buf)) >= 0){
- if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ')
- {
- // Clumsy but will have to do for now.
- // Chdir has no effect on the parent if run in the child.
- if(chdir(buf+3) < 0)
- printf("cannot cd %s\n", buf+3);
- continue;
- }
- pid_t pid = 0;
- if((pid = fork1()) == 0) {
- setpgid(0, 0);
- runcmd(parsecmd(buf));
- }
- tcsetpgrp(STDOUT_FILENO, pid);
- setpgid(pid, 0);
- int code;
- wait(&code);
- tcsetpgrp(STDOUT_FILENO, getpid());
- printf("[status: %d] ", code);
- }
- _exit(0);
- }
- void __attribute__((noreturn))
- panic(char *s)
- {
- printf("%s\n", s);
- _exit(-1);
- }
- int
- fork1(void)
- {
- int pid;
-
- pid = fork();
- if(pid == -1)
- panic("fork");
- return pid;
- }
- //PAGEBREAK!
- // Constructors
- struct cmd*
- execcmd(void)
- {
- struct execcmd *cmd;
- cmd = malloc(sizeof(*cmd));
- memset(cmd, 0, sizeof(*cmd));
- cmd->type = EXEC;
- return (struct cmd*)cmd;
- }
- struct cmd*
- redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
- {
- struct redircmd *cmd;
- cmd = malloc(sizeof(*cmd));
- memset(cmd, 0, sizeof(*cmd));
- cmd->type = REDIR;
- cmd->cmd = subcmd;
- cmd->file = file;
- cmd->efile = efile;
- cmd->mode = mode;
- cmd->fd = fd;
- return (struct cmd*)cmd;
- }
- struct cmd*
- pipecmd(struct cmd *left, struct cmd *right)
- {
- struct pipecmd *cmd;
- cmd = malloc(sizeof(*cmd));
- memset(cmd, 0, sizeof(*cmd));
- cmd->type = PIPE;
- cmd->left = left;
- cmd->right = right;
- return (struct cmd*)cmd;
- }
- struct cmd*
- listcmd(struct cmd *left, struct cmd *right)
- {
- struct listcmd *cmd;
- cmd = malloc(sizeof(*cmd));
- memset(cmd, 0, sizeof(*cmd));
- cmd->type = LIST;
- cmd->left = left;
- cmd->right = right;
- return (struct cmd*)cmd;
- }
- struct cmd*
- backcmd(struct cmd *subcmd)
- {
- struct backcmd *cmd;
- cmd = malloc(sizeof(*cmd));
- memset(cmd, 0, sizeof(*cmd));
- cmd->type = BACK;
- cmd->cmd = subcmd;
- return (struct cmd*)cmd;
- }
- //PAGEBREAK!
- // Parsing
- char whitespace[] = " \t\r\n\v";
- char symbols[] = "<|>&;()";
- int
- gettoken(char **ps, char *es, char **q, char **eq)
- {
- char *s;
- int ret;
-
- s = *ps;
- while(s < es && strchr(whitespace, *s))
- s++;
- if(q)
- *q = s;
- ret = *s;
- switch(*s){
- case 0:
- break;
- case '|':
- case '(':
- case ')':
- case ';':
- case '&':
- case '<':
- s++;
- break;
- case '>':
- s++;
- if(*s == '>'){
- ret = '+';
- s++;
- }
- break;
- default:
- ret = 'a';
- while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
- s++;
- break;
- }
- if(eq)
- *eq = s;
-
- while(s < es && strchr(whitespace, *s))
- s++;
- *ps = s;
- return ret;
- }
- int
- peek(char **ps, char *es, char *toks)
- {
- char *s;
-
- s = *ps;
- while(s < es && strchr(whitespace, *s))
- s++;
- *ps = s;
- return *s && strchr(toks, *s);
- }
- struct cmd *parseline(char**, char*);
- struct cmd *parsepipe(char**, char*);
- struct cmd *parseexec(char**, char*);
- struct cmd *nulterminate(struct cmd*);
- struct cmd*
- parsecmd(char *s)
- {
- char *es;
- struct cmd *cmd;
- es = s + strlen(s);
- cmd = parseline(&s, es);
- peek(&s, es, "");
- if(s != es){
- printf("leftovers: %s\n", s);
- panic("syntax");
- }
- nulterminate(cmd);
- return cmd;
- }
- struct cmd*
- parseline(char **ps, char *es)
- {
- struct cmd *cmd;
- cmd = parsepipe(ps, es);
- while(peek(ps, es, "&")){
- gettoken(ps, es, 0, 0);
- cmd = backcmd(cmd);
- }
- if(peek(ps, es, ";")){
- gettoken(ps, es, 0, 0);
- cmd = listcmd(cmd, parseline(ps, es));
- }
- return cmd;
- }
- struct cmd*
- parsepipe(char **ps, char *es)
- {
- struct cmd *cmd;
- cmd = parseexec(ps, es);
- if(peek(ps, es, "|")){
- gettoken(ps, es, 0, 0);
- cmd = pipecmd(cmd, parsepipe(ps, es));
- }
- return cmd;
- }
- struct cmd*
- parseredirs(struct cmd *cmd, char **ps, char *es)
- {
- int tok;
- char *q, *eq;
- while(peek(ps, es, "<>")){
- tok = gettoken(ps, es, 0, 0);
- if(gettoken(ps, es, &q, &eq) != 'a')
- panic("missing file for redirection");
- switch(tok){
- case '<':
- cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
- break;
- case '>':
- cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT | O_TRUNC, 1);
- break;
- case '+': // >>
- cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT | O_APPEND, 1);
- break;
- }
- }
- return cmd;
- }
- struct cmd*
- parseblock(char **ps, char *es)
- {
- struct cmd *cmd;
- if(!peek(ps, es, "("))
- panic("parseblock");
- gettoken(ps, es, 0, 0);
- cmd = parseline(ps, es);
- if(!peek(ps, es, ")"))
- panic("syntax - missing )");
- gettoken(ps, es, 0, 0);
- cmd = parseredirs(cmd, ps, es);
- return cmd;
- }
- struct cmd*
- parseexec(char **ps, char *es)
- {
- char *q, *eq;
- int tok, argc;
- struct execcmd *cmd;
- struct cmd *ret;
-
- if(peek(ps, es, "("))
- return parseblock(ps, es);
- ret = execcmd();
- cmd = (struct execcmd*)ret;
- argc = 0;
- ret = parseredirs(ret, ps, es);
- while(!peek(ps, es, "|)&;")){
- if((tok=gettoken(ps, es, &q, &eq)) == 0)
- break;
- if(tok != 'a')
- panic("syntax");
- cmd->argv[argc] = q;
- cmd->eargv[argc] = eq;
- argc++;
- if(argc >= MAXARGS)
- panic("too many args");
- ret = parseredirs(ret, ps, es);
- }
- cmd->argv[argc] = 0;
- cmd->eargv[argc] = 0;
- return ret;
- }
- // NUL-terminate all the counted strings.
- struct cmd*
- nulterminate(struct cmd *cmd)
- {
- int i;
- struct backcmd *bcmd;
- struct execcmd *ecmd;
- struct listcmd *lcmd;
- struct pipecmd *pcmd;
- struct redircmd *rcmd;
- if(cmd == 0)
- return 0;
-
- switch(cmd->type){
- case EXEC:
- ecmd = (struct execcmd*)cmd;
- for(i=0; ecmd->argv[i]; i++)
- *ecmd->eargv[i] = 0;
- break;
- case REDIR:
- rcmd = (struct redircmd*)cmd;
- nulterminate(rcmd->cmd);
- *rcmd->efile = 0;
- break;
- case PIPE:
- pcmd = (struct pipecmd*)cmd;
- nulterminate(pcmd->left);
- nulterminate(pcmd->right);
- break;
-
- case LIST:
- lcmd = (struct listcmd*)cmd;
- nulterminate(lcmd->left);
- nulterminate(lcmd->right);
- break;
- case BACK:
- bcmd = (struct backcmd*)cmd;
- nulterminate(bcmd->cmd);
- break;
- }
- return cmd;
- }
|