sh.c 9.9 KB

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