TerminalWrapper.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #include "TerminalWrapper.h"
  2. #include "ProcessStateWidget.h"
  3. #include <AK/String.h>
  4. #include <LibCore/CConfigFile.h>
  5. #include <LibGUI/GBoxLayout.h>
  6. #include <LibGUI/GMessageBox.h>
  7. #include <LibVT/TerminalWidget.h>
  8. #include <fcntl.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <sys/ioctl.h>
  12. #include <sys/wait.h>
  13. #include <unistd.h>
  14. void TerminalWrapper::run_command(const String& command)
  15. {
  16. if (m_pid != -1) {
  17. GMessageBox::show(
  18. "A command is already running in this TerminalWrapper",
  19. "Can't run command",
  20. GMessageBox::Type::Error,
  21. GMessageBox::InputType::OK,
  22. window());
  23. return;
  24. }
  25. int ptm_fd = open("/dev/ptmx", O_RDWR);
  26. if (ptm_fd < 0) {
  27. perror("open(ptmx)");
  28. ASSERT_NOT_REACHED();
  29. }
  30. m_terminal_widget->set_pty_master_fd(ptm_fd);
  31. m_terminal_widget->on_command_exit = [this] {
  32. int wstatus;
  33. int rc = waitpid(m_pid, &wstatus, 0);
  34. if (rc < 0) {
  35. perror("waitpid");
  36. ASSERT_NOT_REACHED();
  37. }
  38. if (WIFEXITED(wstatus)) {
  39. m_terminal_widget->inject_string(String::format("\033[%d;1m(Command exited with code %d)\033[0m\n", wstatus == 0 ? 32 : 31, WEXITSTATUS(wstatus)));
  40. } else if (WIFSTOPPED(wstatus)) {
  41. m_terminal_widget->inject_string(String::format("\033[34;1m(Command stopped!)\033[0m\n"));
  42. } else if (WIFSIGNALED(wstatus)) {
  43. m_terminal_widget->inject_string(String::format("\033[34;1m(Command signaled with %s!)\033[0m\n", strsignal(WTERMSIG(wstatus))));
  44. }
  45. m_process_state_widget->set_tty_fd(-1);
  46. m_pid = -1;
  47. };
  48. m_pid = fork();
  49. if (m_pid == 0) {
  50. const char* tty_name = ptsname(ptm_fd);
  51. if (!tty_name) {
  52. perror("ptsname");
  53. exit(1);
  54. }
  55. close(ptm_fd);
  56. int pts_fd = open(tty_name, O_RDWR);
  57. if (pts_fd < 0) {
  58. perror("open");
  59. exit(1);
  60. }
  61. // NOTE: It's okay if this fails.
  62. (void)ioctl(0, TIOCNOTTY);
  63. close(0);
  64. close(1);
  65. close(2);
  66. int rc = dup2(pts_fd, 0);
  67. if (rc < 0) {
  68. perror("dup2");
  69. exit(1);
  70. }
  71. rc = dup2(pts_fd, 1);
  72. if (rc < 0) {
  73. perror("dup2");
  74. exit(1);
  75. }
  76. rc = dup2(pts_fd, 2);
  77. if (rc < 0) {
  78. perror("dup2");
  79. exit(1);
  80. }
  81. rc = close(pts_fd);
  82. if (rc < 0) {
  83. perror("close");
  84. exit(1);
  85. }
  86. rc = ioctl(0, TIOCSCTTY);
  87. if (rc < 0) {
  88. perror("ioctl(TIOCSCTTY)");
  89. exit(1);
  90. }
  91. const char* args[4] = { "/bin/Shell", nullptr, nullptr, nullptr };
  92. if (!command.is_empty()) {
  93. args[1] = "-c";
  94. args[2] = command.characters();
  95. }
  96. const char* envs[] = { "TERM=xterm", "PATH=/bin:/usr/bin:/usr/local/bin", nullptr };
  97. rc = execve("/bin/Shell", const_cast<char**>(args), const_cast<char**>(envs));
  98. if (rc < 0) {
  99. perror("execve");
  100. exit(1);
  101. }
  102. ASSERT_NOT_REACHED();
  103. }
  104. // Parent process, cont'd.
  105. m_process_state_widget->set_tty_fd(ptm_fd);
  106. }
  107. TerminalWrapper::TerminalWrapper(GWidget* parent)
  108. : GWidget(parent)
  109. {
  110. set_layout(make<GBoxLayout>(Orientation::Vertical));
  111. m_process_state_widget = ProcessStateWidget::construct(this);
  112. RefPtr<CConfigFile> config = CConfigFile::get_for_app("Terminal");
  113. m_terminal_widget = TerminalWidget::construct(-1, false, config);
  114. add_child(*m_terminal_widget);
  115. }
  116. TerminalWrapper::~TerminalWrapper()
  117. {
  118. }