Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / devfs / tty_node.cc
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.
4
5 #include "nacl_io/devfs/tty_node.h"
6
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>
14
15 #include <algorithm>
16
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"
24
25 #define CHECK_LFLAG(TERMIOS, FLAG) (TERMIOS.c_lflag& FLAG)
26
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)
32
33 #define DEFAULT_TTY_COLS 80
34 #define DEFAULT_TTY_ROWS 30
35
36 namespace nacl_io {
37
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();
45
46   // Output will never block
47   emitter_->RaiseEvents_Locked(POLLOUT);
48 }
49
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   termios_.c_cc[VSWTC] = 0;
69   termios_.c_cc[VSTART] = 17;
70   termios_.c_cc[VSTOP] = 19;
71   termios_.c_cc[VSUSP] = 26;
72   termios_.c_cc[VEOL] = 0;
73   termios_.c_cc[VREPRINT] = 18;
74   termios_.c_cc[VDISCARD] = 15;
75   termios_.c_cc[VWERASE] = 23;
76   termios_.c_cc[VLNEXT] = 22;
77   termios_.c_cc[VEOL2] = 0;
78 }
79
80 EventEmitter* TtyNode::GetEventEmitter() {
81   return emitter_.get();
82 }
83
84 Error TtyNode::Write(const HandleAttr& attr,
85                      const void* buf,
86                      size_t count,
87                      int* out_bytes) {
88   AUTO_LOCK(output_lock_);
89   *out_bytes = 0;
90
91   // No handler registered.
92   if (output_handler_.handler == NULL) {
93     // No error here; many of the tests trigger this message.
94     LOG_TRACE("No output handler registered.");
95     return EIO;
96   }
97
98   int rtn = output_handler_.handler(
99       static_cast<const char*>(buf), count, output_handler_.user_data);
100
101   // Negative return value means an error occured and the return
102   // value is a negated errno value.
103   if (rtn < 0)
104     return -rtn;
105
106   *out_bytes = rtn;
107   return 0;
108 }
109
110 Error TtyNode::Read(const HandleAttr& attr,
111                     void* buf,
112                     size_t count,
113                     int* out_bytes) {
114   EventListenerLock wait(GetEventEmitter());
115   *out_bytes = 0;
116
117   // If interrupted, return
118   Error err = wait.WaitOnEvent(POLLIN, -1);
119   if (err == ETIMEDOUT)
120     err = EWOULDBLOCK;
121   if (err != 0)
122     return err;
123
124   size_t bytes_to_copy = std::min(count, input_buffer_.size());
125   if (IS_ICANON) {
126     // Only read up to (and including) the first newline
127     std::deque<char>::iterator nl =
128         std::find(input_buffer_.begin(), input_buffer_.end(), '\n');
129
130     if (nl != input_buffer_.end()) {
131       // We found a newline in the buffer, adjust bytes_to_copy accordingly
132       size_t line_len = static_cast<size_t>(nl - input_buffer_.begin()) + 1;
133       bytes_to_copy = std::min(bytes_to_copy, line_len);
134     }
135   }
136
137   // Copies data from the input buffer into buf.
138   std::copy(input_buffer_.begin(),
139             input_buffer_.begin() + bytes_to_copy,
140             static_cast<char*>(buf));
141   *out_bytes = bytes_to_copy;
142   input_buffer_.erase(input_buffer_.begin(),
143                       input_buffer_.begin() + bytes_to_copy);
144
145   // mark input as no longer readable if we consumed
146   // the entire buffer or, in the case of buffered input,
147   // we consumed the final \n char.
148   bool avail;
149   if (IS_ICANON)
150     avail = std::find(input_buffer_.begin(), input_buffer_.end(), '\n') !=
151             input_buffer_.end();
152   else
153     avail = input_buffer_.size() > 0;
154
155   if (!avail)
156     emitter_->ClearEvents_Locked(POLLIN);
157
158   return 0;
159 }
160
161 Error TtyNode::Echo(const char* string, int count) {
162   int wrote;
163   HandleAttr data;
164   Error error = Write(data, string, count, &wrote);
165   if (error != 0 || wrote != count) {
166     // TOOD(sbc): Do something more useful in response to a
167     // failure to echo.
168     return error;
169   }
170
171   return 0;
172 }
173
174 Error TtyNode::ProcessInput(PP_Var message) {
175   if (message.type != PP_VARTYPE_STRING) {
176     LOG_ERROR("Expected VarString but got %d.", message.type);
177     return EINVAL;
178   }
179
180   PepperInterface* ppapi = filesystem_->ppapi();
181   if (!ppapi) {
182     LOG_ERROR("ppapi is NULL.");
183     return EINVAL;
184   }
185
186   VarInterface* var_iface = ppapi->GetVarInterface();
187   if (!var_iface) {
188     LOG_ERROR("Got NULL interface: Var");
189     return EINVAL;
190   }
191
192   uint32_t num_bytes;
193   const char* buffer = var_iface->VarToUtf8(message, &num_bytes);
194   Error error = ProcessInput(buffer, num_bytes);
195   return error;
196 }
197
198 Error TtyNode::ProcessInput(const char* buffer, size_t num_bytes) {
199   AUTO_LOCK(emitter_->GetLock())
200
201   for (size_t i = 0; i < num_bytes; i++) {
202     char c = buffer[i];
203     // Transform characters according to input flags.
204     if (c == '\r') {
205       if (termios_.c_iflag & IGNCR)
206         continue;
207       if (termios_.c_iflag & ICRNL)
208         c = '\n';
209     } else if (c == '\n') {
210       if (termios_.c_iflag & INLCR)
211         c = '\r';
212     }
213
214     bool skip = false;
215
216     // ICANON mode means we wait for a newline before making the
217     // file readable.
218     if (IS_ICANON) {
219       if (IS_ECHOE && c == termios_.c_cc[VERASE]) {
220         // Remove previous character in the line if any.
221         if (!input_buffer_.empty()) {
222           char char_to_delete = input_buffer_.back();
223           if (char_to_delete != '\n') {
224             input_buffer_.pop_back();
225             if (IS_ECHO)
226               Echo("\b \b", 3);
227
228             // When ECHOCTL is set the echo buffer contains an extra
229             // char for each control char.
230             if (IS_ECHOCTL && iscntrl(char_to_delete))
231               Echo("\b \b", 3);
232           }
233         }
234         continue;
235       } else if (IS_ECHO || (IS_ECHONL && c == '\n')) {
236         if (c == termios_.c_cc[VEOF]) {
237           // VEOF sequence is not echoed, nor is it sent as
238           // input.
239           skip = true;
240         } else if (c != '\n' && iscntrl(c) && IS_ECHOCTL) {
241           // In ECHOCTL mode a control char C is echoed  as '^'
242           // followed by the ascii char which at C + 0x40.
243           char visible_char = c + 0x40;
244           Echo("^", 1);
245           Echo(&visible_char, 1);
246         } else {
247           Echo(&c, 1);
248         }
249       }
250     }
251
252     if (!skip)
253       input_buffer_.push_back(c);
254
255     if (c == '\n' || c == termios_.c_cc[VEOF] || !IS_ICANON)
256       emitter_->RaiseEvents_Locked(POLLIN);
257   }
258
259   return 0;
260 }
261
262 Error TtyNode::VIoctl(int request, va_list args) {
263   switch (request) {
264     case TIOCNACLOUTPUT: {
265       struct tioc_nacl_output* arg = va_arg(args, struct tioc_nacl_output*);
266       AUTO_LOCK(output_lock_);
267       if (arg == NULL) {
268         output_handler_.handler = NULL;
269         return 0;
270       }
271       if (output_handler_.handler != NULL) {
272         LOG_ERROR("Output handler already set.");
273         return EALREADY;
274       }
275       output_handler_ = *arg;
276       return 0;
277     }
278     case NACL_IOC_HANDLEMESSAGE: {
279       struct PP_Var* message = va_arg(args, struct PP_Var*);
280       return ProcessInput(*message);
281     }
282     case TIOCSWINSZ: {
283       struct winsize* size = va_arg(args, struct winsize*);
284       {
285         AUTO_LOCK(node_lock_);
286         if (rows_ == size->ws_row && cols_ == size->ws_col)
287           return 0;
288         rows_ = size->ws_row;
289         cols_ = size->ws_col;
290       }
291       ki_kill(getpid(), SIGWINCH);
292       {
293         // Wake up any thread waiting on Read with POLLERR then immediate
294         // clear it to signal EINTR.
295         AUTO_LOCK(emitter_->GetLock())
296         emitter_->RaiseEvents_Locked(POLLERR);
297         emitter_->ClearEvents_Locked(POLLERR);
298       }
299       return 0;
300     }
301     case TIOCGWINSZ: {
302       struct winsize* size = va_arg(args, struct winsize*);
303       size->ws_row = rows_;
304       size->ws_col = cols_;
305       return 0;
306     }
307     default: {
308       LOG_ERROR("TtyNode:VIoctl: Unknown request: %#x", request);
309     }
310   }
311
312   return EINVAL;
313 }
314
315 Error TtyNode::Tcgetattr(struct termios* termios_p) {
316   AUTO_LOCK(node_lock_);
317   *termios_p = termios_;
318   return 0;
319 }
320
321 Error TtyNode::Tcsetattr(int optional_actions,
322                          const struct termios* termios_p) {
323   AUTO_LOCK(node_lock_);
324   termios_ = *termios_p;
325   return 0;
326 }
327
328 }  // namespace nacl_io