sh.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. #include <stdint.h>
  2. #include <stdarg.h>
  3. #include <sys/wait.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. #include <stdlib.h>
  9. // Parsed command representation
  10. #define EXEC 1
  11. #define REDIR 2
  12. #define PIPE 3
  13. #define LIST 4
  14. #define BACK 5
  15. #define MAXARGS 10
  16. struct cmd {
  17. int type;
  18. };
  19. struct execcmd {
  20. int type;
  21. char *argv[MAXARGS];
  22. char *eargv[MAXARGS];
  23. };
  24. struct redircmd {
  25. int type;
  26. struct cmd *cmd;
  27. char *file;
  28. char *efile;
  29. int mode;
  30. int fd;
  31. };
  32. struct pipecmd {
  33. int type;
  34. struct cmd *left;
  35. struct cmd *right;
  36. };
  37. struct listcmd {
  38. int type;
  39. struct cmd *left;
  40. struct cmd *right;
  41. };
  42. struct backcmd {
  43. int type;
  44. struct cmd *cmd;
  45. };
  46. int fork1(void); // Fork but panics on failure.
  47. void panic(char*);
  48. struct cmd *parsecmd(char*);
  49. #ifdef __clang__
  50. #pragma clang diagnostic push
  51. #pragma clang diagnostic ignored "-Winfinite-recursion"
  52. #else
  53. #ifdef __GNUC__
  54. #pragma GCC diagnostic push
  55. #pragma GCC diagnostic ignored "-Winfinite-recursion"
  56. #endif
  57. #endif
  58. // Execute cmd. Never returns.
  59. void
  60. runcmd(struct cmd *cmd)
  61. {
  62. int p[2];
  63. int code;
  64. struct backcmd *bcmd;
  65. struct execcmd *ecmd;
  66. struct listcmd *lcmd;
  67. struct pipecmd *pcmd;
  68. struct redircmd *rcmd;
  69. if(cmd == 0)
  70. _exit(-1);
  71. switch(cmd->type){
  72. default:
  73. panic("runcmd");
  74. case EXEC:
  75. ecmd = (struct execcmd*)cmd;
  76. if(ecmd->argv[0] == 0)
  77. _exit(-1);
  78. execve(ecmd->argv[0], ecmd->argv, environ);
  79. printf("exec %s failed\n", ecmd->argv[0]);
  80. break;
  81. case REDIR:
  82. rcmd = (struct redircmd*)cmd;
  83. close(rcmd->fd);
  84. if(open(rcmd->file, rcmd->mode, 0666) < 0){
  85. printf("open %s failed\n", rcmd->file);
  86. _exit(-1);
  87. }
  88. runcmd(rcmd->cmd);
  89. break;
  90. case LIST:
  91. lcmd = (struct listcmd*)cmd;
  92. if(fork1() == 0)
  93. runcmd(lcmd->left);
  94. wait(&code);
  95. runcmd(lcmd->right);
  96. break;
  97. case PIPE:
  98. pcmd = (struct pipecmd*)cmd;
  99. if(pipe(p) < 0)
  100. panic("pipe");
  101. if(fork1() == 0){
  102. close(1);
  103. dup(p[1]);
  104. close(p[0]);
  105. close(p[1]);
  106. runcmd(pcmd->left);
  107. }
  108. if(fork1() == 0){
  109. close(0);
  110. dup(p[0]);
  111. close(p[0]);
  112. close(p[1]);
  113. runcmd(pcmd->right);
  114. }
  115. close(p[0]);
  116. close(p[1]);
  117. wait(&code);
  118. wait(&code);
  119. break;
  120. case BACK:
  121. bcmd = (struct backcmd*)cmd;
  122. if(fork1() == 0)
  123. runcmd(bcmd->cmd);
  124. break;
  125. }
  126. _exit(0);
  127. }
  128. #ifdef __clang__
  129. #pragma clang diagnostic pop
  130. #else
  131. #ifdef __GNUC__
  132. #pragma GCC diagnostic pop
  133. #endif
  134. #endif
  135. int
  136. getcmd(char *buf, int nbuf)
  137. {
  138. printf("[root@localhost] #\n");
  139. memset(buf, 0, nbuf);
  140. gets(buf);
  141. if(buf[0] == 0) // EOF
  142. return -1;
  143. return 0;
  144. }
  145. int
  146. main(void)
  147. {
  148. static char buf[100];
  149. int fd = 0;
  150. // Assumes three file descriptors open.
  151. while((fd = open("/dev/console", 0)) >= 0){
  152. if(fd >= 3){
  153. close(fd);
  154. break;
  155. }
  156. }
  157. // Read and run input commands.
  158. while(getcmd(buf, sizeof(buf)) >= 0){
  159. if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ')
  160. {
  161. // Clumsy but will have to do for now.
  162. // Chdir has no effect on the parent if run in the child.
  163. if(chdir(buf+3) < 0)
  164. printf("cannot cd %s\n", buf+3);
  165. continue;
  166. }
  167. pid_t pid = 0;
  168. if((pid = fork1()) == 0) {
  169. setpgid(0, 0);
  170. runcmd(parsecmd(buf));
  171. }
  172. tcsetpgrp(STDOUT_FILENO, pid);
  173. setpgid(pid, 0);
  174. int code;
  175. wait(&code);
  176. tcsetpgrp(STDOUT_FILENO, getpid());
  177. printf("[status: %d] ", code);
  178. }
  179. _exit(0);
  180. }
  181. void __attribute__((noreturn))
  182. panic(char *s)
  183. {
  184. printf("%s\n", s);
  185. _exit(-1);
  186. }
  187. int
  188. fork1(void)
  189. {
  190. int pid;
  191. pid = fork();
  192. if(pid == -1)
  193. panic("fork");
  194. return pid;
  195. }
  196. //PAGEBREAK!
  197. // Constructors
  198. struct cmd*
  199. execcmd(void)
  200. {
  201. struct execcmd *cmd;
  202. cmd = malloc(sizeof(*cmd));
  203. memset(cmd, 0, sizeof(*cmd));
  204. cmd->type = EXEC;
  205. return (struct cmd*)cmd;
  206. }
  207. struct cmd*
  208. redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
  209. {
  210. struct redircmd *cmd;
  211. cmd = malloc(sizeof(*cmd));
  212. memset(cmd, 0, sizeof(*cmd));
  213. cmd->type = REDIR;
  214. cmd->cmd = subcmd;
  215. cmd->file = file;
  216. cmd->efile = efile;
  217. cmd->mode = mode;
  218. cmd->fd = fd;
  219. return (struct cmd*)cmd;
  220. }
  221. struct cmd*
  222. pipecmd(struct cmd *left, struct cmd *right)
  223. {
  224. struct pipecmd *cmd;
  225. cmd = malloc(sizeof(*cmd));
  226. memset(cmd, 0, sizeof(*cmd));
  227. cmd->type = PIPE;
  228. cmd->left = left;
  229. cmd->right = right;
  230. return (struct cmd*)cmd;
  231. }
  232. struct cmd*
  233. listcmd(struct cmd *left, struct cmd *right)
  234. {
  235. struct listcmd *cmd;
  236. cmd = malloc(sizeof(*cmd));
  237. memset(cmd, 0, sizeof(*cmd));
  238. cmd->type = LIST;
  239. cmd->left = left;
  240. cmd->right = right;
  241. return (struct cmd*)cmd;
  242. }
  243. struct cmd*
  244. backcmd(struct cmd *subcmd)
  245. {
  246. struct backcmd *cmd;
  247. cmd = malloc(sizeof(*cmd));
  248. memset(cmd, 0, sizeof(*cmd));
  249. cmd->type = BACK;
  250. cmd->cmd = subcmd;
  251. return (struct cmd*)cmd;
  252. }
  253. //PAGEBREAK!
  254. // Parsing
  255. char whitespace[] = " \t\r\n\v";
  256. char symbols[] = "<|>&;()";
  257. int
  258. gettoken(char **ps, char *es, char **q, char **eq)
  259. {
  260. char *s;
  261. int ret;
  262. s = *ps;
  263. while(s < es && strchr(whitespace, *s))
  264. s++;
  265. if(q)
  266. *q = s;
  267. ret = *s;
  268. switch(*s){
  269. case 0:
  270. break;
  271. case '|':
  272. case '(':
  273. case ')':
  274. case ';':
  275. case '&':
  276. case '<':
  277. s++;
  278. break;
  279. case '>':
  280. s++;
  281. if(*s == '>'){
  282. ret = '+';
  283. s++;
  284. }
  285. break;
  286. default:
  287. ret = 'a';
  288. while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
  289. s++;
  290. break;
  291. }
  292. if(eq)
  293. *eq = s;
  294. while(s < es && strchr(whitespace, *s))
  295. s++;
  296. *ps = s;
  297. return ret;
  298. }
  299. int
  300. peek(char **ps, char *es, char *toks)
  301. {
  302. char *s;
  303. s = *ps;
  304. while(s < es && strchr(whitespace, *s))
  305. s++;
  306. *ps = s;
  307. return *s && strchr(toks, *s);
  308. }
  309. struct cmd *parseline(char**, char*);
  310. struct cmd *parsepipe(char**, char*);
  311. struct cmd *parseexec(char**, char*);
  312. struct cmd *nulterminate(struct cmd*);
  313. struct cmd*
  314. parsecmd(char *s)
  315. {
  316. char *es;
  317. struct cmd *cmd;
  318. es = s + strlen(s);
  319. cmd = parseline(&s, es);
  320. peek(&s, es, "");
  321. if(s != es){
  322. printf("leftovers: %s\n", s);
  323. panic("syntax");
  324. }
  325. nulterminate(cmd);
  326. return cmd;
  327. }
  328. struct cmd*
  329. parseline(char **ps, char *es)
  330. {
  331. struct cmd *cmd;
  332. cmd = parsepipe(ps, es);
  333. while(peek(ps, es, "&")){
  334. gettoken(ps, es, 0, 0);
  335. cmd = backcmd(cmd);
  336. }
  337. if(peek(ps, es, ";")){
  338. gettoken(ps, es, 0, 0);
  339. cmd = listcmd(cmd, parseline(ps, es));
  340. }
  341. return cmd;
  342. }
  343. struct cmd*
  344. parsepipe(char **ps, char *es)
  345. {
  346. struct cmd *cmd;
  347. cmd = parseexec(ps, es);
  348. if(peek(ps, es, "|")){
  349. gettoken(ps, es, 0, 0);
  350. cmd = pipecmd(cmd, parsepipe(ps, es));
  351. }
  352. return cmd;
  353. }
  354. struct cmd*
  355. parseredirs(struct cmd *cmd, char **ps, char *es)
  356. {
  357. int tok;
  358. char *q, *eq;
  359. while(peek(ps, es, "<>")){
  360. tok = gettoken(ps, es, 0, 0);
  361. if(gettoken(ps, es, &q, &eq) != 'a')
  362. panic("missing file for redirection");
  363. switch(tok){
  364. case '<':
  365. cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
  366. break;
  367. case '>':
  368. cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT | O_TRUNC, 1);
  369. break;
  370. case '+': // >>
  371. cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT | O_APPEND, 1);
  372. break;
  373. }
  374. }
  375. return cmd;
  376. }
  377. struct cmd*
  378. parseblock(char **ps, char *es)
  379. {
  380. struct cmd *cmd;
  381. if(!peek(ps, es, "("))
  382. panic("parseblock");
  383. gettoken(ps, es, 0, 0);
  384. cmd = parseline(ps, es);
  385. if(!peek(ps, es, ")"))
  386. panic("syntax - missing )");
  387. gettoken(ps, es, 0, 0);
  388. cmd = parseredirs(cmd, ps, es);
  389. return cmd;
  390. }
  391. struct cmd*
  392. parseexec(char **ps, char *es)
  393. {
  394. char *q, *eq;
  395. int tok, argc;
  396. struct execcmd *cmd;
  397. struct cmd *ret;
  398. if(peek(ps, es, "("))
  399. return parseblock(ps, es);
  400. ret = execcmd();
  401. cmd = (struct execcmd*)ret;
  402. argc = 0;
  403. ret = parseredirs(ret, ps, es);
  404. while(!peek(ps, es, "|)&;")){
  405. if((tok=gettoken(ps, es, &q, &eq)) == 0)
  406. break;
  407. if(tok != 'a')
  408. panic("syntax");
  409. cmd->argv[argc] = q;
  410. cmd->eargv[argc] = eq;
  411. argc++;
  412. if(argc >= MAXARGS)
  413. panic("too many args");
  414. ret = parseredirs(ret, ps, es);
  415. }
  416. cmd->argv[argc] = 0;
  417. cmd->eargv[argc] = 0;
  418. return ret;
  419. }
  420. // NUL-terminate all the counted strings.
  421. struct cmd*
  422. nulterminate(struct cmd *cmd)
  423. {
  424. int i;
  425. struct backcmd *bcmd;
  426. struct execcmd *ecmd;
  427. struct listcmd *lcmd;
  428. struct pipecmd *pcmd;
  429. struct redircmd *rcmd;
  430. if(cmd == 0)
  431. return 0;
  432. switch(cmd->type){
  433. case EXEC:
  434. ecmd = (struct execcmd*)cmd;
  435. for(i=0; ecmd->argv[i]; i++)
  436. *ecmd->eargv[i] = 0;
  437. break;
  438. case REDIR:
  439. rcmd = (struct redircmd*)cmd;
  440. nulterminate(rcmd->cmd);
  441. *rcmd->efile = 0;
  442. break;
  443. case PIPE:
  444. pcmd = (struct pipecmd*)cmd;
  445. nulterminate(pcmd->left);
  446. nulterminate(pcmd->right);
  447. break;
  448. case LIST:
  449. lcmd = (struct listcmd*)cmd;
  450. nulterminate(lcmd->left);
  451. nulterminate(lcmd->right);
  452. break;
  453. case BACK:
  454. bcmd = (struct backcmd*)cmd;
  455. nulterminate(bcmd->cmd);
  456. break;
  457. }
  458. return cmd;
  459. }