| 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 <signal.h> |
| 10 #include <stdio.h> | 10 #include <stdio.h> |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE) | 25 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE) |
| 26 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL) | 26 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL) |
| 27 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL) | 27 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL) |
| 28 #define IS_ICANON CHECK_LFLAG(termios_, ICANON) | 28 #define IS_ICANON CHECK_LFLAG(termios_, ICANON) |
| 29 | 29 |
| 30 #define DEFAULT_TTY_COLS 80 | 30 #define DEFAULT_TTY_COLS 80 |
| 31 #define DEFAULT_TTY_ROWS 30 | 31 #define DEFAULT_TTY_ROWS 30 |
| 32 | 32 |
| 33 namespace nacl_io { | 33 namespace nacl_io { |
| 34 | 34 |
| 35 MountNodeTty::MountNodeTty(Mount* mount) : MountNodeCharDevice(mount), | 35 MountNodeTty::MountNodeTty(Mount* mount) |
| 36 is_readable_(false), | 36 : MountNodeCharDevice(mount), |
| 37 rows_(DEFAULT_TTY_ROWS), | 37 emitter_(new EventEmitter), |
| 38 cols_(DEFAULT_TTY_COLS) { | 38 rows_(DEFAULT_TTY_ROWS), |
| 39 cols_(DEFAULT_TTY_COLS) { |
| 39 output_handler_.handler = NULL; | 40 output_handler_.handler = NULL; |
| 40 pthread_cond_init(&is_readable_cond_, NULL); | |
| 41 InitTermios(); | 41 InitTermios(); |
| 42 |
| 43 // Output will never block |
| 44 emitter_->RaiseEvents_Locked(POLLOUT); |
| 42 } | 45 } |
| 43 | 46 |
| 44 void MountNodeTty::InitTermios() { | 47 void MountNodeTty::InitTermios() { |
| 45 // Some sane values that produce good result. | 48 // Some sane values that produce good result. |
| 46 termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8; | 49 termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8; |
| 47 termios_.c_oflag = OPOST | ONLCR; | 50 termios_.c_oflag = OPOST | ONLCR; |
| 48 termios_.c_cflag = CREAD | 077; | 51 termios_.c_cflag = CREAD | 077; |
| 49 termios_.c_lflag = | 52 termios_.c_lflag = |
| 50 ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; | 53 ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; |
| 51 termios_.c_ispeed = B38400; | 54 termios_.c_ispeed = B38400; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 62 termios_.c_cc[VSTOP] = 19; | 65 termios_.c_cc[VSTOP] = 19; |
| 63 termios_.c_cc[VSUSP] = 26; | 66 termios_.c_cc[VSUSP] = 26; |
| 64 termios_.c_cc[VEOL] = 0; | 67 termios_.c_cc[VEOL] = 0; |
| 65 termios_.c_cc[VREPRINT] = 18; | 68 termios_.c_cc[VREPRINT] = 18; |
| 66 termios_.c_cc[VDISCARD] = 15; | 69 termios_.c_cc[VDISCARD] = 15; |
| 67 termios_.c_cc[VWERASE] = 23; | 70 termios_.c_cc[VWERASE] = 23; |
| 68 termios_.c_cc[VLNEXT] = 22; | 71 termios_.c_cc[VLNEXT] = 22; |
| 69 termios_.c_cc[VEOL2] = 0; | 72 termios_.c_cc[VEOL2] = 0; |
| 70 } | 73 } |
| 71 | 74 |
| 72 MountNodeTty::~MountNodeTty() { | 75 EventEmitter* MountNodeTty::GetEventEmitter() { |
| 73 pthread_cond_destroy(&is_readable_cond_); | 76 return emitter_.get(); |
| 74 } | 77 } |
| 75 | 78 |
| 76 Error MountNodeTty::Write(size_t offs, | 79 Error MountNodeTty::Write(size_t offs, |
| 77 const void* buf, | 80 const void* buf, |
| 78 size_t count, | 81 size_t count, |
| 79 int* out_bytes) { | 82 int* out_bytes) { |
| 83 |
| 80 AUTO_LOCK(output_lock_); | 84 AUTO_LOCK(output_lock_); |
| 81 *out_bytes = 0; | 85 *out_bytes = 0; |
| 82 | 86 |
| 83 // No handler registered. | 87 // No handler registered. |
| 84 if (output_handler_.handler == NULL) | 88 if (output_handler_.handler == NULL) |
| 85 return EIO; | 89 return EIO; |
| 86 | 90 |
| 87 int rtn = output_handler_.handler(static_cast<const char*>(buf), | 91 int rtn = output_handler_.handler(static_cast<const char*>(buf), |
| 88 count, | 92 count, |
| 89 output_handler_.user_data); | 93 output_handler_.user_data); |
| 90 | 94 |
| 91 // Negative return value means an error occured and the return | 95 // Negative return value means an error occured and the return |
| 92 // value is a negated errno value. | 96 // value is a negated errno value. |
| 93 if (rtn < 0) | 97 if (rtn < 0) |
| 94 return -rtn; | 98 return -rtn; |
| 95 | 99 |
| 96 *out_bytes = rtn; | 100 *out_bytes = rtn; |
| 97 return 0; | 101 return 0; |
| 98 } | 102 } |
| 99 | 103 |
| 104 |
| 100 Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { | 105 Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { |
| 101 AUTO_LOCK(node_lock_); | 106 EventListenerLock wait(GetEventEmitter()); |
| 102 while (!is_readable_) { | 107 *out_bytes = 0; |
| 103 pthread_cond_wait(&is_readable_cond_, node_lock_.mutex()); | 108 |
| 104 } | 109 // If interrupted, return |
| 110 Error err = wait.WaitOnEvent(POLLIN, -1); |
| 111 if (err != 0) |
| 112 return err; |
| 105 | 113 |
| 106 size_t bytes_to_copy = std::min(count, input_buffer_.size()); | 114 size_t bytes_to_copy = std::min(count, input_buffer_.size()); |
| 107 | |
| 108 if (IS_ICANON) { | 115 if (IS_ICANON) { |
| 109 // Only read up to (and including) the first newline | 116 // Only read up to (and including) the first newline |
| 110 std::deque<char>::iterator nl = std::find(input_buffer_.begin(), | 117 std::deque<char>::iterator nl = std::find(input_buffer_.begin(), |
| 111 input_buffer_.end(), | 118 input_buffer_.end(), |
| 112 '\n'); | 119 '\n'); |
| 113 | 120 |
| 114 if (nl != input_buffer_.end()) { | 121 if (nl != input_buffer_.end()) { |
| 115 // We found a newline in the buffer, adjust bytes_to_copy accordingly | 122 // We found a newline in the buffer, adjust bytes_to_copy accordingly |
| 116 size_t line_len = static_cast<size_t>(nl - input_buffer_.begin()) + 1; | 123 size_t line_len = static_cast<size_t>(nl - input_buffer_.begin()) + 1; |
| 117 bytes_to_copy = std::min(bytes_to_copy, line_len); | 124 bytes_to_copy = std::min(bytes_to_copy, line_len); |
| 118 } | 125 } |
| 119 } | 126 } |
| 120 | 127 |
| 121 | 128 |
| 122 // Copies data from the input buffer into buf. | 129 // Copies data from the input buffer into buf. |
| 123 std::copy(input_buffer_.begin(), input_buffer_.begin() + bytes_to_copy, | 130 std::copy(input_buffer_.begin(), input_buffer_.begin() + bytes_to_copy, |
| 124 static_cast<char*>(buf)); | 131 static_cast<char*>(buf)); |
| 125 *out_bytes = bytes_to_copy; | 132 *out_bytes = bytes_to_copy; |
| 126 input_buffer_.erase(input_buffer_.begin(), | 133 input_buffer_.erase(input_buffer_.begin(), |
| 127 input_buffer_.begin() + bytes_to_copy); | 134 input_buffer_.begin() + bytes_to_copy); |
| 128 | 135 |
| 129 // mark input as no longer readable if we consumed | 136 // mark input as no longer readable if we consumed |
| 130 // the entire buffer or, in the case of buffered input, | 137 // the entire buffer or, in the case of buffered input, |
| 131 // we consumed the final \n char. | 138 // we consumed the final \n char. |
| 139 bool avail; |
| 132 if (IS_ICANON) | 140 if (IS_ICANON) |
| 133 is_readable_ = | 141 avail = std::find(input_buffer_.begin(), |
| 134 std::find(input_buffer_.begin(), | 142 input_buffer_.end(), '\n') != input_buffer_.end(); |
| 135 input_buffer_.end(), '\n') != input_buffer_.end(); | |
| 136 else | 143 else |
| 137 is_readable_ = input_buffer_.size() > 0; | 144 avail = input_buffer_.size() > 0; |
| 145 |
| 146 if (!avail) |
| 147 emitter_->ClearEvents_Locked(POLLIN); |
| 138 | 148 |
| 139 return 0; | 149 return 0; |
| 140 } | 150 } |
| 141 | 151 |
| 142 Error MountNodeTty::Echo(const char* string, int count) { | 152 Error MountNodeTty::Echo(const char* string, int count) { |
| 143 int wrote; | 153 int wrote; |
| 144 Error error = Write(0, string, count, &wrote); | 154 Error error = Write(0, string, count, &wrote); |
| 145 if (error != 0 || wrote != count) { | 155 if (error != 0 || wrote != count) { |
| 146 // TOOD(sbc): Do something more useful in response to a | 156 // TOOD(sbc): Do something more useful in response to a |
| 147 // failure to echo. | 157 // failure to echo. |
| 148 return error; | 158 return error; |
| 149 } | 159 } |
| 150 | 160 |
| 151 return 0; | 161 return 0; |
| 152 } | 162 } |
| 153 | 163 |
| 154 Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { | 164 Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { |
| 155 AUTO_LOCK(node_lock_); | 165 AUTO_LOCK(emitter_->GetLock()) |
| 156 | 166 |
| 157 const char* buffer = message->buffer; | 167 const char* buffer = message->buffer; |
| 158 size_t num_bytes = message->length; | 168 size_t num_bytes = message->length; |
| 159 | 169 |
| 160 for (size_t i = 0; i < num_bytes; i++) { | 170 for (size_t i = 0; i < num_bytes; i++) { |
| 161 char c = buffer[i]; | 171 char c = buffer[i]; |
| 162 // Transform characters according to input flags. | 172 // Transform characters according to input flags. |
| 163 if (c == '\r') { | 173 if (c == '\r') { |
| 164 if (termios_.c_iflag & IGNCR) | 174 if (termios_.c_iflag & IGNCR) |
| 165 continue; | 175 continue; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 } else { | 215 } else { |
| 206 Echo(&c, 1); | 216 Echo(&c, 1); |
| 207 } | 217 } |
| 208 } | 218 } |
| 209 } | 219 } |
| 210 | 220 |
| 211 if (!skip) | 221 if (!skip) |
| 212 input_buffer_.push_back(c); | 222 input_buffer_.push_back(c); |
| 213 | 223 |
| 214 if (c == '\n' || c == termios_.c_cc[VEOF] || !IS_ICANON) | 224 if (c == '\n' || c == termios_.c_cc[VEOF] || !IS_ICANON) |
| 215 is_readable_ = true; | 225 emitter_->RaiseEvents_Locked(POLLIN); |
| 216 } | |
| 217 | |
| 218 if (is_readable_) { | |
| 219 RaiseEvent(POLLIN); | |
| 220 pthread_cond_broadcast(&is_readable_cond_); | |
| 221 } | 226 } |
| 222 | 227 |
| 223 return 0; | 228 return 0; |
| 224 } | 229 } |
| 225 | 230 |
| 226 Error MountNodeTty::Ioctl(int request, char* arg) { | 231 Error MountNodeTty::Ioctl(int request, char* arg) { |
| 227 switch (request) { | 232 switch (request) { |
| 228 case TIOCNACLOUTPUT: { | 233 case TIOCNACLOUTPUT: { |
| 229 AUTO_LOCK(output_lock_); | 234 AUTO_LOCK(output_lock_); |
| 230 if (arg == NULL) { | 235 if (arg == NULL) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 244 return ProcessInput(message); | 249 return ProcessInput(message); |
| 245 } | 250 } |
| 246 case TIOCSWINSZ: { | 251 case TIOCSWINSZ: { |
| 247 struct winsize* size = reinterpret_cast<struct winsize*>(arg); | 252 struct winsize* size = reinterpret_cast<struct winsize*>(arg); |
| 248 { | 253 { |
| 249 AUTO_LOCK(node_lock_); | 254 AUTO_LOCK(node_lock_); |
| 250 rows_ = size->ws_row; | 255 rows_ = size->ws_row; |
| 251 cols_ = size->ws_col; | 256 cols_ = size->ws_col; |
| 252 } | 257 } |
| 253 kill(getpid(), SIGWINCH); | 258 kill(getpid(), SIGWINCH); |
| 259 { |
| 260 // Wake up any thread waiting on Read with POLLERR then immediate |
| 261 // clear it to signal EINTR. |
| 262 AUTO_LOCK(emitter_->GetLock()) |
| 263 emitter_->RaiseEvents_Locked(POLLERR); |
| 264 emitter_->ClearEvents_Locked(POLLERR); |
| 265 } |
| 254 return 0; | 266 return 0; |
| 255 } | 267 } |
| 256 case TIOCGWINSZ: { | 268 case TIOCGWINSZ: { |
| 257 struct winsize* size = reinterpret_cast<struct winsize*>(arg); | 269 struct winsize* size = reinterpret_cast<struct winsize*>(arg); |
| 258 size->ws_row = rows_; | 270 size->ws_row = rows_; |
| 259 size->ws_col = cols_; | 271 size->ws_col = cols_; |
| 260 return 0; | 272 return 0; |
| 261 } | 273 } |
| 262 } | 274 } |
| 263 | 275 |
| 264 return EINVAL; | 276 return EINVAL; |
| 265 } | 277 } |
| 266 | 278 |
| 267 Error MountNodeTty::Tcgetattr(struct termios* termios_p) { | 279 Error MountNodeTty::Tcgetattr(struct termios* termios_p) { |
| 268 AUTO_LOCK(node_lock_); | 280 AUTO_LOCK(node_lock_); |
| 269 *termios_p = termios_; | 281 *termios_p = termios_; |
| 270 return 0; | 282 return 0; |
| 271 } | 283 } |
| 272 | 284 |
| 273 Error MountNodeTty::Tcsetattr(int optional_actions, | 285 Error MountNodeTty::Tcsetattr(int optional_actions, |
| 274 const struct termios *termios_p) { | 286 const struct termios *termios_p) { |
| 275 AUTO_LOCK(node_lock_); | 287 AUTO_LOCK(node_lock_); |
| 276 termios_ = *termios_p; | 288 termios_ = *termios_p; |
| 277 return 0; | 289 return 0; |
| 278 } | 290 } |
| 279 | 291 |
| 280 } | 292 } |
| OLD | NEW |