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"
12 #include <sys/ioctl.h>
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
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
;
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
;
54 termios_
.c_iflag
|= IUTF8
;
56 termios_
.c_oflag
= OPOST
| ONLCR
;
57 termios_
.c_cflag
= CREAD
| 077;
59 ISIG
| ICANON
| ECHO
| ECHOE
| ECHOK
| ECHOCTL
| ECHOKE
| IEXTEN
;
60 #if !defined(__BIONIC__) && !(defined(__GLIBC__) && defined(__arm__))
61 termios_
.c_ispeed
= B38400
;
62 termios_
.c_ospeed
= B38400
;
64 termios_
.c_cc
[VINTR
] = 3;
65 termios_
.c_cc
[VQUIT
] = 28;
66 termios_
.c_cc
[VERASE
] = 127;
67 termios_
.c_cc
[VKILL
] = 21;
68 termios_
.c_cc
[VEOF
] = 4;
69 termios_
.c_cc
[VTIME
] = 0;
70 termios_
.c_cc
[VMIN
] = 1;
71 #if defined(VSWTC) /* Not defined on on Mac */
72 termios_
.c_cc
[VSWTC
] = 0;
74 termios_
.c_cc
[VSTART
] = 17;
75 termios_
.c_cc
[VSTOP
] = 19;
76 termios_
.c_cc
[VSUSP
] = 26;
77 termios_
.c_cc
[VEOL
] = 0;
78 termios_
.c_cc
[VREPRINT
] = 18;
79 termios_
.c_cc
[VDISCARD
] = 15;
80 termios_
.c_cc
[VWERASE
] = 23;
81 termios_
.c_cc
[VLNEXT
] = 22;
82 termios_
.c_cc
[VEOL2
] = 0;
85 EventEmitter
* TtyNode::GetEventEmitter() {
86 return emitter_
.get();
89 Error
TtyNode::Write(const HandleAttr
& attr
,
93 AUTO_LOCK(output_lock_
);
96 // No handler registered.
97 if (output_handler_
.handler
== NULL
) {
98 // No error here; many of the tests trigger this message.
99 LOG_TRACE("No output handler registered.");
103 int rtn
= output_handler_
.handler(
104 static_cast<const char*>(buf
), count
, output_handler_
.user_data
);
106 // Negative return value means an error occured and the return
107 // value is a negated errno value.
115 Error
TtyNode::Read(const HandleAttr
& attr
,
119 EventListenerLock
wait(GetEventEmitter());
122 // If interrupted, return
123 int ms
= attr
.IsBlocking() ? -1 : 0;
124 Error err
= wait
.WaitOnEvent(POLLIN
, ms
);
125 if (err
== ETIMEDOUT
)
130 size_t bytes_to_copy
= std::min(count
, input_buffer_
.size());
132 // Only read up to (and including) the first newline
133 std::deque
<char>::iterator nl
=
134 std::find(input_buffer_
.begin(), input_buffer_
.end(), '\n');
136 if (nl
!= input_buffer_
.end()) {
137 // We found a newline in the buffer, adjust bytes_to_copy accordingly
138 size_t line_len
= static_cast<size_t>(nl
- input_buffer_
.begin()) + 1;
139 bytes_to_copy
= std::min(bytes_to_copy
, line_len
);
143 // Copies data from the input buffer into buf.
144 std::copy(input_buffer_
.begin(),
145 input_buffer_
.begin() + bytes_to_copy
,
146 static_cast<char*>(buf
));
147 *out_bytes
= bytes_to_copy
;
148 input_buffer_
.erase(input_buffer_
.begin(),
149 input_buffer_
.begin() + bytes_to_copy
);
151 // mark input as no longer readable if we consumed
152 // the entire buffer or, in the case of buffered input,
153 // we consumed the final \n char.
156 avail
= std::find(input_buffer_
.begin(), input_buffer_
.end(), '\n') !=
159 avail
= input_buffer_
.size() > 0;
162 emitter_
->ClearEvents_Locked(POLLIN
);
167 Error
TtyNode::Echo(const char* string
, int count
) {
170 Error error
= Write(data
, string
, count
, &wrote
);
171 if (error
!= 0 || wrote
!= count
) {
172 // TOOD(sbc): Do something more useful in response to a
180 Error
TtyNode::ProcessInput(PP_Var message
) {
181 if (message
.type
!= PP_VARTYPE_STRING
) {
182 LOG_ERROR("Expected VarString but got %d.", message
.type
);
186 PepperInterface
* ppapi
= filesystem_
->ppapi();
188 LOG_ERROR("ppapi is NULL.");
192 VarInterface
* var_iface
= ppapi
->GetVarInterface();
194 LOG_ERROR("Got NULL interface: Var");
199 const char* buffer
= var_iface
->VarToUtf8(message
, &num_bytes
);
200 Error error
= ProcessInput(buffer
, num_bytes
);
204 Error
TtyNode::ProcessInput(const char* buffer
, size_t num_bytes
) {
205 AUTO_LOCK(emitter_
->GetLock())
207 for (size_t i
= 0; i
< num_bytes
; i
++) {
209 // Transform characters according to input flags.
211 if (termios_
.c_iflag
& IGNCR
)
213 if (termios_
.c_iflag
& ICRNL
)
215 } else if (c
== '\n') {
216 if (termios_
.c_iflag
& INLCR
)
222 // ICANON mode means we wait for a newline before making the
225 if (IS_ECHOE
&& c
== termios_
.c_cc
[VERASE
]) {
226 // Remove previous character in the line if any.
227 if (!input_buffer_
.empty()) {
228 char char_to_delete
= input_buffer_
.back();
229 if (char_to_delete
!= '\n') {
230 input_buffer_
.pop_back();
234 // When ECHOCTL is set the echo buffer contains an extra
235 // char for each control char.
236 if (IS_ECHOCTL
&& iscntrl(char_to_delete
))
241 } else if (IS_ECHO
|| (IS_ECHONL
&& c
== '\n')) {
242 if (c
== termios_
.c_cc
[VEOF
]) {
243 // VEOF sequence is not echoed, nor is it sent as
246 } else if (c
!= '\n' && iscntrl(c
) && IS_ECHOCTL
) {
247 // In ECHOCTL mode a control char C is echoed as '^'
248 // followed by the ascii char which at C + 0x40.
249 char visible_char
= c
+ 0x40;
251 Echo(&visible_char
, 1);
259 input_buffer_
.push_back(c
);
261 if (c
== '\n' || c
== termios_
.c_cc
[VEOF
] || !IS_ICANON
)
262 emitter_
->RaiseEvents_Locked(POLLIN
);
268 Error
TtyNode::VIoctl(int request
, va_list args
) {
270 * Casts required for some of these case statements in order to silence
271 * compiler warning when built with darwin headers.
274 case TIOCNACLOUTPUT
: {
275 struct tioc_nacl_output
* arg
= va_arg(args
, struct tioc_nacl_output
*);
276 AUTO_LOCK(output_lock_
);
278 output_handler_
.handler
= NULL
;
281 if (output_handler_
.handler
!= NULL
) {
282 LOG_ERROR("Output handler already set.");
285 output_handler_
= *arg
;
288 case NACL_IOC_HANDLEMESSAGE
: {
289 struct PP_Var
* message
= va_arg(args
, struct PP_Var
*);
290 return ProcessInput(*message
);
292 case (unsigned int)TIOCSWINSZ
: {
293 struct winsize
* size
= va_arg(args
, struct winsize
*);
295 AUTO_LOCK(node_lock_
);
296 if (rows_
== size
->ws_row
&& cols_
== size
->ws_col
)
298 rows_
= size
->ws_row
;
299 cols_
= size
->ws_col
;
301 ki_kill(getpid(), SIGWINCH
);
303 // Wake up any thread waiting on Read with POLLERR then immediate
304 // clear it to signal EINTR.
305 AUTO_LOCK(emitter_
->GetLock())
306 emitter_
->RaiseEvents_Locked(POLLERR
);
307 emitter_
->ClearEvents_Locked(POLLERR
);
311 case (unsigned int)TIOCGWINSZ
: {
312 struct winsize
* size
= va_arg(args
, struct winsize
*);
313 size
->ws_row
= rows_
;
314 size
->ws_col
= cols_
;
318 LOG_ERROR("TtyNode:VIoctl: Unknown request: %#x", request
);
325 Error
TtyNode::Tcgetattr(struct termios
* termios_p
) {
326 AUTO_LOCK(node_lock_
);
327 *termios_p
= termios_
;
331 Error
TtyNode::Tcsetattr(int optional_actions
,
332 const struct termios
* termios_p
) {
333 AUTO_LOCK(node_lock_
);
334 termios_
= *termios_p
;
338 } // namespace nacl_io