13 // Because we can't pass a parameter to the signal handler, we can't really
14 // handle more than one terminal in our process without adding extra mechanism.
15 bool Terminal::child_died
= false;
18 Terminal::Terminal(History
* history_in
)
19 : history(history_in
), child_pid(0)
21 buffer
= (char*) malloc(BUFSIZ
);
22 prebuffered_bytes
= 0;
25 int result
= openpty(&terminal_fd
, &child_fd
, NULL
, NULL
, NULL
);
27 throw std::runtime_error("openpty() failed");
31 throw std::runtime_error("fork() failed");
33 // We're in the child.
34 setsid(); // Create a new process group.
38 if (ioctl(child_fd
, TIOCSCTTY
, NULL
) < 0)
39 throw std::runtime_error("ioctl(TIOCSCTTY) failed");
47 struct sigaction sigchld_action
;
48 memset(&sigchld_action
, 0, sizeof(sigchld_action
));
49 sigchld_action
.sa_handler
= sigchld_received
;
50 sigaction(SIGCHLD
, &sigchld_action
, NULL
);
61 bool Terminal::is_done()
69 // We assume this won't be called unless select() has indicated there is
77 read(terminal_fd
, buffer
+ prebuffered_bytes
, BUFSIZ
- prebuffered_bytes
);
82 else if (result
< 0) {
83 // Wait a moment before checking child_died again.
84 struct timespec sleep_spec
= { 0, 1000000 };
85 nanosleep(&sleep_spec
, nullptr);
87 // This is a race condition; the child died *while* we were reading.
90 throw std::runtime_error("read() failed");
93 // Give it to the History.
94 int length
= prebuffered_bytes
+ result
;
95 int bytes_consumed
= history
->add_input(buffer
, length
);
96 length
-= bytes_consumed
;
98 if (length
== BUFSIZ
) {
99 // The history didn't consume *anything* -- probably it hit an
100 // unterminated escape sequence. Just discard the whole buffer. It's
101 // not a great solution, but it'll keep us from getting wedged.
105 memmove(buffer
, buffer
+ bytes_consumed
, length
);
107 prebuffered_bytes
= length
;
111 void Terminal::send(const char* data
, int length
)
114 length
= strlen(data
);
115 // st makes sure not to send more than 256 bytes at a time, because it might
116 // be connected to a modem. We don't bother with that (currently).
118 ssize_t bytes_written
= write(terminal_fd
, data
, length
);
119 if (bytes_written
< 0)
120 throw std::runtime_error("write() failed");
121 data
+= bytes_written
;
122 length
-= bytes_written
;
127 void Terminal::hang_up()
130 kill(child_pid
, SIGHUP
);
134 void Terminal::notify_resize(int columns
, int rows
, int pixel_width
, int pixel_height
)
136 struct winsize window_size
;
137 window_size
.ws_row
= rows
;
138 window_size
.ws_col
= columns
;
139 window_size
.ws_xpixel
= pixel_width
;
140 window_size
.ws_ypixel
= pixel_height
;
141 ioctl(terminal_fd
, TIOCSWINSZ
, &window_size
);
147 // Alles cool, nothing to do.
150 void Terminal::exec_shell()
152 const struct passwd
* user_info
= getpwuid(getuid());
153 if (user_info
== NULL
)
154 throw std::runtime_error("Couldn't get user info");
156 // Which shell to use.
157 const char* shell_path
= getenv("SHELL");
158 if (shell_path
== NULL
) {
159 if (user_info
->pw_shell
[0])
160 shell_path
= user_info
->pw_shell
;
162 shell_path
= "/bin/bash";
169 setenv("LOGNAME", user_info
->pw_name
, true);
170 setenv("USER", user_info
->pw_name
, true);
171 setenv("SHELL", shell_path
, true);
172 setenv("HOME", user_info
->pw_dir
, true);
173 setenv("TERM", settings
.term_name
.c_str(), true);
174 setenv("SPFT", "true", true);
177 signal(SIGCHLD
, SIG_DFL
);
178 signal(SIGHUP
, SIG_DFL
);
179 signal(SIGINT
, SIG_DFL
);
180 signal(SIGQUIT
, SIG_DFL
);
181 signal(SIGTERM
, SIG_DFL
);
182 signal(SIGALRM
, SIG_DFL
);
184 // Working directory.
185 if (!settings
.working_directory
.empty()) {
186 std::string path
= settings
.working_directory
;
188 path
= settings
.home_path() + path
.substr(1);
189 int result
= chdir(path
.c_str());
195 char* args
[] = { (char*) shell_path
, 0 };
196 execvp(shell_path
, args
);
200 void Terminal::sigchld_received(int signal_number
)