ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / devfs / tty_node.cc
blob445d4857809b85aa009e9d939c9b88be5d06261a
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "nacl_io/devfs/tty_node.h"
7 #include <assert.h>
8 #include <errno.h>
9 #include <signal.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/ioctl.h>
13 #include <unistd.h>
15 #include <algorithm>
17 #include "nacl_io/filesystem.h"
18 #include "nacl_io/ioctl.h"
19 #include "nacl_io/kernel_handle.h"
20 #include "nacl_io/kernel_intercept.h"
21 #include "nacl_io/log.h"
22 #include "nacl_io/pepper_interface.h"
23 #include "sdk_util/auto_lock.h"
25 #define CHECK_LFLAG(TERMIOS, FLAG) (TERMIOS.c_lflag& FLAG)
27 #define IS_ECHO CHECK_LFLAG(termios_, ECHO)
28 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE)
29 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL)
30 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL)
31 #define IS_ICANON CHECK_LFLAG(termios_, ICANON)
33 #define DEFAULT_TTY_COLS 80
34 #define DEFAULT_TTY_ROWS 30
36 namespace nacl_io {
38 TtyNode::TtyNode(Filesystem* filesystem)
39 : CharNode(filesystem),
40 emitter_(new EventEmitter),
41 rows_(DEFAULT_TTY_ROWS),
42 cols_(DEFAULT_TTY_COLS) {
43 output_handler_.handler = NULL;
44 InitTermios();
46 // Output will never block
47 emitter_->RaiseEvents_Locked(POLLOUT);
50 void TtyNode::InitTermios() {
51 // Some sane values that produce good result.
52 termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8;
53 termios_.c_oflag = OPOST | ONLCR;
54 termios_.c_cflag = CREAD | 077;
55 termios_.c_lflag =
56 ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
57 #if !defined(__BIONIC__)
58 termios_.c_ispeed = B38400;
59 termios_.c_ospeed = B38400;
60 #endif
61 termios_.c_cc[VINTR] = 3;
62 termios_.c_cc[VQUIT] = 28;
63 termios_.c_cc[VERASE] = 127;
64 termios_.c_cc[VKILL] = 21;
65 termios_.c_cc[VEOF] = 4;
66 termios_.c_cc[VTIME] = 0;
67 termios_.c_cc[VMIN] = 1;
68 #if defined(VSWTC) /* Not defined on on Mac */
69 termios_.c_cc[VSWTC] = 0;
70 #endif
71 termios_.c_cc[VSTART] = 17;
72 termios_.c_cc[VSTOP] = 19;
73 termios_.c_cc[VSUSP] = 26;
74 termios_.c_cc[VEOL] = 0;
75 termios_.c_cc[VREPRINT] = 18;
76 termios_.c_cc[VDISCARD] = 15;
77 termios_.c_cc[VWERASE] = 23;
78 termios_.c_cc[VLNEXT] = 22;
79 termios_.c_cc[VEOL2] = 0;
82 EventEmitter* TtyNode::GetEventEmitter() {
83 return emitter_.get();
86 Error TtyNode::Write(const HandleAttr& attr,
87 const void* buf,
88 size_t count,
89 int* out_bytes) {
90 AUTO_LOCK(output_lock_);
91 *out_bytes = 0;
93 // No handler registered.
94 if (output_handler_.handler == NULL) {
95 // No error here; many of the tests trigger this message.
96 LOG_TRACE("No output handler registered.");
97 return EIO;
100 int rtn = output_handler_.handler(
101 static_cast<const char*>(buf), count, output_handler_.user_data);
103 // Negative return value means an error occured and the return
104 // value is a negated errno value.
105 if (rtn < 0)
106 return -rtn;
108 *out_bytes = rtn;
109 return 0;
112 Error TtyNode::Read(const HandleAttr& attr,
113 void* buf,
114 size_t count,
115 int* out_bytes) {
116 EventListenerLock wait(GetEventEmitter());
117 *out_bytes = 0;
119 // If interrupted, return
120 int ms = attr.IsBlocking() ? -1 : 0;
121 Error err = wait.WaitOnEvent(POLLIN, ms);
122 if (err == ETIMEDOUT)
123 err = EWOULDBLOCK;
124 if (err != 0)
125 return err;
127 size_t bytes_to_copy = std::min(count, input_buffer_.size());
128 if (IS_ICANON) {
129 // Only read up to (and including) the first newline
130 std::deque<char>::iterator nl =
131 std::find(input_buffer_.begin(), input_buffer_.end(), '\n');
133 if (nl != input_buffer_.end()) {
134 // We found a newline in the buffer, adjust bytes_to_copy accordingly
135 size_t line_len = static_cast<size_t>(nl - input_buffer_.begin()) + 1;
136 bytes_to_copy = std::min(bytes_to_copy, line_len);
140 // Copies data from the input buffer into buf.
141 std::copy(input_buffer_.begin(),
142 input_buffer_.begin() + bytes_to_copy,
143 static_cast<char*>(buf));
144 *out_bytes = bytes_to_copy;
145 input_buffer_.erase(input_buffer_.begin(),
146 input_buffer_.begin() + bytes_to_copy);
148 // mark input as no longer readable if we consumed
149 // the entire buffer or, in the case of buffered input,
150 // we consumed the final \n char.
151 bool avail;
152 if (IS_ICANON)
153 avail = std::find(input_buffer_.begin(), input_buffer_.end(), '\n') !=
154 input_buffer_.end();
155 else
156 avail = input_buffer_.size() > 0;
158 if (!avail)
159 emitter_->ClearEvents_Locked(POLLIN);
161 return 0;
164 Error TtyNode::Echo(const char* string, int count) {
165 int wrote;
166 HandleAttr data;
167 Error error = Write(data, string, count, &wrote);
168 if (error != 0 || wrote != count) {
169 // TOOD(sbc): Do something more useful in response to a
170 // failure to echo.
171 return error;
174 return 0;
177 Error TtyNode::ProcessInput(PP_Var message) {
178 if (message.type != PP_VARTYPE_STRING) {
179 LOG_ERROR("Expected VarString but got %d.", message.type);
180 return EINVAL;
183 PepperInterface* ppapi = filesystem_->ppapi();
184 if (!ppapi) {
185 LOG_ERROR("ppapi is NULL.");
186 return EINVAL;
189 VarInterface* var_iface = ppapi->GetVarInterface();
190 if (!var_iface) {
191 LOG_ERROR("Got NULL interface: Var");
192 return EINVAL;
195 uint32_t num_bytes;
196 const char* buffer = var_iface->VarToUtf8(message, &num_bytes);
197 Error error = ProcessInput(buffer, num_bytes);
198 return error;
201 Error TtyNode::ProcessInput(const char* buffer, size_t num_bytes) {
202 AUTO_LOCK(emitter_->GetLock())
204 for (size_t i = 0; i < num_bytes; i++) {
205 char c = buffer[i];
206 // Transform characters according to input flags.
207 if (c == '\r') {
208 if (termios_.c_iflag & IGNCR)
209 continue;
210 if (termios_.c_iflag & ICRNL)
211 c = '\n';
212 } else if (c == '\n') {
213 if (termios_.c_iflag & INLCR)
214 c = '\r';
217 bool skip = false;
219 // ICANON mode means we wait for a newline before making the
220 // file readable.
221 if (IS_ICANON) {
222 if (IS_ECHOE && c == termios_.c_cc[VERASE]) {
223 // Remove previous character in the line if any.
224 if (!input_buffer_.empty()) {
225 char char_to_delete = input_buffer_.back();
226 if (char_to_delete != '\n') {
227 input_buffer_.pop_back();
228 if (IS_ECHO)
229 Echo("\b \b", 3);
231 // When ECHOCTL is set the echo buffer contains an extra
232 // char for each control char.
233 if (IS_ECHOCTL && iscntrl(char_to_delete))
234 Echo("\b \b", 3);
237 continue;
238 } else if (IS_ECHO || (IS_ECHONL && c == '\n')) {
239 if (c == termios_.c_cc[VEOF]) {
240 // VEOF sequence is not echoed, nor is it sent as
241 // input.
242 skip = true;
243 } else if (c != '\n' && iscntrl(c) && IS_ECHOCTL) {
244 // In ECHOCTL mode a control char C is echoed as '^'
245 // followed by the ascii char which at C + 0x40.
246 char visible_char = c + 0x40;
247 Echo("^", 1);
248 Echo(&visible_char, 1);
249 } else {
250 Echo(&c, 1);
255 if (!skip)
256 input_buffer_.push_back(c);
258 if (c == '\n' || c == termios_.c_cc[VEOF] || !IS_ICANON)
259 emitter_->RaiseEvents_Locked(POLLIN);
262 return 0;
265 Error TtyNode::VIoctl(int request, va_list args) {
267 * Casts required for some of these case statements in order to silence
268 * compiler warning when built with darwin headers.
270 switch (request) {
271 case TIOCNACLOUTPUT: {
272 struct tioc_nacl_output* arg = va_arg(args, struct tioc_nacl_output*);
273 AUTO_LOCK(output_lock_);
274 if (arg == NULL) {
275 output_handler_.handler = NULL;
276 return 0;
278 if (output_handler_.handler != NULL) {
279 LOG_ERROR("Output handler already set.");
280 return EALREADY;
282 output_handler_ = *arg;
283 return 0;
285 case NACL_IOC_HANDLEMESSAGE: {
286 struct PP_Var* message = va_arg(args, struct PP_Var*);
287 return ProcessInput(*message);
289 case (unsigned int)TIOCSWINSZ: {
290 struct winsize* size = va_arg(args, struct winsize*);
292 AUTO_LOCK(node_lock_);
293 if (rows_ == size->ws_row && cols_ == size->ws_col)
294 return 0;
295 rows_ = size->ws_row;
296 cols_ = size->ws_col;
298 ki_kill(getpid(), SIGWINCH);
300 // Wake up any thread waiting on Read with POLLERR then immediate
301 // clear it to signal EINTR.
302 AUTO_LOCK(emitter_->GetLock())
303 emitter_->RaiseEvents_Locked(POLLERR);
304 emitter_->ClearEvents_Locked(POLLERR);
306 return 0;
308 case (unsigned int)TIOCGWINSZ: {
309 struct winsize* size = va_arg(args, struct winsize*);
310 size->ws_row = rows_;
311 size->ws_col = cols_;
312 return 0;
314 default: {
315 LOG_ERROR("TtyNode:VIoctl: Unknown request: %#x", request);
319 return EINVAL;
322 Error TtyNode::Tcgetattr(struct termios* termios_p) {
323 AUTO_LOCK(node_lock_);
324 *termios_p = termios_;
325 return 0;
328 Error TtyNode::Tcsetattr(int optional_actions,
329 const struct termios* termios_p) {
330 AUTO_LOCK(node_lock_);
331 termios_ = *termios_p;
332 return 0;
335 } // namespace nacl_io