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
| IUTF8
;
53 termios_
.c_oflag
= OPOST
| ONLCR
;
54 termios_
.c_cflag
= CREAD
| 077;
56 ISIG
| ICANON
| ECHO
| ECHOE
| ECHOK
| ECHOCTL
| ECHOKE
| IEXTEN
;
57 #if !defined(__BIONIC__)
58 termios_
.c_ispeed
= B38400
;
59 termios_
.c_ospeed
= B38400
;
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;
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
,
90 AUTO_LOCK(output_lock_
);
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.");
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.
112 Error
TtyNode::Read(const HandleAttr
& attr
,
116 EventListenerLock
wait(GetEventEmitter());
119 // If interrupted, return
120 int ms
= attr
.IsBlocking() ? -1 : 0;
121 Error err
= wait
.WaitOnEvent(POLLIN
, ms
);
122 if (err
== ETIMEDOUT
)
127 size_t bytes_to_copy
= std::min(count
, input_buffer_
.size());
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.
153 avail
= std::find(input_buffer_
.begin(), input_buffer_
.end(), '\n') !=
156 avail
= input_buffer_
.size() > 0;
159 emitter_
->ClearEvents_Locked(POLLIN
);
164 Error
TtyNode::Echo(const char* string
, int count
) {
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
177 Error
TtyNode::ProcessInput(PP_Var message
) {
178 if (message
.type
!= PP_VARTYPE_STRING
) {
179 LOG_ERROR("Expected VarString but got %d.", message
.type
);
183 PepperInterface
* ppapi
= filesystem_
->ppapi();
185 LOG_ERROR("ppapi is NULL.");
189 VarInterface
* var_iface
= ppapi
->GetVarInterface();
191 LOG_ERROR("Got NULL interface: Var");
196 const char* buffer
= var_iface
->VarToUtf8(message
, &num_bytes
);
197 Error error
= ProcessInput(buffer
, num_bytes
);
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
++) {
206 // Transform characters according to input flags.
208 if (termios_
.c_iflag
& IGNCR
)
210 if (termios_
.c_iflag
& ICRNL
)
212 } else if (c
== '\n') {
213 if (termios_
.c_iflag
& INLCR
)
219 // ICANON mode means we wait for a newline before making the
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();
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
))
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
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;
248 Echo(&visible_char
, 1);
256 input_buffer_
.push_back(c
);
258 if (c
== '\n' || c
== termios_
.c_cc
[VEOF
] || !IS_ICANON
)
259 emitter_
->RaiseEvents_Locked(POLLIN
);
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.
271 case TIOCNACLOUTPUT
: {
272 struct tioc_nacl_output
* arg
= va_arg(args
, struct tioc_nacl_output
*);
273 AUTO_LOCK(output_lock_
);
275 output_handler_
.handler
= NULL
;
278 if (output_handler_
.handler
!= NULL
) {
279 LOG_ERROR("Output handler already set.");
282 output_handler_
= *arg
;
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
)
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
);
308 case (unsigned int)TIOCGWINSZ
: {
309 struct winsize
* size
= va_arg(args
, struct winsize
*);
310 size
->ws_row
= rows_
;
311 size
->ws_col
= cols_
;
315 LOG_ERROR("TtyNode:VIoctl: Unknown request: %#x", request
);
322 Error
TtyNode::Tcgetattr(struct termios
* termios_p
) {
323 AUTO_LOCK(node_lock_
);
324 *termios_p
= termios_
;
328 Error
TtyNode::Tcsetattr(int optional_actions
,
329 const struct termios
* termios_p
) {
330 AUTO_LOCK(node_lock_
);
331 termios_
= *termios_p
;
335 } // namespace nacl_io