- add sources.
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / mount_node_tty.cc
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
3 // found in the LICENSE file.
4
5 #include "nacl_io/mount_node_tty.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/ioctl.h"
18 #include "nacl_io/kernel_handle.h"
19 #include "nacl_io/mount.h"
20 #include "nacl_io/pepper_interface.h"
21 #include "sdk_util/auto_lock.h"
22
23 #define CHECK_LFLAG(TERMIOS, FLAG) (TERMIOS .c_lflag & FLAG)
24
25 #define IS_ECHO CHECK_LFLAG(termios_, ECHO)
26 #define IS_ECHOE CHECK_LFLAG(termios_, ECHOE)
27 #define IS_ECHONL CHECK_LFLAG(termios_, ECHONL)
28 #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL)
29 #define IS_ICANON CHECK_LFLAG(termios_, ICANON)
30
31 #define DEFAULT_TTY_COLS 80
32 #define DEFAULT_TTY_ROWS 30
33
34 namespace nacl_io {
35
36 MountNodeTty::MountNodeTty(Mount* mount)
37     : MountNodeCharDevice(mount),
38       emitter_(new EventEmitter),
39       rows_(DEFAULT_TTY_ROWS),
40       cols_(DEFAULT_TTY_COLS) {
41   output_handler_.handler = NULL;
42   InitTermios();
43
44   // Output will never block
45   emitter_->RaiseEvents_Locked(POLLOUT);
46 }
47
48 void MountNodeTty::InitTermios() {
49   // Some sane values that produce good result.
50   termios_.c_iflag = ICRNL | IXON | IXOFF | IUTF8;
51   termios_.c_oflag = OPOST | ONLCR;
52   termios_.c_cflag = CREAD | 077;
53   termios_.c_lflag =
54       ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
55   termios_.c_ispeed = B38400;
56   termios_.c_ospeed = B38400;
57   termios_.c_cc[VINTR] = 3;
58   termios_.c_cc[VQUIT] = 28;
59   termios_.c_cc[VERASE] = 127;
60   termios_.c_cc[VKILL] = 21;
61   termios_.c_cc[VEOF] = 4;
62   termios_.c_cc[VTIME] = 0;
63   termios_.c_cc[VMIN] = 1;
64   termios_.c_cc[VSWTC] = 0;
65   termios_.c_cc[VSTART] = 17;
66   termios_.c_cc[VSTOP] = 19;
67   termios_.c_cc[VSUSP] = 26;
68   termios_.c_cc[VEOL] = 0;
69   termios_.c_cc[VREPRINT] = 18;
70   termios_.c_cc[VDISCARD] = 15;
71   termios_.c_cc[VWERASE] = 23;
72   termios_.c_cc[VLNEXT] = 22;
73   termios_.c_cc[VEOL2] = 0;
74 }
75
76 EventEmitter* MountNodeTty::GetEventEmitter() {
77   return emitter_.get();
78 }
79
80 Error MountNodeTty::Write(const HandleAttr& attr,
81                           const void* buf,
82                           size_t count,
83                           int* out_bytes) {
84   AUTO_LOCK(output_lock_);
85   *out_bytes = 0;
86
87   // No handler registered.
88   if (output_handler_.handler == NULL)
89     return EIO;
90
91   int rtn = output_handler_.handler(static_cast<const char*>(buf),
92                                     count,
93                                     output_handler_.user_data);
94
95   // Negative return value means an error occured and the return
96   // value is a negated errno value.
97   if (rtn < 0)
98     return -rtn;
99
100   *out_bytes = rtn;
101   return 0;
102 }
103
104
105 Error MountNodeTty::Read(const HandleAttr& attr,
106                          void* buf,
107                          size_t count,
108                          int* out_bytes) {
109   EventListenerLock wait(GetEventEmitter());
110   *out_bytes = 0;
111
112   // If interrupted, return
113   Error err = wait.WaitOnEvent(POLLIN, -1);
114   if (err != 0)
115     return err;
116
117   size_t bytes_to_copy = std::min(count, input_buffer_.size());
118   if (IS_ICANON) {
119     // Only read up to (and including) the first newline
120     std::deque<char>::iterator nl = std::find(input_buffer_.begin(),
121                                               input_buffer_.end(),
122                                               '\n');
123
124     if (nl != input_buffer_.end()) {
125       // We found a newline in the buffer, adjust bytes_to_copy accordingly
126       size_t line_len = static_cast<size_t>(nl - input_buffer_.begin()) + 1;
127       bytes_to_copy = std::min(bytes_to_copy, line_len);
128     }
129   }
130
131
132   // Copies data from the input buffer into buf.
133   std::copy(input_buffer_.begin(), input_buffer_.begin() + bytes_to_copy,
134             static_cast<char*>(buf));
135   *out_bytes = bytes_to_copy;
136   input_buffer_.erase(input_buffer_.begin(),
137                       input_buffer_.begin() + bytes_to_copy);
138
139   // mark input as no longer readable if we consumed
140   // the entire buffer or, in the case of buffered input,
141   // we consumed the final \n char.
142   bool avail;
143   if (IS_ICANON)
144     avail = std::find(input_buffer_.begin(),
145                       input_buffer_.end(), '\n') != input_buffer_.end();
146   else
147     avail = input_buffer_.size() > 0;
148
149   if (!avail)
150     emitter_->ClearEvents_Locked(POLLIN);
151
152   return 0;
153 }
154
155 Error MountNodeTty::Echo(const char* string, int count) {
156   int wrote;
157   HandleAttr data;
158   Error error = Write(data, string, count, &wrote);
159   if (error != 0 || wrote != count) {
160     // TOOD(sbc): Do something more useful in response to a
161     // failure to echo.
162     return error;
163   }
164
165   return 0;
166 }
167
168 Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) {
169   AUTO_LOCK(emitter_->GetLock())
170
171   const char* buffer = message->buffer;
172   size_t num_bytes = message->length;
173
174   for (size_t i = 0; i < num_bytes; i++) {
175     char c = buffer[i];
176     // Transform characters according to input flags.
177     if (c == '\r') {
178       if (termios_.c_iflag & IGNCR)
179         continue;
180       if (termios_.c_iflag & ICRNL)
181         c = '\n';
182     } else if (c == '\n') {
183       if (termios_.c_iflag & INLCR)
184         c = '\r';
185     }
186
187     bool skip = false;
188
189     // ICANON mode means we wait for a newline before making the
190     // file readable.
191     if (IS_ICANON) {
192       if (IS_ECHOE && c == termios_.c_cc[VERASE]) {
193         // Remove previous character in the line if any.
194         if (!input_buffer_.empty()) {
195           char char_to_delete = input_buffer_.back();
196           if (char_to_delete != '\n') {
197             input_buffer_.pop_back();
198             if (IS_ECHO)
199               Echo("\b \b", 3);
200
201             // When ECHOCTL is set the echo buffer contains an extra
202             // char for each control char.
203             if (IS_ECHOCTL && iscntrl(char_to_delete))
204               Echo("\b \b", 3);
205           }
206         }
207         continue;
208       } else if (IS_ECHO || (IS_ECHONL && c == '\n')) {
209         if (c == termios_.c_cc[VEOF]) {
210           // VEOF sequence is not echoed, nor is it sent as
211           // input.
212           skip = true;
213         } else if (c != '\n' && iscntrl(c) && IS_ECHOCTL) {
214           // In ECHOCTL mode a control char C is echoed  as '^'
215           // followed by the ascii char which at C + 0x40.
216           char visible_char = c + 0x40;
217           Echo("^", 1);
218           Echo(&visible_char, 1);
219         } else {
220           Echo(&c, 1);
221         }
222       }
223     }
224
225     if (!skip)
226       input_buffer_.push_back(c);
227
228     if (c == '\n' || c == termios_.c_cc[VEOF] || !IS_ICANON)
229       emitter_->RaiseEvents_Locked(POLLIN);
230   }
231
232   return 0;
233 }
234
235 Error MountNodeTty::VIoctl(int request, va_list args) {
236   switch (request) {
237     case TIOCNACLOUTPUT: {
238       struct tioc_nacl_output* arg = va_arg(args, struct tioc_nacl_output*);
239       AUTO_LOCK(output_lock_);
240       if (arg == NULL) {
241         output_handler_.handler = NULL;
242         return 0;
243       }
244       if (output_handler_.handler != NULL)
245         return EALREADY;
246       output_handler_ = *arg;
247       return 0;
248     }
249     case TIOCNACLINPUT: {
250       // This ioctl is used to deliver data from the user to this tty node's
251       // input buffer.
252       struct tioc_nacl_input_string* message =
253         va_arg(args, struct tioc_nacl_input_string*);
254       return ProcessInput(message);
255     }
256     case TIOCSWINSZ: {
257       struct winsize* size = va_arg(args, struct winsize*);
258       {
259         AUTO_LOCK(node_lock_);
260         rows_ = size->ws_row;
261         cols_ = size->ws_col;
262       }
263       kill(getpid(), SIGWINCH);
264       {
265         // Wake up any thread waiting on Read with POLLERR then immediate
266         // clear it to signal EINTR.
267         AUTO_LOCK(emitter_->GetLock())
268         emitter_->RaiseEvents_Locked(POLLERR);
269         emitter_->ClearEvents_Locked(POLLERR);
270       }
271       return 0;
272     }
273     case TIOCGWINSZ: {
274       struct winsize* size = va_arg(args, struct winsize*);
275       size->ws_row = rows_;
276       size->ws_col = cols_;
277       return 0;
278     }
279   }
280
281   return EINVAL;
282 }
283
284 Error MountNodeTty::Tcgetattr(struct termios* termios_p) {
285   AUTO_LOCK(node_lock_);
286   *termios_p = termios_;
287   return 0;
288 }
289
290 Error MountNodeTty::Tcsetattr(int optional_actions,
291                          const struct termios *termios_p) {
292   AUTO_LOCK(node_lock_);
293   termios_ = *termios_p;
294   return 0;
295 }
296
297 }