blob: bc10fd9a28cdbe8a6ae66e91d57c508588f3286b [file] [log] [blame]
Jeremy Kerrbc1e8932016-04-28 12:27:30 +08001/**
2 * Copyright © 2016 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define _GNU_SOURCE
18
19#include <assert.h>
20#include <err.h>
Jeremy Kerrf733c852017-02-07 18:40:10 +080021#include <errno.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080022#include <fcntl.h>
23#include <stdio.h>
24#include <stdlib.h>
Xo Wangc5ef8ea2017-04-17 16:20:43 -070025#include <string.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080026#include <unistd.h>
Xo Wangc5ef8ea2017-04-17 16:20:43 -070027#include <termios.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080028
29#include "console-server.h"
30
31struct tty_handler {
Jeremy Kerrf733c852017-02-07 18:40:10 +080032 struct handler handler;
33 struct console *console;
34 struct ringbuffer_consumer *rbc;
35 struct poller *poller;
36 int fd;
Jeremy Kerr6b1fed22017-02-07 21:40:38 +080037 int fd_flags;
38 bool blocked;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080039};
40
Xo Wangc5ef8ea2017-04-17 16:20:43 -070041struct terminal_speed_name {
42 speed_t speed;
43 const char *name;
44};
45
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080046static struct tty_handler *to_tty_handler(struct handler *handler)
47{
48 return container_of(handler, struct tty_handler, handler);
49}
50
Jeremy Kerr6b1fed22017-02-07 21:40:38 +080051static void tty_set_fd_blocking(struct tty_handler *th, bool fd_blocking)
52{
53 int flags;
54
55 flags = th->fd_flags & ~O_NONBLOCK;
56 if (!fd_blocking)
57 flags |= O_NONBLOCK;
58
59 if (flags != th->fd_flags) {
60 fcntl(th->fd, F_SETFL, flags);
61 th->fd_flags = flags;
62 }
63}
64
65/*
66 * A "blocked" handler indicates that the last write returned EAGAIN
67 * (==EWOULDBLOCK), so we know not to continue writing (for non-forced output),
68 * as it'll just return EAGAIN again.
69 *
70 * Once we detect this, we watch for POLLOUT in the poller events. A
71 * POLLOUT indicates that the fd is no longer blocking, so we clear
72 * blocked mode and can continue writing.
73 */
74static void tty_set_blocked(struct tty_handler *th, bool blocked)
75{
76 int events;
77
78 if (blocked == th->blocked)
79 return;
80
81 th->blocked = blocked;
82 events = POLLIN;
83
84 if (th->blocked)
85 events |= POLLOUT;
86
87 console_poller_set_events(th->console, th->poller, events);
88}
89
Jeremy Kerrf733c852017-02-07 18:40:10 +080090static int tty_drain_queue(struct tty_handler *th, size_t force_len)
91{
92 size_t len, total_len;
93 ssize_t wlen;
94 uint8_t *buf;
Jeremy Kerrf733c852017-02-07 18:40:10 +080095
96 /* if we're forcing data, we need to clear non-blocking mode */
Jeremy Kerr6b1fed22017-02-07 21:40:38 +080097 if (force_len)
98 tty_set_fd_blocking(th, true);
99
100 /* no point writing, we'll just see -EAGAIN */
101 else if (th->blocked)
102 return 0;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800103
104 total_len = 0;
105
106 for (;;) {
107 len = ringbuffer_dequeue_peek(th->rbc, total_len, &buf);
108 if (!len)
109 break;
110
111 /* write as little as possible while blocking */
112 if (force_len && force_len < total_len + len)
113 len = force_len - total_len;
114
115 wlen = write(th->fd, buf, len);
116 if (wlen < 0) {
117 if (errno == EINTR)
118 continue;
119 if ((errno == EAGAIN || errno == EWOULDBLOCK)
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800120 && !force_len) {
121 tty_set_blocked(th, true);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800122 break;
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800123 }
Jeremy Kerrf733c852017-02-07 18:40:10 +0800124 warn("failed writing to local tty; disabling");
125 return -1;
126 }
127
128 total_len += wlen;
129
130 if (force_len && total_len >= force_len)
131 break;
132 }
133
134 ringbuffer_dequeue_commit(th->rbc, total_len);
135
136 if (force_len)
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800137 tty_set_fd_blocking(th, false);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800138
139 return 0;
140}
141
142static enum ringbuffer_poll_ret tty_ringbuffer_poll(void *arg, size_t force_len)
143{
144 struct tty_handler *th = arg;
145 int rc;
146
147 rc = tty_drain_queue(th, force_len);
148 if (rc) {
149 console_poller_unregister(th->console, th->poller);
150 return RINGBUFFER_POLL_REMOVE;
151 }
152
153 return RINGBUFFER_POLL_OK;
154}
155
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800156static enum poller_ret tty_poll(struct handler *handler,
157 int events, void __attribute__((unused)) *data)
158{
159 struct tty_handler *th = to_tty_handler(handler);
160 uint8_t buf[4096];
161 ssize_t len;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800162 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800163
Jeremy Kerrf733c852017-02-07 18:40:10 +0800164 if (events & POLLIN) {
165 len = read(th->fd, buf, sizeof(buf));
Jeremy Kerr67eab042017-07-12 12:08:35 +0800166 if (len <= 0)
167 goto err;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800168
Jeremy Kerrf733c852017-02-07 18:40:10 +0800169 console_data_out(th->console, buf, len);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800170 }
171
Jeremy Kerrf733c852017-02-07 18:40:10 +0800172 if (events & POLLOUT) {
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800173 tty_set_blocked(th, false);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800174 rc = tty_drain_queue(th, 0);
Jeremy Kerr67eab042017-07-12 12:08:35 +0800175 if (rc)
176 goto err;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800177 }
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800178
179 return POLLER_OK;
Jeremy Kerr67eab042017-07-12 12:08:35 +0800180
181err:
182 th->poller = NULL;
183 close(th->fd);
184 ringbuffer_consumer_unregister(th->rbc);
185 return POLLER_REMOVE;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800186}
187
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700188static int baud_string_to_speed(speed_t *speed, const char *baud_string) {
189 const struct terminal_speed_name terminal_speeds[] = {
190 { B50, "50" },
191 { B75, "75" },
192 { B110, "110" },
193 { B134, "134" },
194 { B150, "150" },
195 { B200, "200" },
196 { B300, "300" },
197 { B600, "600" },
198 { B1200, "1200" },
199 { B1800, "1800" },
200 { B2400, "2400" },
201 { B4800, "4800" },
202 { B9600, "9600" },
203 { B19200, "19200" },
204 { B38400, "38400" },
205 { B57600, "57600" },
206 { B115200, "115200" },
207 { B230400, "230400" },
208 { B460800, "460800" },
209 { B500000, "500000" },
210 { B576000, "576000" },
211 { B921600, "921600" },
212 { B1000000, "1000000" },
213 { B1152000, "1152000" },
214 { B1500000, "1500000" },
215 { B2000000, "2000000" },
216 { B2500000, "2500000" },
217 { B3000000, "3000000" },
218 { B3500000, "3500000" },
219 { B4000000, "4000000" },
220 };
221 const size_t num_terminal_speeds = sizeof(terminal_speeds) /
222 sizeof(struct terminal_speed_name);
223 size_t i;
224
225 for (i = 0; i < num_terminal_speeds; i++) {
226 if (strcmp(baud_string, terminal_speeds[i].name) == 0) {
227 *speed = terminal_speeds[i].speed;
228 return 0;
229 }
230 }
231 return -1;
232}
233
234static int set_terminal_baud(struct tty_handler *th, const char *tty_name,
235 const char *desired_baud) {
236 struct termios term_options;
237 speed_t speed;
238
239 if (baud_string_to_speed(&speed, desired_baud) != 0) {
240 fprintf(stderr, "%s is not a valid baud rate for terminal %s\n",
241 desired_baud, tty_name);
242 return -1;
243 }
244
245 if (tcgetattr(th->fd, &term_options) < 0) {
246 warn("Can't get config for %s", tty_name);
247 return -1;
248 }
249
250 if (cfsetspeed(&term_options, speed) < 0) {
251 warn("Couldn't set speeds for %s", tty_name);
252 return -1;
253 }
254
255 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
256 warn("Couldn't commit terminal options for %s", tty_name);
257 return -1;
258 }
259 printf("Set %s terminal baud rate to %s\n", tty_name, desired_baud);
260
261 return 0;
262}
263
Xo Wang81408bd2017-04-18 16:43:07 -0700264static int make_terminal_raw(struct tty_handler *th, const char *tty_name) {
265 struct termios term_options;
266
267 if (tcgetattr(th->fd, &term_options) < 0) {
268 warn("Can't get config for %s", tty_name);
269 return -1;
270 }
271
272 /* Disable various input and output processing including character
273 * translation, line edit (canonical) mode, flow control, and special signal
274 * generating characters. */
275 cfmakeraw(&term_options);
276
277 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
278 warn("Couldn't commit terminal options for %s", tty_name);
279 return -1;
280 }
281 printf("Set %s for raw byte handling\n", tty_name);
282
283 return 0;
284}
285
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800286static int tty_init(struct handler *handler, struct console *console,
287 struct config *config __attribute__((unused)))
288{
289 struct tty_handler *th = to_tty_handler(handler);
290 const char *tty_name;
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700291 const char *tty_baud;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800292 char *tty_path;
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800293 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800294
295 tty_name = config_get_value(config, "local-tty");
296 if (!tty_name)
297 return -1;
298
299 rc = asprintf(&tty_path, "/dev/%s", tty_name);
300 if (!rc)
301 return -1;
302
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800303 th->fd = open(tty_path, O_RDWR | O_NONBLOCK);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800304 if (th->fd < 0) {
305 warn("Can't open %s; disabling local tty", tty_name);
306 free(tty_path);
307 return -1;
308 }
309
310 free(tty_path);
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800311 th->fd_flags = fcntl(th->fd, F_GETFL, 0);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800312
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700313 tty_baud = config_get_value(config, "local-tty-baud");
314 if (tty_baud != NULL)
315 if (set_terminal_baud(th, tty_name, tty_baud) != 0)
316 fprintf(stderr, "Couldn't set baud rate for %s to %s\n",
317 tty_name, tty_baud);
318
Xo Wang81408bd2017-04-18 16:43:07 -0700319 if (make_terminal_raw(th, tty_name) != 0)
320 fprintf(stderr, "Couldn't make %s a raw terminal\n", tty_name);
321
Jeremy Kerr55c97122017-02-07 17:06:46 +0800322 th->poller = console_poller_register(console, handler, tty_poll,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800323 th->fd, POLLIN, NULL);
324 th->console = console;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800325 th->rbc = console_ringbuffer_consumer_register(console,
326 tty_ringbuffer_poll, th);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800327
328 return 0;
329}
330
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800331static void tty_fini(struct handler *handler)
332{
333 struct tty_handler *th = to_tty_handler(handler);
334 if (th->poller)
Jeremy Kerr55c97122017-02-07 17:06:46 +0800335 console_poller_unregister(th->console, th->poller);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800336 close(th->fd);
337}
338
339static struct tty_handler tty_handler = {
340 .handler = {
341 .name = "tty",
342 .init = tty_init,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800343 .fini = tty_fini,
344 },
345};
346
Jeremy Kerr55c97122017-02-07 17:06:46 +0800347console_handler_register(&tty_handler.handler);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800348