main.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. #include <errno.h>
  2. #include <pwd.h>
  3. #include <signal.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <unistd.h>
  8. #include <fcntl.h>
  9. #include <termios.h>
  10. #include <sys/mman.h>
  11. #include <sys/stat.h>
  12. #include <sys/utsname.h>
  13. #include <AK/FileSystemPath.h>
  14. #include <LibCore/CElapsedTimer.h>
  15. #include "GlobalState.h"
  16. #include "Parser.h"
  17. #include "LineEditor.h"
  18. //#define SH_DEBUG
  19. GlobalState g;
  20. static LineEditor editor;
  21. static void prompt()
  22. {
  23. if (g.uid == 0)
  24. printf("# ");
  25. else {
  26. printf("\033]0;%s@%s:%s\007", g.username.characters(), g.hostname, g.cwd.characters());
  27. printf("\033[31;1m%s\033[0m@\033[37;1m%s\033[0m:\033[32;1m%s\033[0m$> ", g.username.characters(), g.hostname, g.cwd.characters());
  28. }
  29. fflush(stdout);
  30. }
  31. static int sh_pwd(int, char**)
  32. {
  33. printf("%s\n", g.cwd.characters());
  34. return 0;
  35. }
  36. static volatile bool g_got_signal = false;
  37. void did_receive_signal(int signum)
  38. {
  39. printf("\nMy word, I've received a signal with number %d\n", signum);
  40. g_got_signal = true;
  41. }
  42. void handle_sigint(int)
  43. {
  44. g.was_interrupted = true;
  45. }
  46. static int sh_exit(int, char**)
  47. {
  48. printf("Good-bye!\n");
  49. exit(0);
  50. return 0;
  51. }
  52. static int sh_export(int argc, char** argv)
  53. {
  54. if (argc == 1) {
  55. for (int i = 0; environ[i]; ++i)
  56. puts(environ[i]);
  57. return 0;
  58. }
  59. auto parts = String(argv[1]).split('=');
  60. if (parts.size() != 2) {
  61. fprintf(stderr, "usage: export variable=value\n");
  62. return 1;
  63. }
  64. // FIXME: Yes, this leaks.
  65. // Maybe LibCore should grow a CEnvironment which is secretly a map to char*,
  66. // so it can keep track of the environment pointers as needed?
  67. const auto& s = String::format("%s=%s", parts[0].characters(), parts[1].characters());
  68. char *ev = strndup(s.characters(), s.length());
  69. putenv(ev);
  70. return 0;
  71. }
  72. static int sh_unset(int argc, char** argv)
  73. {
  74. if (argc != 2) {
  75. fprintf(stderr, "usage: unset variable\n");
  76. return 1;
  77. }
  78. unsetenv(argv[1]);
  79. return 0;
  80. }
  81. static int sh_cd(int argc, char** argv)
  82. {
  83. char pathbuf[PATH_MAX];
  84. if (argc == 1) {
  85. strcpy(pathbuf, g.home.characters());
  86. } else {
  87. if (argv[1][0] == '/')
  88. memcpy(pathbuf, argv[1], strlen(argv[1]) + 1);
  89. else
  90. sprintf(pathbuf, "%s/%s", g.cwd.characters(), argv[1]);
  91. }
  92. FileSystemPath canonical_path(pathbuf);
  93. if (!canonical_path.is_valid()) {
  94. printf("FileSystemPath failed to canonicalize '%s'\n", pathbuf);
  95. return 1;
  96. }
  97. const char* path = canonical_path.string().characters();
  98. struct stat st;
  99. int rc = stat(path, &st);
  100. if (rc < 0) {
  101. printf("lstat(%s) failed: %s\n", path, strerror(errno));
  102. return 1;
  103. }
  104. if (!S_ISDIR(st.st_mode)) {
  105. printf("Not a directory: %s\n", path);
  106. return 1;
  107. }
  108. rc = chdir(path);
  109. if (rc < 0) {
  110. printf("chdir(%s) failed: %s\n", path, strerror(errno));
  111. return 1;
  112. }
  113. g.cwd = canonical_path.string();
  114. return 0;
  115. }
  116. static int sh_history(int, char**)
  117. {
  118. for (int i = 0; i < editor.history().size(); ++i) {
  119. printf("%6d %s\n", i, editor.history()[i].characters());
  120. }
  121. return 0;
  122. }
  123. static int sh_umask(int argc, char** argv)
  124. {
  125. if (argc == 1) {
  126. mode_t old_mask = umask(0);
  127. printf("%#o\n", old_mask);
  128. umask(old_mask);
  129. return 0;
  130. }
  131. if (argc == 2) {
  132. unsigned mask;
  133. int matches = sscanf(argv[1], "%o", &mask);
  134. if (matches == 1) {
  135. umask(mask);
  136. return 0;
  137. }
  138. }
  139. printf("usage: umask <octal-mask>\n");
  140. return 0;
  141. }
  142. static bool handle_builtin(int argc, char** argv, int& retval)
  143. {
  144. if (argc == 0)
  145. return false;
  146. if (!strcmp(argv[0], "cd")) {
  147. retval = sh_cd(argc, argv);
  148. return true;
  149. }
  150. if (!strcmp(argv[0], "pwd")) {
  151. retval = sh_pwd(argc, argv);
  152. return true;
  153. }
  154. if (!strcmp(argv[0], "exit")) {
  155. retval = sh_exit(argc, argv);
  156. return true;
  157. }
  158. if (!strcmp(argv[0], "export")) {
  159. retval = sh_export(argc, argv);
  160. return true;
  161. }
  162. if (!strcmp(argv[0], "unset")) {
  163. retval = sh_unset(argc, argv);
  164. return true;
  165. }
  166. if (!strcmp(argv[0], "history")) {
  167. retval = sh_history(argc, argv);
  168. return true;
  169. }
  170. if (!strcmp(argv[0], "umask")) {
  171. retval = sh_umask(argc, argv);
  172. return true;
  173. }
  174. return false;
  175. }
  176. class FileDescriptorCollector {
  177. public:
  178. FileDescriptorCollector() { }
  179. ~FileDescriptorCollector() { collect(); }
  180. void collect()
  181. {
  182. for (auto fd : m_fds)
  183. close(fd);
  184. m_fds.clear();
  185. }
  186. void add(int fd) { m_fds.append(fd); }
  187. private:
  188. Vector<int, 32> m_fds;
  189. };
  190. struct CommandTimer {
  191. CommandTimer()
  192. {
  193. timer.start();
  194. }
  195. ~CommandTimer()
  196. {
  197. dbgprintf("sh: command finished in %d ms\n", timer.elapsed());
  198. }
  199. CElapsedTimer timer;
  200. };
  201. static int run_command(const String& cmd)
  202. {
  203. if (cmd.is_empty())
  204. return 0;
  205. auto subcommands = Parser(cmd).parse();
  206. #ifdef SH_DEBUG
  207. for (int i = 0; i < subcommands.size(); ++i) {
  208. for (int j = 0; j < i; ++j)
  209. dbgprintf(" ");
  210. for (auto& arg : subcommands[i].args) {
  211. dbgprintf("<%s> ", arg.characters());
  212. }
  213. dbgprintf("\n");
  214. for (auto& redirecton : subcommands[i].redirections) {
  215. for (int j = 0; j < i; ++j)
  216. dbgprintf(" ");
  217. dbgprintf(" ");
  218. switch (redirecton.type) {
  219. case Redirection::Pipe:
  220. dbgprintf("Pipe\n");
  221. break;
  222. case Redirection::FileRead:
  223. dbgprintf("fd:%d = FileRead: %s\n", redirecton.fd, redirecton.path.characters());
  224. break;
  225. case Redirection::FileWrite:
  226. dbgprintf("fd:%d = FileWrite: %s\n", redirecton.fd, redirecton.path.characters());
  227. break;
  228. case Redirection::FileWriteAppend:
  229. dbgprintf("fd:%d = FileWriteAppend: %s\n", redirecton.fd, redirecton.path.characters());
  230. break;
  231. default:
  232. break;
  233. }
  234. }
  235. }
  236. #endif
  237. if (subcommands.is_empty())
  238. return 0;
  239. FileDescriptorCollector fds;
  240. for (int i = 0; i < subcommands.size(); ++i) {
  241. auto& subcommand = subcommands[i];
  242. for (auto& redirection : subcommand.redirections) {
  243. switch (redirection.type) {
  244. case Redirection::Pipe: {
  245. int pipefd[2];
  246. int rc = pipe(pipefd);
  247. if (rc < 0) {
  248. perror("pipe");
  249. return 1;
  250. }
  251. subcommand.redirections.append({ Redirection::Rewire, STDOUT_FILENO, pipefd[1] });
  252. auto& next_command = subcommands[i + 1];
  253. next_command.redirections.append({ Redirection::Rewire, STDIN_FILENO, pipefd[0] });
  254. fds.add(pipefd[0]);
  255. fds.add(pipefd[1]);
  256. break;
  257. }
  258. case Redirection::FileWriteAppend: {
  259. int fd = open(redirection.path.characters(), O_WRONLY | O_CREAT | O_APPEND, 0666);
  260. if (fd < 0) {
  261. perror("open");
  262. return 1;
  263. }
  264. subcommand.redirections.append({ Redirection::Rewire, redirection.fd, fd });
  265. fds.add(fd);
  266. break;
  267. }
  268. case Redirection::FileWrite: {
  269. int fd = open(redirection.path.characters(), O_WRONLY | O_CREAT, 0666);
  270. if (fd < 0) {
  271. perror("open");
  272. return 1;
  273. }
  274. subcommand.redirections.append({ Redirection::Rewire, redirection.fd, fd });
  275. fds.add(fd);
  276. break;
  277. }
  278. case Redirection::FileRead: {
  279. int fd = open(redirection.path.characters(), O_RDONLY);
  280. if (fd < 0) {
  281. perror("open");
  282. return 1;
  283. }
  284. subcommand.redirections.append({ Redirection::Rewire, redirection.fd, fd });
  285. fds.add(fd);
  286. break;
  287. }
  288. case Redirection::Rewire:
  289. break; // ignore
  290. }
  291. }
  292. }
  293. struct termios trm;
  294. tcgetattr(0, &trm);
  295. Vector<pid_t> children;
  296. CommandTimer timer;
  297. for (int i = 0; i < subcommands.size(); ++i) {
  298. auto& subcommand = subcommands[i];
  299. Vector<const char*> argv;
  300. for (auto& arg : subcommand.args)
  301. argv.append(arg.characters());
  302. argv.append(nullptr);
  303. int retval = 0;
  304. if (handle_builtin(argv.size() - 1, const_cast<char**>(argv.data()), retval))
  305. return retval;
  306. pid_t child = fork();
  307. if (!child) {
  308. setpgid(0, 0);
  309. tcsetpgrp(0, getpid());
  310. for (auto& redirection : subcommand.redirections) {
  311. if (redirection.type == Redirection::Rewire) {
  312. #ifdef SH_DEBUGsh
  313. dbgprintf("in %s<%d>, dup2(%d, %d)\n", argv[0], getpid(), redirection.rewire_fd, redirection.fd);
  314. #endif
  315. int rc = dup2(redirection.rewire_fd, redirection.fd);
  316. if (rc < 0) {
  317. perror("dup2");
  318. return 1;
  319. }
  320. }
  321. }
  322. fds.collect();
  323. int rc = execvp(argv[0], const_cast<char* const*>(argv.data()));
  324. if (rc < 0) {
  325. fprintf(stderr, "execvp(%s): %s\n", argv[0], strerror(errno));
  326. exit(1);
  327. }
  328. ASSERT_NOT_REACHED();
  329. }
  330. children.append(child);
  331. }
  332. #ifdef SH_DEBUG
  333. dbgprintf("Closing fds in shell process:\n");
  334. #endif
  335. fds.collect();
  336. #ifdef SH_DEBUG
  337. dbgprintf("Now we gotta wait on children:\n");
  338. for (auto& child : children)
  339. dbgprintf(" %d\n", child);
  340. #endif
  341. int wstatus = 0;
  342. int rc;
  343. for (auto& child : children) {
  344. do {
  345. rc = waitpid(child, &wstatus, 0);
  346. if (rc < 0 && errno != EINTR) {
  347. perror("waitpid");
  348. break;
  349. }
  350. } while(errno == EINTR);
  351. }
  352. // FIXME: Should I really have to tcsetpgrp() after my child has exited?
  353. // Is the terminal controlling pgrp really still the PGID of the dead process?
  354. tcsetpgrp(0, getpid());
  355. tcsetattr(0, TCSANOW, &trm);
  356. if (WIFEXITED(wstatus)) {
  357. if (WEXITSTATUS(wstatus) != 0)
  358. printf("Exited with status %d\n", WEXITSTATUS(wstatus));
  359. return WEXITSTATUS(wstatus);
  360. } else {
  361. if (WIFSIGNALED(wstatus)) {
  362. switch (WTERMSIG(wstatus)) {
  363. case SIGINT:
  364. printf("Interrupted\n");
  365. break;
  366. default:
  367. printf("Terminated by signal %d\n", WTERMSIG(wstatus));
  368. break;
  369. }
  370. } else {
  371. printf("Exited abnormally\n");
  372. return 1;
  373. }
  374. }
  375. return 0;
  376. }
  377. int main(int argc, char** argv)
  378. {
  379. g.uid = getuid();
  380. g.sid = setsid();
  381. tcsetpgrp(0, getpgrp());
  382. tcgetattr(0, &g.termios);
  383. {
  384. struct sigaction sa;
  385. sa.sa_handler = handle_sigint;
  386. sa.sa_flags = 0;
  387. sa.sa_mask = 0;
  388. int rc = sigaction(SIGINT, &sa, nullptr);
  389. assert(rc == 0);
  390. }
  391. int rc = gethostname(g.hostname, sizeof(g.hostname));
  392. if (rc < 0)
  393. perror("gethostname");
  394. rc = ttyname_r(0, g.ttyname, sizeof(g.ttyname));
  395. if (rc < 0)
  396. perror("ttyname_r");
  397. {
  398. auto* pw = getpwuid(getuid());
  399. if (pw) {
  400. g.username = pw->pw_name;
  401. g.home = pw->pw_dir;
  402. const auto& s = String::format("HOME=%s", pw->pw_dir);
  403. char *ev = strndup(s.characters(), s.length());
  404. putenv(ev);
  405. }
  406. endpwent();
  407. }
  408. if (argc > 2 && !strcmp(argv[1], "-c")) {
  409. dbgprintf("sh -c '%s'\n", argv[2]);
  410. run_command(argv[2]);
  411. return 0;
  412. }
  413. {
  414. auto* cwd = getcwd(nullptr, 0);
  415. g.cwd = cwd;
  416. free(cwd);
  417. }
  418. for (;;) {
  419. prompt();
  420. auto line = editor.get_line();
  421. if (line.is_empty())
  422. continue;
  423. run_command(line);
  424. editor.add_to_history(line);
  425. }
  426. return 0;
  427. }