Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "nacl_io/mount_node_tty.h" | 5 #include "nacl_io/mount_node_tty.h" |
| 6 | 6 |
| 7 #include <assert.h> | 7 #include <assert.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <signal.h> | |
| 9 #include <stdio.h> | 10 #include <stdio.h> |
| 10 #include <string.h> | 11 #include <string.h> |
| 12 #include <sys/ioctl.h> | |
| 13 #include <unistd.h> | |
| 11 | 14 |
| 12 #include <algorithm> | 15 #include <algorithm> |
| 13 | 16 |
| 17 #include "nacl_io/dbgprint.h" | |
| 14 #include "nacl_io/ioctl.h" | 18 #include "nacl_io/ioctl.h" |
| 15 #include "nacl_io/mount.h" | 19 #include "nacl_io/mount.h" |
| 16 #include "nacl_io/pepper_interface.h" | 20 #include "nacl_io/pepper_interface.h" |
| 17 #include "sdk_util/auto_lock.h" | 21 #include "sdk_util/auto_lock.h" |
| 18 | 22 |
| 19 #define CHECK_LFLAG(TERMIOS, FLAG) (TERMIOS .c_lflag & FLAG) | 23 #define CHECK_LFLAG(TERMIOS, FLAG) (TERMIOS .c_lflag & FLAG) |
| 20 | 24 |
| 21 #define IS_ECHO CHECK_LFLAG(termios_, ECHO) | 25 #define IS_ECHO CHECK_LFLAG(termios_, ECHO) |
| 22 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE) | 26 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE) |
| 23 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL) | 27 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL) |
| 24 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL) | 28 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL) |
| 25 #define IS_ICANON CHECK_LFLAG(termios_, ICANON) | 29 #define IS_ICANON CHECK_LFLAG(termios_, ICANON) |
| 26 | 30 |
| 27 namespace nacl_io { | 31 namespace nacl_io { |
| 28 | 32 |
| 29 MountNodeTty::MountNodeTty(Mount* mount) : MountNodeCharDevice(mount), | 33 MountNodeTty::MountNodeTty(Mount* mount) : MountNodeCharDevice(mount), |
| 30 is_readable_(false) { | 34 is_readable_(false), |
| 35 did_resize_(false), | |
| 36 rows_(10), | |
|
binji
2013/08/21 17:42:01
use consts. BTW, why 10?
Sam Clegg
2013/08/22 03:32:45
Done.
Changed default rows to 30. Is there a mor
binji
2013/08/22 18:09:36
25 or 50, is what I remember, but I guess it proba
| |
| 37 cols_(80) { | |
| 31 pthread_cond_init(&is_readable_cond_, NULL); | 38 pthread_cond_init(&is_readable_cond_, NULL); |
| 32 InitTermios(); | 39 InitTermios(); |
| 33 } | 40 } |
| 34 | 41 |
| 35 void MountNodeTty::InitTermios() { | 42 void MountNodeTty::InitTermios() { |
| 36 // Some sane values that produce good result. | 43 // Some sane values that produce good result. |
| 37 termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8; | 44 termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8; |
| 38 termios_.c_oflag = OPOST | ONLCR; | 45 termios_.c_oflag = OPOST | ONLCR; |
| 39 termios_.c_cflag = CREAD | 077; | 46 termios_.c_cflag = CREAD | 077; |
| 40 termios_.c_lflag = | 47 termios_.c_lflag = |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 102 uint32_t len = static_cast<uint32_t>(message.size()); | 109 uint32_t len = static_cast<uint32_t>(message.size()); |
| 103 struct PP_Var val = var_intr->VarFromUtf8(message.data(), len); | 110 struct PP_Var val = var_intr->VarFromUtf8(message.data(), len); |
| 104 msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); | 111 msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); |
| 105 var_intr->Release(val); | 112 var_intr->Release(val); |
| 106 *out_bytes = count; | 113 *out_bytes = count; |
| 107 return 0; | 114 return 0; |
| 108 } | 115 } |
| 109 | 116 |
| 110 Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { | 117 Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { |
| 111 AUTO_LOCK(node_lock_); | 118 AUTO_LOCK(node_lock_); |
| 119 did_resize_ = false; | |
| 112 while (!is_readable_) { | 120 while (!is_readable_) { |
| 113 pthread_cond_wait(&is_readable_cond_, node_lock_.mutex()); | 121 pthread_cond_wait(&is_readable_cond_, node_lock_.mutex()); |
| 122 if (!is_readable_ && did_resize_) { | |
| 123 // If an async resize event occured then return the failure and | |
| 124 // set EINTR. | |
| 125 *out_bytes = 0; | |
| 126 return EINTR; | |
| 127 } | |
| 114 } | 128 } |
| 115 | 129 |
| 116 size_t bytes_to_copy = std::min(count, input_buffer_.size()); | 130 size_t bytes_to_copy = std::min(count, input_buffer_.size()); |
| 117 | 131 |
| 118 if (IS_ICANON) { | 132 if (IS_ICANON) { |
| 119 // Only read up to (and including) the first newline | 133 // Only read up to (and including) the first newline |
| 120 std::deque<char>::iterator nl = std::find(input_buffer_.begin(), | 134 std::deque<char>::iterator nl = std::find(input_buffer_.begin(), |
| 121 input_buffer_.end(), | 135 input_buffer_.end(), |
| 122 '\n'); | 136 '\n'); |
| 123 | 137 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 155 if (error != 0 || wrote != count) { | 169 if (error != 0 || wrote != count) { |
| 156 // TOOD(sbc): Do something more useful in response to a | 170 // TOOD(sbc): Do something more useful in response to a |
| 157 // failure to echo. | 171 // failure to echo. |
| 158 return error; | 172 return error; |
| 159 } | 173 } |
| 160 | 174 |
| 161 return 0; | 175 return 0; |
| 162 } | 176 } |
| 163 | 177 |
| 164 Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { | 178 Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { |
| 165 AUTO_LOCK(node_lock_); | 179 sdk_util::AutoLock lock(node_lock_); |
| 166 if (message->length < prefix_.size() || | 180 if (message->length < prefix_.size() || |
| 167 strncmp(message->buffer, prefix_.data(), prefix_.size()) != 0) { | 181 strncmp(message->buffer, prefix_.data(), prefix_.size()) != 0) { |
| 168 return ENOTTY; | 182 return ENOTTY; |
| 169 } | 183 } |
| 170 | 184 |
| 171 const char* buffer = message->buffer + prefix_.size(); | 185 const char* buffer = message->buffer + prefix_.size(); |
| 172 int num_bytes = message->length - prefix_.size(); | 186 int num_bytes = message->length - prefix_.size(); |
| 173 | 187 |
| 188 if (!strncmp(buffer, "resize:", 7)) { | |
| 189 num_bytes -= 7; | |
| 190 buffer += 7; | |
| 191 | |
| 192 struct winsize size; | |
| 193 int rtn = sscanf(buffer, "%hdx%hd", &size.ws_col, &size.ws_row); | |
| 194 if (rtn != 2) { | |
| 195 dbgprintf("error parsing resize: '%s' %d\n", buffer, rtn); | |
| 196 return ENOTTY; | |
| 197 } | |
| 198 lock.Unlock(); | |
| 199 //dbgprintf("got resize from JS\n"); | |
| 200 this->Ioctl(TIOCSWINSZ, reinterpret_cast<char*>(&size)); | |
| 201 return 0; | |
| 202 } else if (!strncmp(buffer, "input:", 6)) { | |
| 203 num_bytes -= 6; | |
| 204 buffer += 6; | |
| 205 } else { | |
| 206 dbgprintf("error parsing input: '%s'\n", message->buffer); | |
| 207 return ENOTTY; | |
| 208 } | |
| 209 | |
| 174 for (int i = 0; i < num_bytes; i++) { | 210 for (int i = 0; i < num_bytes; i++) { |
| 175 char c = buffer[i]; | 211 char c = buffer[i]; |
| 176 // Transform characters according to input flags. | 212 // Transform characters according to input flags. |
| 177 if (c == '\r') { | 213 if (c == '\r') { |
| 178 if (termios_.c_iflag & IGNCR) | 214 if (termios_.c_iflag & IGNCR) |
| 179 continue; | 215 continue; |
| 180 if (termios_.c_iflag & ICRNL) | 216 if (termios_.c_iflag & ICRNL) |
| 181 c = '\n'; | 217 c = '\n'; |
| 182 } else if (c == '\n') { | 218 } else if (c == '\n') { |
| 183 if (termios_.c_iflag & INLCR) | 219 if (termios_.c_iflag & INLCR) |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 230 } | 266 } |
| 231 | 267 |
| 232 if (is_readable_) { | 268 if (is_readable_) { |
| 233 RaiseEvent(POLLIN); | 269 RaiseEvent(POLLIN); |
| 234 pthread_cond_broadcast(&is_readable_cond_); | 270 pthread_cond_broadcast(&is_readable_cond_); |
| 235 } | 271 } |
| 236 return 0; | 272 return 0; |
| 237 } | 273 } |
| 238 | 274 |
| 239 Error MountNodeTty::Ioctl(int request, char* arg) { | 275 Error MountNodeTty::Ioctl(int request, char* arg) { |
| 240 if (request == TIOCNACLPREFIX) { | 276 switch (request) { |
| 241 // This ioctl is used to change the prefix for this tty node. | 277 case TIOCNACLPREFIX: { |
| 242 // The prefix is used to distinguish messages intended for this | 278 // This ioctl is used to change the prefix for this tty node. |
| 243 // tty node from all the other messages cluttering up the | 279 // The prefix is used to distinguish messages intended for this |
| 244 // javascript postMessage() channel. | 280 // tty node from all the other messages cluttering up the |
| 245 AUTO_LOCK(node_lock_); | 281 // javascript postMessage() channel. |
| 246 prefix_ = arg; | 282 AUTO_LOCK(node_lock_); |
| 247 return 0; | 283 prefix_ = arg; |
| 248 } else if (request == TIOCNACLINPUT) { | 284 return 0; |
| 249 // This ioctl is used to deliver data from the user to this tty node's | 285 } |
| 250 // input buffer. We check if the prefix in the input data matches the | 286 case TIOCNACLINPUT: { |
| 251 // prefix for this node, and only deliver the data if so. | 287 // This ioctl is used to deliver data from the user to this tty node's |
| 252 struct tioc_nacl_input_string* message = | 288 // input buffer. We check if the prefix in the input data matches the |
| 253 reinterpret_cast<struct tioc_nacl_input_string*>(arg); | 289 // prefix for this node, and only deliver the data if so. |
| 254 return ProcessInput(message); | 290 struct tioc_nacl_input_string* message = |
| 255 } else { | 291 reinterpret_cast<struct tioc_nacl_input_string*>(arg); |
| 256 return EINVAL; | 292 return ProcessInput(message); |
| 293 } | |
| 294 case TIOCSWINSZ: { | |
| 295 struct winsize* size = reinterpret_cast<struct winsize*>(arg); | |
| 296 { | |
| 297 AUTO_LOCK(node_lock_); | |
| 298 rows_ = size->ws_row; | |
| 299 cols_ = size->ws_col; | |
| 300 } | |
| 301 //dbgprintf("tty TIOCSWINSZ: %d %d\n", cols_, rows_); | |
| 302 kill(getpid(), SIGWINCH); | |
| 303 | |
| 304 // Wake up any thread waiting on Read | |
| 305 { | |
| 306 AUTO_LOCK(node_lock_); | |
| 307 did_resize_ = true; | |
| 308 pthread_cond_broadcast(&is_readable_cond_); | |
| 309 } | |
| 310 return 0; | |
| 311 } | |
| 312 case TIOCGWINSZ: { | |
| 313 struct winsize* size = reinterpret_cast<struct winsize*>(arg); | |
| 314 size->ws_row = rows_; | |
| 315 size->ws_col = cols_; | |
| 316 //dbgprintf("tty TIOCGWINSZ: %d %d\n", cols_, rows_); | |
| 317 return 0; | |
| 318 } | |
| 257 } | 319 } |
| 320 return EINVAL; | |
| 258 } | 321 } |
| 259 | 322 |
| 260 Error MountNodeTty::Tcgetattr(struct termios* termios_p) { | 323 Error MountNodeTty::Tcgetattr(struct termios* termios_p) { |
| 324 //dbgprintf("tcgetattr\n"); | |
| 261 AUTO_LOCK(node_lock_); | 325 AUTO_LOCK(node_lock_); |
| 262 *termios_p = termios_; | 326 *termios_p = termios_; |
| 263 return 0; | 327 return 0; |
| 264 } | 328 } |
| 265 | 329 |
| 266 Error MountNodeTty::Tcsetattr(int optional_actions, | 330 Error MountNodeTty::Tcsetattr(int optional_actions, |
| 267 const struct termios *termios_p) { | 331 const struct termios *termios_p) { |
| 332 //dbgprintf("tcsetattr\n"); | |
| 268 AUTO_LOCK(node_lock_); | 333 AUTO_LOCK(node_lock_); |
| 269 termios_ = *termios_p; | 334 termios_ = *termios_p; |
| 270 return 0; | 335 return 0; |
| 271 } | 336 } |
| 272 | 337 |
| 273 } | 338 } |
| OLD | NEW |