blob: af9b7543a1637684f46f26267246daad9fe672d1 [file] [log] [blame]
Ben Murdochba5b9a62013-08-12 14:20:17 +01001// 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>
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +01009#include <signal.h>
Ben Murdochba5b9a62013-08-12 14:20:17 +010010#include <stdio.h>
11#include <string.h>
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +010012#include <sys/ioctl.h>
13#include <unistd.h>
Ben Murdochba5b9a62013-08-12 14:20:17 +010014
15#include <algorithm>
16
17#include "nacl_io/ioctl.h"
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +010018#include "nacl_io/kernel_handle.h"
Ben Murdochba5b9a62013-08-12 14:20:17 +010019#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
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +010031#define DEFAULT_TTY_COLS 80
32#define DEFAULT_TTY_ROWS 30
33
Ben Murdochba5b9a62013-08-12 14:20:17 +010034namespace nacl_io {
35
Torne (Richard Coles)68043e12013-09-26 13:24:57 +010036MountNodeTty::MountNodeTty(Mount* mount)
37 : MountNodeCharDevice(mount),
38 emitter_(new EventEmitter),
39 rows_(DEFAULT_TTY_ROWS),
40 cols_(DEFAULT_TTY_COLS) {
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +010041 output_handler_.handler = NULL;
Ben Murdochba5b9a62013-08-12 14:20:17 +010042 InitTermios();
Torne (Richard Coles)68043e12013-09-26 13:24:57 +010043
44 // Output will never block
45 emitter_->RaiseEvents_Locked(POLLOUT);
Ben Murdochba5b9a62013-08-12 14:20:17 +010046}
47
48void 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
Torne (Richard Coles)68043e12013-09-26 13:24:57 +010076EventEmitter* MountNodeTty::GetEventEmitter() {
77 return emitter_.get();
Ben Murdochba5b9a62013-08-12 14:20:17 +010078}
79
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +010080Error MountNodeTty::Write(const HandleAttr& attr,
81 const void* buf,
82 size_t count,
83 int* out_bytes) {
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +010084 AUTO_LOCK(output_lock_);
Ben Murdochba5b9a62013-08-12 14:20:17 +010085 *out_bytes = 0;
86
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +010087 // No handler registered.
88 if (output_handler_.handler == NULL)
89 return EIO;
Ben Murdochba5b9a62013-08-12 14:20:17 +010090
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +010091 int rtn = output_handler_.handler(static_cast<const char*>(buf),
92 count,
93 output_handler_.user_data);
Ben Murdochba5b9a62013-08-12 14:20:17 +010094
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +010095 // Negative return value means an error occured and the return
96 // value is a negated errno value.
97 if (rtn < 0)
98 return -rtn;
Ben Murdochba5b9a62013-08-12 14:20:17 +010099
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100100 *out_bytes = rtn;
Ben Murdochba5b9a62013-08-12 14:20:17 +0100101 return 0;
102}
103
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100104
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100105Error MountNodeTty::Read(const HandleAttr& attr,
106 void* buf,
107 size_t count,
108 int* out_bytes) {
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100109 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;
Ben Murdochba5b9a62013-08-12 14:20:17 +0100116
117 size_t bytes_to_copy = std::min(count, input_buffer_.size());
Ben Murdochba5b9a62013-08-12 14:20:17 +0100118 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.
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100142 bool avail;
Ben Murdochba5b9a62013-08-12 14:20:17 +0100143 if (IS_ICANON)
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100144 avail = std::find(input_buffer_.begin(),
145 input_buffer_.end(), '\n') != input_buffer_.end();
Ben Murdochba5b9a62013-08-12 14:20:17 +0100146 else
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100147 avail = input_buffer_.size() > 0;
148
149 if (!avail)
150 emitter_->ClearEvents_Locked(POLLIN);
Ben Murdochba5b9a62013-08-12 14:20:17 +0100151
152 return 0;
153}
154
155Error MountNodeTty::Echo(const char* string, int count) {
156 int wrote;
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100157 HandleAttr data;
158 Error error = Write(data, string, count, &wrote);
Ben Murdochba5b9a62013-08-12 14:20:17 +0100159 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
168Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) {
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100169 AUTO_LOCK(emitter_->GetLock())
Ben Murdochba5b9a62013-08-12 14:20:17 +0100170
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100171 const char* buffer = message->buffer;
172 size_t num_bytes = message->length;
Ben Murdochba5b9a62013-08-12 14:20:17 +0100173
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100174 for (size_t i = 0; i < num_bytes; i++) {
Ben Murdochba5b9a62013-08-12 14:20:17 +0100175 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)
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100229 emitter_->RaiseEvents_Locked(POLLIN);
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100230 }
231
Ben Murdochba5b9a62013-08-12 14:20:17 +0100232 return 0;
233}
234
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100235Error MountNodeTty::VIoctl(int request, va_list args) {
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100236 switch (request) {
237 case TIOCNACLOUTPUT: {
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100238 struct tioc_nacl_output* arg = va_arg(args, struct tioc_nacl_output*);
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100239 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;
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100246 output_handler_ = *arg;
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100247 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 =
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100253 va_arg(args, struct tioc_nacl_input_string*);
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100254 return ProcessInput(message);
255 }
256 case TIOCSWINSZ: {
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100257 struct winsize* size = va_arg(args, struct winsize*);
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100258 {
259 AUTO_LOCK(node_lock_);
260 rows_ = size->ws_row;
261 cols_ = size->ws_col;
262 }
263 kill(getpid(), SIGWINCH);
Torne (Richard Coles)68043e12013-09-26 13:24:57 +0100264 {
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 }
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100271 return 0;
272 }
273 case TIOCGWINSZ: {
Torne (Richard Coles)4e180b62013-10-18 15:46:22 +0100274 struct winsize* size = va_arg(args, struct winsize*);
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100275 size->ws_row = rows_;
276 size->ws_col = cols_;
277 return 0;
278 }
Ben Murdochba5b9a62013-08-12 14:20:17 +0100279 }
Torne (Richard Coles)3551c9c2013-08-23 16:39:15 +0100280
281 return EINVAL;
Ben Murdochba5b9a62013-08-12 14:20:17 +0100282}
283
284Error MountNodeTty::Tcgetattr(struct termios* termios_p) {
285 AUTO_LOCK(node_lock_);
286 *termios_p = termios_;
287 return 0;
288}
289
290Error 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}