xv6, line by line
user/sh.c

user/sh.c

C · 499 lines · annotated 100% · user program / library · upstream

About this file

The xv6 shell: the program that prints $ , reads what you type, and runs it. It is an ordinary user program with no special powers, and it is one of the best places to see why Unix splits process creation into fork and exec.

Each command line goes through two stages:

  1. Parse. A small recursive-descent parser parser (parsecmd down to gettoken) turns the text into a parse tree of five node kinds: run a program, redirect a descriptor, connect a pipe, run two commands in sequence, run in the background.
  2. Run. runcmd walks the tree. Every node becomes a few system calls: fork, exec, wait, close, open, dup, pipe. Redirection and pipelines both rest on one kernel rule: a new file descriptor always takes the lowest free number.

main ties them together, and handles the one command the shell must run itself, cd.

Read before: user/init.c, which starts the shell. Read next: kernel/sysfile.c and kernel/pipe.c for the system calls the shell relies on.

1// Shell.
7// Parsed command representation
8#define EXEC 1
9#define REDIR 2
10#define PIPE 3
11#define LIST 4
12#define BACK 5
14#define MAXARGS 10
16struct cmd {
17 int type;
18};
20struct execcmd {
21 int type;
22 char *argv[MAXARGS];
23 char *eargv[MAXARGS];
24};
26struct redircmd {
27 int type;
28 struct cmd *cmd;
29 char *file;
30 char *efile;
31 int mode;
32 int fd;
33};
35struct pipecmd {
36 int type;
37 struct cmd *left;
38 struct cmd *right;
39};
41struct listcmd {
42 int type;
43 struct cmd *left;
44 struct cmd *right;
45};
47struct backcmd {
48 int type;
49 struct cmd *cmd;
50};
52int fork1(void); // Fork but panics on failure.
53void panic(char *);
54struct cmd *parsecmd(char *);
55void runcmd(struct cmd *) __attribute__((noreturn));
57// Execute cmd. Never returns.
58void
59runcmd(struct cmd *cmd)
61 int p[2];
62 struct backcmd *bcmd;
63 struct execcmd *ecmd;
64 struct listcmd *lcmd;
65 struct pipecmd *pcmd;
66 struct redircmd *rcmd;
68 if (cmd == 0)
69 exit(1);
71 switch (cmd->type) {
72 default:
73 panic("runcmd");
75 case EXEC:
76 ecmd = (struct execcmd *)cmd;
77 if (ecmd->argv[0] == 0)
78 exit(1);
80 fprintf(2, "exec %s failed\n", ecmd->argv[0]);
81 break;
83 case REDIR:
84 rcmd = (struct redircmd *)cmd;
86 if (open(rcmd->file, rcmd->mode) < 0) {
87 fprintf(2, "open %s failed\n", rcmd->file);
88 exit(1);
89 }
91 break;
93 case LIST:
94 lcmd = (struct listcmd *)cmd;
95 if (fork1() == 0)
97 wait(0);
99 break;
101 case PIPE:
102 pcmd = (struct pipecmd *)cmd;
103 if (pipe(p) < 0)
104 panic("pipe");
105 if (fork1() == 0) {
107 dup(p[1]);
108 close(p[0]);
109 close(p[1]);
111 }
112 if (fork1() == 0) {
114 dup(p[0]);
115 close(p[0]);
116 close(p[1]);
118 }
119 close(p[0]);
120 close(p[1]);
121 wait(0);
122 wait(0);
123 break;
125 case BACK:
126 bcmd = (struct backcmd *)cmd;
127 if (fork1() == 0)
129 break;
130 }
131 exit(0);
134int
135getcmd(char *buf, int nbuf)
137 write(2, "$ ", 2);
140 if (buf[0] == 0) // EOF
141 return -1;
142 return 0;
145int
146main(void)
148 static char buf[100];
149 int fd;
151 // Ensure that three file descriptors are open.
152 while ((fd = open("console", O_RDWR)) >= 0) {
153 if (fd >= 3) {
155 break;
156 }
157 }
159 // Read and run input commands.
160 while (getcmd(buf, sizeof(buf)) >= 0) {
161 char *cmd = buf;
162 while (*cmd == ' ' || *cmd == '\t')
163 cmd++;
164 if (*cmd == '\n') // is a blank command
165 continue;
166 if (cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' ') {
167 // Chdir must be called by the parent, not the child.
168 cmd[strlen(cmd) - 1] = 0; // chop \n
169 if (chdir(cmd + 3) < 0)
170 fprintf(2, "cannot cd %s\n", cmd + 3);
171 } else {
172 if (fork1() == 0)
174 wait(0);
175 }
176 }
177 exit(0);
180void
181panic(char *s)
183 fprintf(2, "%s\n", s);
184 exit(1);
187int
188fork1(void)
190 int pid;
192 pid = fork();
193 if (pid == -1)
194 panic("fork");
195 return pid;
198//PAGEBREAK!
199// Constructors
201struct cmd *
204 struct execcmd *cmd;
206 cmd = malloc(sizeof(*cmd));
207 memset(cmd, 0, sizeof(*cmd));
209 return (struct cmd *)cmd;
212struct cmd *
213redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
215 struct redircmd *cmd;
217 cmd = malloc(sizeof(*cmd));
218 memset(cmd, 0, sizeof(*cmd));
224 cmd->fd = fd;
225 return (struct cmd *)cmd;
228struct cmd *
229pipecmd(struct cmd *left, struct cmd *right)
231 struct pipecmd *cmd;
233 cmd = malloc(sizeof(*cmd));
234 memset(cmd, 0, sizeof(*cmd));
238 return (struct cmd *)cmd;
241struct cmd *
242listcmd(struct cmd *left, struct cmd *right)
244 struct listcmd *cmd;
246 cmd = malloc(sizeof(*cmd));
247 memset(cmd, 0, sizeof(*cmd));
251 return (struct cmd *)cmd;
254struct cmd *
257 struct backcmd *cmd;
259 cmd = malloc(sizeof(*cmd));
260 memset(cmd, 0, sizeof(*cmd));
263 return (struct cmd *)cmd;
265//PAGEBREAK!
266// Parsing
268char whitespace[] = " \t\r\n\v";
269char symbols[] = "<|>&;()";
271int
272gettoken(char **ps, char *es, char **q, char **eq)
274 char *s;
275 int ret;
277 s = *ps;
278 while (s < es && strchr(whitespace, *s))
279 s++;
280 if (q)
281 *q = s;
282 ret = *s;
283 switch (*s) {
284 case 0:
285 break;
286 case '|':
287 case '(':
288 case ')':
289 case ';':
290 case '&':
291 case '<':
292 s++;
293 break;
294 case '>':
295 s++;
296 if (*s == '>') {
297 ret = '+';
298 s++;
299 }
300 break;
301 default:
302 ret = 'a';
303 while (s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
304 s++;
305 break;
306 }
307 if (eq)
308 *eq = s;
310 while (s < es && strchr(whitespace, *s))
311 s++;
312 *ps = s;
313 return ret;
316int
317peek(char **ps, char *es, char *toks)
319 char *s;
321 s = *ps;
322 while (s < es && strchr(whitespace, *s))
323 s++;
324 *ps = s;
325 return *s && strchr(toks, *s);
328struct cmd *parseline(char **, char *);
329struct cmd *parsepipe(char **, char *);
330struct cmd *parseexec(char **, char *);
331struct cmd *nulterminate(struct cmd *);
333struct cmd *
334parsecmd(char *s)
336 char *es;
337 struct cmd *cmd;
339 es = s + strlen(s);
341 peek(&s, es, "");
342 if (s != es) {
343 fprintf(2, "leftovers: %s\n", s);
344 panic("syntax");
345 }
347 return cmd;
350struct cmd *
351parseline(char **ps, char *es)
353 struct cmd *cmd;
356 while (peek(ps, es, "&")) {
357 gettoken(ps, es, 0, 0);
359 }
360 if (peek(ps, es, ";")) {
361 gettoken(ps, es, 0, 0);
363 }
364 return cmd;
367struct cmd *
368parsepipe(char **ps, char *es)
370 struct cmd *cmd;
373 if (peek(ps, es, "|")) {
374 gettoken(ps, es, 0, 0);
376 }
377 return cmd;
380struct cmd *
381parseredirs(struct cmd *cmd, char **ps, char *es)
383 int tok;
384 char *q, *eq;
386 while (peek(ps, es, "<>")) {
387 tok = gettoken(ps, es, 0, 0);
388 if (gettoken(ps, es, &q, &eq) != 'a')
389 panic("missing file for redirection");
390 switch (tok) {
391 case '<':
393 break;
394 case '>':
396 break;
397 case '+': // >>
399 break;
400 }
401 }
402 return cmd;
405struct cmd *
406parseblock(char **ps, char *es)
408 struct cmd *cmd;
410 if (!peek(ps, es, "("))
411 panic("parseblock");
412 gettoken(ps, es, 0, 0);
414 if (!peek(ps, es, ")"))
415 panic("syntax - missing )");
416 gettoken(ps, es, 0, 0);
418 return cmd;
421struct cmd *
422parseexec(char **ps, char *es)
424 char *q, *eq;
425 int tok, argc;
426 struct execcmd *cmd;
427 struct cmd *ret;
429 if (peek(ps, es, "("))
430 return parseblock(ps, es);
433 cmd = (struct execcmd *)ret;
435 argc = 0;
437 while (!peek(ps, es, "|)&;")) {
438 if ((tok = gettoken(ps, es, &q, &eq)) == 0)
439 break;
440 if (tok != 'a')
441 panic("syntax");
445 if (argc >= MAXARGS)
446 panic("too many args");
448 }
449 cmd->argv[argc] = 0;
450 cmd->eargv[argc] = 0;
451 return ret;
454// NUL-terminate all the counted strings.
455struct cmd *
458 int i;
459 struct backcmd *bcmd;
460 struct execcmd *ecmd;
461 struct listcmd *lcmd;
462 struct pipecmd *pcmd;
463 struct redircmd *rcmd;
465 if (cmd == 0)
466 return 0;
468 switch (cmd->type) {
469 case EXEC:
470 ecmd = (struct execcmd *)cmd;
471 for (i = 0; ecmd->argv[i]; i++)
472 *ecmd->eargv[i] = 0;
473 break;
475 case REDIR:
476 rcmd = (struct redircmd *)cmd;
478 *rcmd->efile = 0;
479 break;
481 case PIPE:
482 pcmd = (struct pipecmd *)cmd;
485 break;
487 case LIST:
488 lcmd = (struct listcmd *)cmd;
491 break;
493 case BACK:
494 bcmd = (struct backcmd *)cmd;
496 break;
497 }
498 return cmd;