sash.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. /*
  2. * Copyright (c) 1993 by David I. Bell
  3. * Permission is granted to use, distribute, or modify this source,
  4. * provided that this copyright notice remains intact.
  5. *
  6. * Stand-alone shell for system maintainance for Linux.
  7. * This program should NOT be built using shared libraries.
  8. *
  9. * 1.1.1, hacked to re-allow cmd line invocation of script file
  10. * Pat Adamo, padamo@unix.asb.com
  11. */
  12. #include "sash.h"
  13. #include <stdlib.h>
  14. #include <signal.h>
  15. #include <errno.h>
  16. #include <unistd.h>
  17. #include <sys/stat.h>
  18. #include <sys/time.h>
  19. #include <sys/wait.h>
  20. static const char enoent_msg[] = "Bad command or file name";
  21. static const char unkerr_msg[] = "Unknown error!";
  22. extern int intflag;
  23. extern void do_test();
  24. typedef struct {
  25. char name[10];
  26. char usage[30];
  27. void (*func)();
  28. int minargs;
  29. int maxargs;
  30. } CMDTAB;
  31. CMDTAB cmdtab[] = {
  32. "cat", "filename ...",
  33. do_cat, 2, MAXARGS,
  34. "cd", "[dirname]",
  35. do_cd, 1, 2,
  36. "chgrp", "gid filename ...",
  37. do_chgrp, 3, MAXARGS,
  38. "chmod", "mode filename ...",
  39. do_chmod, 3, MAXARGS,
  40. "chown", "uid filename ...",
  41. do_chown, 3, MAXARGS,
  42. "cmp", "filename1 filename2",
  43. do_cmp, 3, 3,
  44. "cp", "srcname ... destname",
  45. do_cp, 3, MAXARGS,
  46. "date", "date [MMDDhhmm[YYYY]]",
  47. do_date, 1, 2,
  48. "df", "[file-system]",
  49. do_df, 1, 2,
  50. "echo", "[args] ...",
  51. do_echo, 1, MAXARGS,
  52. "exec", "filename [args]",
  53. do_exec, 2, MAXARGS,
  54. "exit", "",
  55. do_exit, 1, 1,
  56. "free", "",
  57. do_free, 1, 1,
  58. "help", "",
  59. do_help, 1, MAXARGS,
  60. "hexdump", "[-s pos] filename",
  61. do_hexdump, 1, 4,
  62. "hostname", "[hostname]",
  63. do_hostname, 1, 2,
  64. "kill", "[-sig] pid ...",
  65. do_kill, 2, MAXARGS,
  66. "ln", "[-s] srcname ... destname",
  67. do_ln, 3, MAXARGS,
  68. "ls", "[-lidC] filename ...",
  69. do_ls, 1, MAXARGS,
  70. "mkdir", "dirname ...",
  71. do_mkdir, 2, MAXARGS,
  72. "mknod", "filename type major minor",
  73. do_mknod, 5, 5,
  74. "more", "filename ...",
  75. do_more, 2, MAXARGS,
  76. "mount", "[-t type] devname dirname",
  77. do_mount, 3, MAXARGS,
  78. "mv", "srcname ... destname",
  79. do_mv, 3, MAXARGS,
  80. "pid", "",
  81. do_pid, 1, 1,
  82. "printenv", "[name]",
  83. do_printenv, 1, 2,
  84. "ps", "",
  85. do_ps, 1, MAXARGS,
  86. "pwd", "",
  87. do_pwd, 1, 1,
  88. "quit", "",
  89. do_exit, 1, 1,
  90. "rm", "filename ...",
  91. do_rm, 2, MAXARGS,
  92. "rmdir", "dirname ...",
  93. do_rmdir, 2, MAXARGS,
  94. "setenv", "name value",
  95. do_setenv, 3, 3,
  96. "sleep", "seconds",
  97. do_sleep, 1, 2,
  98. "source", "filename",
  99. do_source, 2, 2,
  100. "sync", "",
  101. do_sync, 1, 1,
  102. "touch", "filename ...",
  103. do_touch, 2, MAXARGS,
  104. "umask", "[mask]",
  105. do_umask, 1, 2,
  106. "umount", "filename",
  107. do_umount, 2, 2,
  108. 0, 0, 0,
  109. 0, 0
  110. };
  111. typedef struct {
  112. char *name;
  113. char *value;
  114. } ALIAS;
  115. static ALIAS *aliastable;
  116. static int aliascount;
  117. static FILE *sourcefiles[MAXSOURCE];
  118. static int sourcecount;
  119. volatile static BOOL intcrlf = TRUE;
  120. static void catchint();
  121. static void catchquit();
  122. static void catchchild();
  123. static void readfile();
  124. static void command();
  125. static void runcmd();
  126. static void showprompt();
  127. static BOOL trybuiltin();
  128. static BOOL command_in_path();
  129. static ALIAS *findalias();
  130. extern char ** environ;
  131. char buf[CMDLEN];
  132. int exit_code = 0;
  133. int main(argc, argv, env)
  134. int argc;
  135. char **argv;
  136. char *env[];
  137. {
  138. struct sigaction act;
  139. char *cp;
  140. int dofile = 0;
  141. if ((argc > 1) && !strcmp(argv[1], "-c")) {
  142. /* We are that fancy a shell */
  143. buf[0] = '\0';
  144. for (dofile = 2; dofile < argc; dofile++) {
  145. strncat(buf, argv[dofile], sizeof(buf));
  146. if (dofile + 1 < argc)
  147. strncat(buf, " ", sizeof(buf));
  148. }
  149. command(buf, FALSE);
  150. exit(exit_code);
  151. }
  152. if ((argc > 1) && strcmp(argv[1], "-t"))
  153. {
  154. dofile++;
  155. printf("Shell invoked to run file: %s\n",argv[1]);
  156. }
  157. else
  158. printf("\nSash command shell (OpenADK edition)\n");
  159. fflush(stdout);
  160. signal(SIGINT, catchint);
  161. signal(SIGQUIT, catchquit);
  162. memset(&act, 0, sizeof(act));
  163. act.sa_handler = catchchild;
  164. act.sa_flags = SA_RESTART;
  165. sigaction(SIGCHLD, &act, NULL);
  166. if (getenv("PATH") == NULL)
  167. putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
  168. readfile(dofile ? argv[1] : NULL);
  169. exit(exit_code);
  170. }
  171. /*
  172. * Read commands from the specified file.
  173. * A null name pointer indicates to read from stdin.
  174. */
  175. static void
  176. readfile(name)
  177. char *name;
  178. {
  179. FILE *fp;
  180. int cc;
  181. BOOL ttyflag;
  182. char *ptr;
  183. if (sourcecount >= MAXSOURCE) {
  184. fprintf(stderr, "Too many source files\n");
  185. return;
  186. }
  187. fp = stdin;
  188. if (name) {
  189. fp = fopen(name, "r");
  190. if (fp == NULL) {
  191. perror(name);
  192. return;
  193. }
  194. }
  195. sourcefiles[sourcecount++] = fp;
  196. ttyflag = isatty(fileno(fp));
  197. while (TRUE) {
  198. fflush(stdout);
  199. if (fp == stdin) //using terminal, so show prompt
  200. showprompt();
  201. if (intflag && !ttyflag && (fp != stdin)) {
  202. fclose(fp);
  203. sourcecount--;
  204. return;
  205. }
  206. if (fgets(buf, CMDLEN - 1, fp) == NULL) {
  207. if (ferror(fp) && (errno == EINTR)) {
  208. clearerr(fp);
  209. continue;
  210. }
  211. break;
  212. }
  213. cc = strlen(buf);
  214. while ((cc > 0) && isspace(buf[cc - 1]))
  215. cc--;
  216. buf[cc] = '\0';
  217. /* remove leading spaces and look for a '#' */
  218. ptr = &buf[0];
  219. while (*ptr == ' ') {
  220. ptr++;
  221. }
  222. if (*ptr != '#') {
  223. if (fp != stdin) {
  224. //taking commands from file - echo
  225. printf("Command: %s\n",buf);
  226. } //end if (fp != stdin)
  227. command(buf, fp == stdin);
  228. }
  229. }
  230. if (ferror(fp)) {
  231. perror("Reading command line");
  232. if (fp == stdin)
  233. exit(1);
  234. }
  235. clearerr(fp);
  236. if (fp != stdin) {
  237. fclose(fp);
  238. printf("Execution Finished, Exiting\n");
  239. }
  240. sourcecount--;
  241. }
  242. /*
  243. * Parse and execute one null-terminated command line string.
  244. * This breaks the command line up into words, checks to see if the
  245. * command is an alias, and expands wildcards.
  246. */
  247. static void
  248. command(cmd, do_history)
  249. int do_history;
  250. char *cmd;
  251. {
  252. ALIAS *alias;
  253. char **argv;
  254. int argc;
  255. int bg;
  256. char *c;
  257. char last_exit_code[10];
  258. sprintf(last_exit_code, "%d", exit_code);
  259. intflag = FALSE;
  260. exit_code = 0;
  261. freechunks();
  262. while (isblank(*cmd))
  263. cmd++;
  264. if (do_history) {
  265. int i;
  266. static char *history[HISTORY_SIZE];
  267. if (*cmd == '!') {
  268. if (cmd[1] == '!')
  269. i = 0;
  270. else {
  271. i = atoi(cmd+1) - 1;
  272. if (i < 0 || i >= HISTORY_SIZE) {
  273. printf("%s: Out of range\n", cmd);
  274. return;
  275. }
  276. }
  277. if (history[i] == NULL) {
  278. printf("%s: Null entry\n", cmd);
  279. return;
  280. }
  281. strcpy(cmd, history[i]);
  282. } else if (*cmd == 'h' && cmd[1] == '\0') {
  283. for (i=0; i<HISTORY_SIZE; i++) {
  284. if (history[i] != NULL)
  285. printf("%2d: %s\n", i+1, history[i]);
  286. }
  287. return;
  288. } else if (*cmd != '\0') {
  289. if (history[HISTORY_SIZE-1] != NULL)
  290. free(history[HISTORY_SIZE-1]);
  291. for (i=HISTORY_SIZE-1; i>0; i--)
  292. history[i] = history[i-1];
  293. history[0] = strdup(cmd);
  294. }
  295. }
  296. if (c = strchr(cmd, '&')) {
  297. *c = '\0';
  298. bg = 1;
  299. } else
  300. bg = 0;
  301. /* Set the last exit code */
  302. setenv("?", last_exit_code, 1);
  303. if ((cmd = expandenvvar(cmd)) == NULL)
  304. return;
  305. if ((*cmd == '\0') || !makeargs(cmd, &argc, &argv))
  306. return;
  307. /*
  308. * Search for the command in the alias table.
  309. * If it is found, then replace the command name with
  310. * the alias, and append any other arguments to it.
  311. */
  312. alias = findalias(argv[0]);
  313. if (alias) {
  314. cmd = buf;
  315. strcpy(cmd, alias->value);
  316. while (--argc > 0) {
  317. strcat(cmd, " ");
  318. strcat(cmd, *++argv);
  319. }
  320. if (!makeargs(cmd, &argc, &argv))
  321. return;
  322. }
  323. /*
  324. * BASH-style variable setting
  325. */
  326. if (argc == 1) {
  327. c = index(argv[0], '=');
  328. if (c > argv[0]) {
  329. *c++ = '\0';
  330. setenv(argv[0], c, 1);
  331. return;
  332. }
  333. }
  334. /*
  335. * Now look for the command in the builtin table, and execute
  336. * the command if found.
  337. */
  338. if (!strcmp(argv[0], "builtin")) {
  339. --argc;
  340. ++argv;
  341. if (!*argv || !trybuiltin(argc, argv))
  342. fprintf(stderr, "%s: %s\n", argv[-1], enoent_msg);
  343. return;
  344. } else if (!command_in_path(argv[0]) && trybuiltin(argc, argv))
  345. return;
  346. /*
  347. * Not found, run the program along the PATH list.
  348. */
  349. runcmd(cmd, bg, argc, argv);
  350. }
  351. /*
  352. * return true if we find this command in our
  353. * path.
  354. */
  355. static BOOL
  356. command_in_path(char *cmd)
  357. {
  358. struct stat stat_buf;
  359. if (strchr(cmd, '/') == 0) {
  360. char * path;
  361. static char path_copy[PATHLEN];
  362. /* Search path for binary */
  363. for (path = getenv("PATH"); path && *path; ) {
  364. char * p2;
  365. strcpy(path_copy, path);
  366. if (p2 = strchr(path_copy, ':')) {
  367. *p2 = '\0';
  368. }
  369. if (strlen(path_copy))
  370. strcat(path_copy, "/");
  371. strcat(path_copy, cmd);
  372. if (!stat(path_copy, &stat_buf) && (stat_buf.st_mode & 0111))
  373. return(TRUE);
  374. p2 = strchr(path, ':');
  375. if (p2)
  376. path = p2 + 1;
  377. else
  378. path = 0;
  379. }
  380. } else if (!stat(cmd, &stat_buf) && (stat_buf.st_mode & 0111))
  381. return(TRUE);
  382. return(FALSE);
  383. }
  384. /*
  385. * Try to execute a built-in command.
  386. * Returns TRUE if the command is a built in, whether or not the
  387. * command succeeds. Returns FALSE if this is not a built-in command.
  388. */
  389. static BOOL
  390. trybuiltin(argc, argv)
  391. int argc;
  392. char **argv;
  393. {
  394. CMDTAB *cmdptr;
  395. int oac;
  396. int newargc;
  397. int matches;
  398. int i;
  399. char *newargv[MAXARGS];
  400. char *nametable[MAXARGS];
  401. cmdptr = cmdtab - 1;
  402. do {
  403. cmdptr++;
  404. if (cmdptr->name[0] == 0)
  405. return FALSE;
  406. } while (strcmp(argv[0], cmdptr->name));
  407. /*
  408. * Give a usage string if the number of arguments is too large
  409. * or too small.
  410. */
  411. if ((argc < cmdptr->minargs) || (argc > cmdptr->maxargs)) {
  412. fprintf(stderr, "usage: %s %s\n",
  413. cmdptr->name, cmdptr->usage);
  414. fflush(stderr);
  415. return TRUE;
  416. }
  417. /*
  418. * Now for each command argument, see if it is a wildcard, and if
  419. * so, replace the argument with the list of matching filenames.
  420. */
  421. newargv[0] = argv[0];
  422. newargc = 1;
  423. oac = 0;
  424. while (++oac < argc) {
  425. if (argv[oac][0] == '"' || argv[oac][0] == '\'') {
  426. argv[oac]++;
  427. matches = 0;
  428. }
  429. else {
  430. matches = expandwildcards(argv[oac], MAXARGS, nametable);
  431. if (matches < 0)
  432. return TRUE;
  433. }
  434. if ((newargc + matches) >= MAXARGS) {
  435. fprintf(stderr, "Too many arguments\n");
  436. return TRUE;
  437. }
  438. if (matches == 0)
  439. newargv[newargc++] = argv[oac];
  440. for (i = 0; i < matches; i++)
  441. newargv[newargc++] = nametable[i];
  442. }
  443. (*cmdptr->func)(newargc, newargv);
  444. return TRUE;
  445. }
  446. /*
  447. * Execute the specified command.
  448. */
  449. static void
  450. runcmd(cmd, bg, argc, argv)
  451. char *cmd;
  452. int bg;
  453. int argc;
  454. char **argv;
  455. {
  456. register char * cp;
  457. int pid;
  458. int status;
  459. int oac;
  460. int newargc;
  461. int matches;
  462. int i;
  463. char *newargv[MAXARGS];
  464. char *nametable[MAXARGS];
  465. struct sigaction act;
  466. newargv[0] = argv[0];
  467. /*
  468. * Now for each command argument, see if it is a wildcard, and if
  469. * so, replace the argument with the list of matching filenames.
  470. */
  471. newargc = 1;
  472. oac = 0;
  473. while (++oac < argc) {
  474. if (argv[oac][0] == '"' || argv[oac][0] == '\'') {
  475. argv[oac]++;
  476. matches = 0;
  477. }
  478. else {
  479. matches = expandwildcards(argv[oac], MAXARGS, nametable);
  480. if (matches < 0)
  481. return;
  482. }
  483. if ((newargc + matches) >= MAXARGS) {
  484. fprintf(stderr, "Too many arguments\n");
  485. return;
  486. }
  487. if (matches == 0)
  488. newargv[newargc++] = argv[oac];
  489. for (i = 0; i < matches; i++)
  490. newargv[newargc++] = nametable[i];
  491. }
  492. newargv[newargc] = 0;
  493. if (!bg)
  494. signal(SIGCHLD, SIG_DFL);
  495. /*
  496. * Do the fork and exec ourselves.
  497. * If this fails with ENOEXEC, then run the
  498. * shell anyway since it might be a shell script.
  499. */
  500. if (!(pid = vfork())) {
  501. int ci;
  502. char errbuf[50];
  503. /*
  504. * We are the child, so run the program.
  505. * First close any extra file descriptors we have opened.
  506. * be sure not to modify any globals after the vfork !
  507. */
  508. for (ci = 0; ci < sourcecount; ci++)
  509. if (sourcefiles[ci] != stdin)
  510. close(fileno(sourcefiles[ci]));
  511. signal(SIGINT, SIG_DFL);
  512. signal(SIGQUIT, SIG_DFL);
  513. signal(SIGCHLD, SIG_DFL);
  514. execvp(newargv[0], newargv);
  515. ci = errno;
  516. write(2, newargv[0], strlen(newargv[0]));
  517. write(2, ": ", 2);
  518. if (ci == ENOENT)
  519. write(2, enoent_msg, sizeof(enoent_msg) - 1);
  520. else if (strerror_r(ci, errbuf, sizeof(errbuf)))
  521. write(2, unkerr_msg, sizeof(unkerr_msg) - 1);
  522. else
  523. write(2, errbuf, strlen(errbuf));
  524. write(2, "\n", 1);
  525. _exit(ci == ENOENT ? 127 : 126);
  526. }
  527. if (pid < 0) {
  528. memset(&act, 0, sizeof(act));
  529. act.sa_handler = catchchild;
  530. act.sa_flags = SA_RESTART;
  531. sigaction(SIGCHLD, &act, NULL);
  532. perror("vfork failed");
  533. return;
  534. }
  535. if (bg) {
  536. printf("[%d]\n", pid);
  537. return;
  538. }
  539. if (pid) {
  540. int cpid;
  541. status = 0;
  542. intcrlf = FALSE;
  543. for (;;) {
  544. cpid = wait4(pid, &status, 0, 0);
  545. if ((cpid < 0) && (errno == EINTR))
  546. continue;
  547. if (cpid < 0)
  548. break;
  549. if (cpid != pid) {
  550. fprintf(stderr, "sh %d: child %d died\n", getpid(), cpid);
  551. continue;
  552. }
  553. }
  554. act.sa_handler = catchchild;
  555. memset(&act.sa_mask, 0, sizeof(act.sa_mask));
  556. act.sa_flags = SA_RESTART;
  557. sigaction(SIGCHLD, &act, NULL);
  558. intcrlf = TRUE;
  559. if (WIFEXITED(status)) {
  560. if (WEXITSTATUS(status) == 0)
  561. return;
  562. exit_code = WEXITSTATUS(status);
  563. } else
  564. exit_code = 1;
  565. return;
  566. }
  567. perror(argv[0]);
  568. exit(1);
  569. }
  570. void
  571. do_help(argc, argv)
  572. int argc;
  573. char **argv;
  574. {
  575. CMDTAB *cmdptr;
  576. for (cmdptr = cmdtab; cmdptr->name && cmdptr->name[0]; cmdptr++)
  577. printf("%-10s %s\n", cmdptr->name, cmdptr->usage);
  578. }
  579. /*
  580. * Look up an alias name, and return a pointer to it.
  581. * Returns NULL if the name does not exist.
  582. */
  583. static ALIAS *
  584. findalias(name)
  585. char *name;
  586. {
  587. ALIAS *alias;
  588. int count;
  589. count = aliascount;
  590. for (alias = aliastable; count-- > 0; alias++) {
  591. if (strcmp(name, alias->name) == 0)
  592. return alias;
  593. }
  594. return NULL;
  595. }
  596. void
  597. do_source(argc, argv)
  598. int argc;
  599. char **argv;
  600. {
  601. readfile(argv[1]);
  602. }
  603. void
  604. do_pid(argc, argv)
  605. int argc;
  606. char **argv;
  607. {
  608. printf("%d\n", getpid());
  609. }
  610. void
  611. do_exec(argc, argv)
  612. int argc;
  613. char **argv;
  614. {
  615. while (--sourcecount >= 0) {
  616. if (sourcefiles[sourcecount] != stdin)
  617. fclose(sourcefiles[sourcecount]);
  618. }
  619. argv[argc] = NULL;
  620. execvp(argv[1], &argv[1]);
  621. perror(argv[1]);
  622. exit(1);
  623. }
  624. /*
  625. * Display the prompt string.
  626. */
  627. static void
  628. showprompt()
  629. {
  630. char *cp;
  631. char buf[60];
  632. if ((cp = getenv("PS1")) != NULL) {
  633. printf("%s", cp);
  634. }
  635. else {
  636. *buf = '\0';
  637. getcwd(buf, sizeof(buf) - 1);
  638. printf("%s> ", buf);
  639. }
  640. fflush(stdout);
  641. }
  642. static void
  643. catchint()
  644. {
  645. signal(SIGINT, catchint);
  646. intflag = TRUE;
  647. if (intcrlf)
  648. write(STDOUT, "\n", 1);
  649. }
  650. static void
  651. catchquit()
  652. {
  653. signal(SIGQUIT, catchquit);
  654. intflag = TRUE;
  655. if (intcrlf)
  656. write(STDOUT, "\n", 1);
  657. }
  658. static void
  659. catchchild()
  660. {
  661. char buf[40];
  662. pid_t pid;
  663. int status;
  664. pid = wait4(-1, &status, WUNTRACED, 0);
  665. if (WIFSTOPPED(status))
  666. sprintf(buf, "sh %d: Child %d stopped\n", getpid(), pid);
  667. else
  668. sprintf(buf, "sh %d: Child %d died\n", getpid(), pid);
  669. if (intcrlf)
  670. write(STDOUT, "\n", 1);
  671. write(STDOUT, buf, strlen(buf));
  672. }
  673. /* END CODE */