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