| 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 |
| 14 #include "nacl_io/ioctl.h" | 17 #include "nacl_io/ioctl.h" |
| 15 #include "nacl_io/mount.h" | 18 #include "nacl_io/mount.h" |
| 16 #include "nacl_io/pepper_interface.h" | 19 #include "nacl_io/pepper_interface.h" |
| 17 #include "sdk_util/auto_lock.h" | 20 #include "sdk_util/auto_lock.h" |
| 18 | 21 |
| 19 #define CHECK_LFLAG(TERMIOS, FLAG) (TERMIOS .c_lflag & FLAG) | 22 #define CHECK_LFLAG(TERMIOS, FLAG) (TERMIOS .c_lflag & FLAG) |
| 20 | 23 |
| 21 #define IS_ECHO CHECK_LFLAG(termios_, ECHO) | 24 #define IS_ECHO CHECK_LFLAG(termios_, ECHO) |
| 22 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE) | 25 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE) |
| 23 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL) | 26 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL) |
| 24 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL) | 27 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL) |
| 25 #define IS_ICANON CHECK_LFLAG(termios_, ICANON) | 28 #define IS_ICANON CHECK_LFLAG(termios_, ICANON) |
| 26 | 29 |
| 30 #define DEFAULT_TTY_COLS 80 |
| 31 #define DEFAULT_TTY_ROWS 30 |
| 32 |
| 27 namespace nacl_io { | 33 namespace nacl_io { |
| 28 | 34 |
| 29 MountNodeTty::MountNodeTty(Mount* mount) : MountNodeCharDevice(mount), | 35 MountNodeTty::MountNodeTty(Mount* mount) : MountNodeCharDevice(mount), |
| 30 is_readable_(false) { | 36 is_readable_(false), |
| 37 did_resize_(false), |
| 38 rows_(DEFAULT_TTY_ROWS), |
| 39 cols_(DEFAULT_TTY_COLS) { |
| 40 output_handler_.handler = NULL; |
| 31 pthread_cond_init(&is_readable_cond_, NULL); | 41 pthread_cond_init(&is_readable_cond_, NULL); |
| 32 InitTermios(); | 42 InitTermios(); |
| 33 } | 43 } |
| 34 | 44 |
| 35 void MountNodeTty::InitTermios() { | 45 void MountNodeTty::InitTermios() { |
| 36 // Some sane values that produce good result. | 46 // Some sane values that produce good result. |
| 37 termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8; | 47 termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8; |
| 38 termios_.c_oflag = OPOST | ONLCR; | 48 termios_.c_oflag = OPOST | ONLCR; |
| 39 termios_.c_cflag = CREAD | 077; | 49 termios_.c_cflag = CREAD | 077; |
| 40 termios_.c_lflag = | 50 termios_.c_lflag = |
| (...skipping 20 matching lines...) Expand all Loading... |
| 61 } | 71 } |
| 62 | 72 |
| 63 MountNodeTty::~MountNodeTty() { | 73 MountNodeTty::~MountNodeTty() { |
| 64 pthread_cond_destroy(&is_readable_cond_); | 74 pthread_cond_destroy(&is_readable_cond_); |
| 65 } | 75 } |
| 66 | 76 |
| 67 Error MountNodeTty::Write(size_t offs, | 77 Error MountNodeTty::Write(size_t offs, |
| 68 const void* buf, | 78 const void* buf, |
| 69 size_t count, | 79 size_t count, |
| 70 int* out_bytes) { | 80 int* out_bytes) { |
| 71 return Write(offs, buf, count, out_bytes, false); | 81 AUTO_LOCK(output_lock_); |
| 72 } | |
| 73 | |
| 74 Error MountNodeTty::Write(size_t offs, | |
| 75 const void* buf, | |
| 76 size_t count, | |
| 77 int* out_bytes, | |
| 78 bool locked) { | |
| 79 *out_bytes = 0; | 82 *out_bytes = 0; |
| 80 | 83 |
| 81 if (!mount_->ppapi()) | 84 // No handler registered. |
| 82 return ENOSYS; | 85 if (output_handler_.handler == NULL) |
| 86 return EIO; |
| 83 | 87 |
| 84 MessagingInterface* msg_intr = mount_->ppapi()->GetMessagingInterface(); | 88 int rtn = output_handler_.handler(static_cast<const char*>(buf), |
| 85 VarInterface* var_intr = mount_->ppapi()->GetVarInterface(); | 89 count, |
| 90 output_handler_.user_data); |
| 86 | 91 |
| 87 if (!(var_intr && msg_intr)) | 92 // Negative return value means an error occured and the return |
| 88 return ENOSYS; | 93 // value is a negated errno value. |
| 94 if (rtn < 0) |
| 95 return -rtn; |
| 89 | 96 |
| 90 // We append the prefix_ to the data in buf, then package it up | 97 *out_bytes = rtn; |
| 91 // and post it as a message. | |
| 92 const char* data = static_cast<const char*>(buf); | |
| 93 std::string message; | |
| 94 if (locked) { | |
| 95 message = prefix_; | |
| 96 } else { | |
| 97 AUTO_LOCK(node_lock_); | |
| 98 message = prefix_; | |
| 99 } | |
| 100 | |
| 101 message.append(data, count); | |
| 102 uint32_t len = static_cast<uint32_t>(message.size()); | |
| 103 struct PP_Var val = var_intr->VarFromUtf8(message.data(), len); | |
| 104 msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); | |
| 105 var_intr->Release(val); | |
| 106 *out_bytes = count; | |
| 107 return 0; | 98 return 0; |
| 108 } | 99 } |
| 109 | 100 |
| 110 Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { | 101 Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { |
| 111 AUTO_LOCK(node_lock_); | 102 AUTO_LOCK(node_lock_); |
| 103 did_resize_ = false; |
| 112 while (!is_readable_) { | 104 while (!is_readable_) { |
| 113 pthread_cond_wait(&is_readable_cond_, node_lock_.mutex()); | 105 pthread_cond_wait(&is_readable_cond_, node_lock_.mutex()); |
| 106 if (!is_readable_ && did_resize_) { |
| 107 // If an async resize event occured then return the failure and |
| 108 // set EINTR. |
| 109 *out_bytes = 0; |
| 110 return EINTR; |
| 111 } |
| 114 } | 112 } |
| 115 | 113 |
| 116 size_t bytes_to_copy = std::min(count, input_buffer_.size()); | 114 size_t bytes_to_copy = std::min(count, input_buffer_.size()); |
| 117 | 115 |
| 118 if (IS_ICANON) { | 116 if (IS_ICANON) { |
| 119 // Only read up to (and including) the first newline | 117 // Only read up to (and including) the first newline |
| 120 std::deque<char>::iterator nl = std::find(input_buffer_.begin(), | 118 std::deque<char>::iterator nl = std::find(input_buffer_.begin(), |
| 121 input_buffer_.end(), | 119 input_buffer_.end(), |
| 122 '\n'); | 120 '\n'); |
| 123 | 121 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 144 std::find(input_buffer_.begin(), | 142 std::find(input_buffer_.begin(), |
| 145 input_buffer_.end(), '\n') != input_buffer_.end(); | 143 input_buffer_.end(), '\n') != input_buffer_.end(); |
| 146 else | 144 else |
| 147 is_readable_ = input_buffer_.size() > 0; | 145 is_readable_ = input_buffer_.size() > 0; |
| 148 | 146 |
| 149 return 0; | 147 return 0; |
| 150 } | 148 } |
| 151 | 149 |
| 152 Error MountNodeTty::Echo(const char* string, int count) { | 150 Error MountNodeTty::Echo(const char* string, int count) { |
| 153 int wrote; | 151 int wrote; |
| 154 Error error = Write(0, string, count, &wrote, true); | 152 Error error = Write(0, string, count, &wrote); |
| 155 if (error != 0 || wrote != count) { | 153 if (error != 0 || wrote != count) { |
| 156 // TOOD(sbc): Do something more useful in response to a | 154 // TOOD(sbc): Do something more useful in response to a |
| 157 // failure to echo. | 155 // failure to echo. |
| 158 return error; | 156 return error; |
| 159 } | 157 } |
| 160 | 158 |
| 161 return 0; | 159 return 0; |
| 162 } | 160 } |
| 163 | 161 |
| 164 Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { | 162 Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { |
| 165 AUTO_LOCK(node_lock_); | 163 AUTO_LOCK(node_lock_); |
| 166 if (message->length < prefix_.size() || | |
| 167 strncmp(message->buffer, prefix_.data(), prefix_.size()) != 0) { | |
| 168 return ENOTTY; | |
| 169 } | |
| 170 | 164 |
| 171 const char* buffer = message->buffer + prefix_.size(); | 165 const char* buffer = message->buffer; |
| 172 int num_bytes = message->length - prefix_.size(); | 166 size_t num_bytes = message->length; |
| 173 | 167 |
| 174 for (int i = 0; i < num_bytes; i++) { | 168 for (size_t i = 0; i < num_bytes; i++) { |
| 175 char c = buffer[i]; | 169 char c = buffer[i]; |
| 176 // Transform characters according to input flags. | 170 // Transform characters according to input flags. |
| 177 if (c == '\r') { | 171 if (c == '\r') { |
| 178 if (termios_.c_iflag & IGNCR) | 172 if (termios_.c_iflag & IGNCR) |
| 179 continue; | 173 continue; |
| 180 if (termios_.c_iflag & ICRNL) | 174 if (termios_.c_iflag & ICRNL) |
| 181 c = '\n'; | 175 c = '\n'; |
| 182 } else if (c == '\n') { | 176 } else if (c == '\n') { |
| 183 if (termios_.c_iflag & INLCR) | 177 if (termios_.c_iflag & INLCR) |
| 184 c = '\r'; | 178 c = '\r'; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 input_buffer_.push_back(c); | 220 input_buffer_.push_back(c); |
| 227 | 221 |
| 228 if (c == '\n' || c == termios_.c_cc[VEOF] || !IS_ICANON) | 222 if (c == '\n' || c == termios_.c_cc[VEOF] || !IS_ICANON) |
| 229 is_readable_ = true; | 223 is_readable_ = true; |
| 230 } | 224 } |
| 231 | 225 |
| 232 if (is_readable_) { | 226 if (is_readable_) { |
| 233 RaiseEvent(POLLIN); | 227 RaiseEvent(POLLIN); |
| 234 pthread_cond_broadcast(&is_readable_cond_); | 228 pthread_cond_broadcast(&is_readable_cond_); |
| 235 } | 229 } |
| 230 |
| 236 return 0; | 231 return 0; |
| 237 } | 232 } |
| 238 | 233 |
| 239 Error MountNodeTty::Ioctl(int request, char* arg) { | 234 Error MountNodeTty::Ioctl(int request, char* arg) { |
| 240 if (request == TIOCNACLPREFIX) { | 235 switch (request) { |
| 241 // This ioctl is used to change the prefix for this tty node. | 236 case TIOCNACLOUTPUT: { |
| 242 // The prefix is used to distinguish messages intended for this | 237 AUTO_LOCK(output_lock_); |
| 243 // tty node from all the other messages cluttering up the | 238 if (arg == NULL) { |
| 244 // javascript postMessage() channel. | 239 output_handler_.handler = NULL; |
| 245 AUTO_LOCK(node_lock_); | 240 return 0; |
| 246 prefix_ = arg; | 241 } |
| 247 return 0; | 242 if (output_handler_.handler != NULL) |
| 248 } else if (request == TIOCNACLINPUT) { | 243 return EALREADY; |
| 249 // This ioctl is used to deliver data from the user to this tty node's | 244 output_handler_ = *reinterpret_cast<tioc_nacl_output*>(arg); |
| 250 // input buffer. We check if the prefix in the input data matches the | 245 return 0; |
| 251 // prefix for this node, and only deliver the data if so. | 246 } |
| 252 struct tioc_nacl_input_string* message = | 247 case TIOCNACLINPUT: { |
| 253 reinterpret_cast<struct tioc_nacl_input_string*>(arg); | 248 // This ioctl is used to deliver data from the user to this tty node's |
| 254 return ProcessInput(message); | 249 // input buffer. |
| 255 } else { | 250 struct tioc_nacl_input_string* message = |
| 256 return EINVAL; | 251 reinterpret_cast<struct tioc_nacl_input_string*>(arg); |
| 252 return ProcessInput(message); |
| 253 } |
| 254 case TIOCSWINSZ: { |
| 255 struct winsize* size = reinterpret_cast<struct winsize*>(arg); |
| 256 { |
| 257 AUTO_LOCK(node_lock_); |
| 258 rows_ = size->ws_row; |
| 259 cols_ = size->ws_col; |
| 260 } |
| 261 kill(getpid(), SIGWINCH); |
| 262 |
| 263 // Wake up any thread waiting on Read |
| 264 { |
| 265 AUTO_LOCK(node_lock_); |
| 266 did_resize_ = true; |
| 267 pthread_cond_broadcast(&is_readable_cond_); |
| 268 } |
| 269 return 0; |
| 270 } |
| 271 case TIOCGWINSZ: { |
| 272 struct winsize* size = reinterpret_cast<struct winsize*>(arg); |
| 273 size->ws_row = rows_; |
| 274 size->ws_col = cols_; |
| 275 return 0; |
| 276 } |
| 257 } | 277 } |
| 278 |
| 279 return EINVAL; |
| 258 } | 280 } |
| 259 | 281 |
| 260 Error MountNodeTty::Tcgetattr(struct termios* termios_p) { | 282 Error MountNodeTty::Tcgetattr(struct termios* termios_p) { |
| 261 AUTO_LOCK(node_lock_); | 283 AUTO_LOCK(node_lock_); |
| 262 *termios_p = termios_; | 284 *termios_p = termios_; |
| 263 return 0; | 285 return 0; |
| 264 } | 286 } |
| 265 | 287 |
| 266 Error MountNodeTty::Tcsetattr(int optional_actions, | 288 Error MountNodeTty::Tcsetattr(int optional_actions, |
| 267 const struct termios *termios_p) { | 289 const struct termios *termios_p) { |
| 268 AUTO_LOCK(node_lock_); | 290 AUTO_LOCK(node_lock_); |
| 269 termios_ = *termios_p; | 291 termios_ = *termios_p; |
| 270 return 0; | 292 return 0; |
| 271 } | 293 } |
| 272 | 294 |
| 273 } | 295 } |
| OLD | NEW |