main.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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 void prompt()
  21. {
  22. if (g.uid == 0)
  23. printf("# ");
  24. else {
  25. printf("\033]0;%s@%s:%s\007", g.username.characters(), g.hostname, g.cwd.characters());
  26. 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());
  27. }
  28. fflush(stdout);
  29. }
  30. static int sh_pwd(int, char**)
  31. {
  32. printf("%s\n", g.cwd.characters());
  33. return 0;
  34. }
  35. static volatile bool g_got_signal = false;
  36. void did_receive_signal(int signum)
  37. {
  38. printf("\nMy word, I've received a signal with number %d\n", signum);
  39. g_got_signal = true;
  40. }
  41. void handle_sigint(int)
  42. {
  43. g.was_interrupted = true;
  44. }
  45. static int sh_exit(int, char**)
  46. {
  47. printf("Good-bye!\n");
  48. exit(0);
  49. return 0;
  50. }
  51. static int sh_export(int argc, char** argv)
  52. {
  53. if (argc == 1) {
  54. for (int i = 0; environ[i]; ++i)
  55. puts(environ[i]);
  56. return 0;
  57. }
  58. auto parts = String(argv[1]).split('=');
  59. if (parts.size() != 2) {
  60. fprintf(stderr, "usage: export variable=value\n");
  61. return 1;
  62. }
  63. putenv(const_cast<char*>(String::format("%s=%s", parts[0].characters(), parts[1].characters()).characters()));
  64. return 0;
  65. }
  66. static int sh_cd(int argc, char** argv)
  67. {
  68. char pathbuf[PATH_MAX];
  69. if (argc == 1) {
  70. strcpy(pathbuf, g.home.characters());
  71. } else {
  72. if (argv[1][0] == '/')
  73. memcpy(pathbuf, argv[1], strlen(argv[1]) + 1);
  74. else
  75. sprintf(pathbuf, "%s/%s", g.cwd.characters(), argv[1]);
  76. }
  77. FileSystemPath canonical_path(pathbuf);
  78. if (!canonical_path.is_valid()) {
  79. printf("FileSystemPath failed to canonicalize '%s'\n", pathbuf);
  80. return 1;
  81. }
  82. const char* path = canonical_path.string().characters();
  83. struct stat st;
  84. int rc = stat(path, &st);
  85. if (rc < 0) {
  86. printf("lstat(%s) failed: %s\n", path, strerror(errno));
  87. return 1;
  88. }
  89. if (!S_ISDIR(st.st_mode)) {
  90. printf("Not a directory: %s\n", path);
  91. return 1;
  92. }
  93. rc = chdir(path);
  94. if (rc < 0) {
  95. printf("chdir(%s) failed: %s\n", path, strerror(errno));
  96. return 1;
  97. }
  98. g.cwd = canonical_path.string();
  99. return 0;
  100. }
  101. static bool handle_builtin(int argc, char** argv, int& retval)
  102. {
  103. if (argc == 0)
  104. return false;
  105. if (!strcmp(argv[0], "cd")) {
  106. retval = sh_cd(argc, argv);
  107. return true;
  108. }
  109. if (!strcmp(argv[0], "pwd")) {
  110. retval = sh_pwd(argc, argv);
  111. return true;
  112. }
  113. if (!strcmp(argv[0], "exit")) {
  114. retval = sh_exit(argc, argv);
  115. return true;
  116. }
  117. if (!strcmp(argv[0], "export")) {
  118. retval = sh_export(argc, argv);
  119. return true;
  120. }
  121. return false;
  122. }
  123. class FileDescriptorCollector {
  124. public:
  125. FileDescriptorCollector() { }
  126. ~FileDescriptorCollector() { collect(); }
  127. void collect()
  128. {
  129. for (auto fd : m_fds)
  130. close(fd);
  131. m_fds.clear();
  132. }
  133. void add(int fd) { m_fds.append(fd); }
  134. private:
  135. Vector<int, 32> m_fds;
  136. };
  137. struct CommandTimer {
  138. CommandTimer()
  139. {
  140. timer.start();
  141. }
  142. ~CommandTimer()
  143. {
  144. dbgprintf("sh: command finished in %d ms\n", timer.elapsed());
  145. }
  146. CElapsedTimer timer;
  147. };
  148. static int run_command(const String& cmd)
  149. {
  150. if (cmd.is_empty())
  151. return 0;
  152. auto subcommands = Parser(cmd).parse();
  153. #ifdef SH_DEBUG
  154. for (int i = 0; i < subcommands.size(); ++i) {
  155. for (int j = 0; j < i; ++j)
  156. printf(" ");
  157. for (auto& arg : subcommands[i].args) {
  158. printf("<%s> ", arg.characters());
  159. }
  160. printf("\n");
  161. for (auto& redirecton : subcommands[i].redirections) {
  162. for (int j = 0; j < i; ++j)
  163. printf(" ");
  164. printf(" ");
  165. switch (redirecton.type) {
  166. case Redirection::Pipe:
  167. printf("Pipe\n");
  168. break;
  169. case Redirection::FileRead:
  170. printf("fd:%d = FileRead: %s\n", redirecton.fd, redirecton.path.characters());
  171. break;
  172. case Redirection::FileWrite:
  173. printf("fd:%d = FileWrite: %s\n", redirecton.fd, redirecton.path.characters());
  174. break;
  175. default:
  176. break;
  177. }
  178. }
  179. }
  180. #endif
  181. if (subcommands.is_empty())
  182. return 0;
  183. FileDescriptorCollector fds;
  184. for (int i = 0; i < subcommands.size(); ++i) {
  185. auto& subcommand = subcommands[i];
  186. for (auto& redirection : subcommand.redirections) {
  187. if (redirection.type == Redirection::Pipe) {
  188. int pipefd[2];
  189. int rc = pipe(pipefd);
  190. if (rc < 0) {
  191. perror("pipe");
  192. return 1;
  193. }
  194. subcommand.redirections.append({ Redirection::Rewire, STDOUT_FILENO, pipefd[1] });
  195. auto& next_command = subcommands[i + 1];
  196. next_command.redirections.append({ Redirection::Rewire, STDIN_FILENO, pipefd[0] });
  197. fds.add(pipefd[0]);
  198. fds.add(pipefd[1]);
  199. }
  200. if (redirection.type == Redirection::FileWrite) {
  201. int fd = open(redirection.path.characters(), O_WRONLY | O_CREAT, 0666);
  202. if (fd < 0) {
  203. perror("open");
  204. return 1;
  205. }
  206. subcommand.redirections.append({ Redirection::Rewire, redirection.fd, fd });
  207. fds.add(fd);
  208. }
  209. if (redirection.type == Redirection::FileRead) {
  210. int fd = open(redirection.path.characters(), O_RDONLY);
  211. if (fd < 0) {
  212. perror("open");
  213. return 1;
  214. }
  215. subcommand.redirections.append({ Redirection::Rewire, redirection.fd, fd });
  216. fds.add(fd);
  217. }
  218. }
  219. }
  220. struct termios trm;
  221. tcgetattr(0, &trm);
  222. Vector<pid_t> children;
  223. CommandTimer timer;
  224. for (int i = 0; i < subcommands.size(); ++i) {
  225. auto& subcommand = subcommands[i];
  226. Vector<const char*> argv;
  227. for (auto& arg : subcommand.args)
  228. argv.append(arg.characters());
  229. argv.append(nullptr);
  230. int retval = 0;
  231. if (handle_builtin(argv.size() - 1, const_cast<char**>(argv.data()), retval))
  232. return retval;
  233. pid_t child = fork();
  234. if (!child) {
  235. setpgid(0, 0);
  236. tcsetpgrp(0, getpid());
  237. for (auto& redirection : subcommand.redirections) {
  238. if (redirection.type == Redirection::Rewire) {
  239. #ifdef SH_DEBUGsh
  240. dbgprintf("in %s<%d>, dup2(%d, %d)\n", argv[0], getpid(), redirection.rewire_fd, redirection.fd);
  241. #endif
  242. int rc = dup2(redirection.rewire_fd, redirection.fd);
  243. if (rc < 0) {
  244. perror("dup2");
  245. return 1;
  246. }
  247. }
  248. }
  249. fds.collect();
  250. int rc = execvp(argv[0], const_cast<char* const*>(argv.data()));
  251. if (rc < 0) {
  252. perror("execvp");
  253. exit(1);
  254. }
  255. ASSERT_NOT_REACHED();
  256. }
  257. children.append(child);
  258. }
  259. #ifdef SH_DEBUG
  260. dbgprintf("Closing fds in shell process:\n");
  261. #endif
  262. fds.collect();
  263. #ifdef SH_DEBUG
  264. dbgprintf("Now we gotta wait on children:\n");
  265. for (auto& child : children)
  266. dbgprintf(" %d\n", child);
  267. #endif
  268. int wstatus = 0;
  269. int rc;
  270. for (auto& child : children) {
  271. do {
  272. rc = waitpid(child, &wstatus, 0);
  273. if (rc < 0 && errno != EINTR) {
  274. perror("waitpid");
  275. break;
  276. }
  277. } while(errno == EINTR);
  278. }
  279. // FIXME: Should I really have to tcsetpgrp() after my child has exited?
  280. // Is the terminal controlling pgrp really still the PGID of the dead process?
  281. tcsetpgrp(0, getpid());
  282. tcsetattr(0, TCSANOW, &trm);
  283. if (WIFEXITED(wstatus)) {
  284. if (WEXITSTATUS(wstatus) != 0)
  285. printf("Exited with status %d\n", WEXITSTATUS(wstatus));
  286. return WEXITSTATUS(wstatus);
  287. } else {
  288. if (WIFSIGNALED(wstatus)) {
  289. switch (WTERMSIG(wstatus)) {
  290. case SIGINT:
  291. printf("Interrupted\n");
  292. break;
  293. default:
  294. printf("Terminated by signal %d\n", WTERMSIG(wstatus));
  295. break;
  296. }
  297. } else {
  298. printf("Exited abnormally\n");
  299. return 1;
  300. }
  301. }
  302. return 0;
  303. }
  304. int main(int argc, char** argv)
  305. {
  306. g.uid = getuid();
  307. g.sid = setsid();
  308. tcsetpgrp(0, getpgrp());
  309. tcgetattr(0, &g.termios);
  310. {
  311. struct sigaction sa;
  312. sa.sa_handler = handle_sigint;
  313. sa.sa_flags = 0;
  314. sa.sa_mask = 0;
  315. int rc = sigaction(SIGINT, &sa, nullptr);
  316. assert(rc == 0);
  317. }
  318. int rc = gethostname(g.hostname, sizeof(g.hostname));
  319. if (rc < 0)
  320. perror("gethostname");
  321. rc = ttyname_r(0, g.ttyname, sizeof(g.ttyname));
  322. if (rc < 0)
  323. perror("ttyname_r");
  324. {
  325. auto* pw = getpwuid(getuid());
  326. if (pw) {
  327. g.username = pw->pw_name;
  328. g.home = pw->pw_dir;
  329. putenv(const_cast<char*>(String::format("HOME=%s", pw->pw_dir).characters()));
  330. }
  331. endpwent();
  332. }
  333. if (argc > 1 && !strcmp(argv[1], "-c")) {
  334. fprintf(stderr, "FIXME: Implement /bin/sh -c\n");
  335. return 1;
  336. }
  337. {
  338. auto* cwd = getcwd(nullptr, 0);
  339. g.cwd = cwd;
  340. free(cwd);
  341. }
  342. LineEditor editor;
  343. for (;;) {
  344. prompt();
  345. auto line = editor.get_line();
  346. if (line.is_empty())
  347. continue;
  348. run_command(line);
  349. editor.add_to_history(line);
  350. }
  351. return 0;
  352. }