blob: 62480548e779499ce0efd313bd614bbdf646daee [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
41static struct tty_handler *to_tty_handler(struct handler *handler)
42{
43 return container_of(handler, struct tty_handler, handler);
44}
45
Jeremy Kerr6b1fed22017-02-07 21:40:38 +080046static void tty_set_fd_blocking(struct tty_handler *th, bool fd_blocking)
47{
48 int flags;
49
50 flags = th->fd_flags & ~O_NONBLOCK;
51 if (!fd_blocking)
52 flags |= O_NONBLOCK;
53
54 if (flags != th->fd_flags) {
55 fcntl(th->fd, F_SETFL, flags);
56 th->fd_flags = flags;
57 }
58}
59
60/*
61 * A "blocked" handler indicates that the last write returned EAGAIN
62 * (==EWOULDBLOCK), so we know not to continue writing (for non-forced output),
63 * as it'll just return EAGAIN again.
64 *
65 * Once we detect this, we watch for POLLOUT in the poller events. A
66 * POLLOUT indicates that the fd is no longer blocking, so we clear
67 * blocked mode and can continue writing.
68 */
69static void tty_set_blocked(struct tty_handler *th, bool blocked)
70{
71 int events;
72
73 if (blocked == th->blocked)
74 return;
75
76 th->blocked = blocked;
77 events = POLLIN;
78
79 if (th->blocked)
80 events |= POLLOUT;
81
82 console_poller_set_events(th->console, th->poller, events);
83}
84
Jeremy Kerrf733c852017-02-07 18:40:10 +080085static int tty_drain_queue(struct tty_handler *th, size_t force_len)
86{
87 size_t len, total_len;
88 ssize_t wlen;
89 uint8_t *buf;
Jeremy Kerrf733c852017-02-07 18:40:10 +080090
91 /* if we're forcing data, we need to clear non-blocking mode */
Jeremy Kerr6b1fed22017-02-07 21:40:38 +080092 if (force_len)
93 tty_set_fd_blocking(th, true);
94
95 /* no point writing, we'll just see -EAGAIN */
96 else if (th->blocked)
97 return 0;
Jeremy Kerrf733c852017-02-07 18:40:10 +080098
99 total_len = 0;
100
101 for (;;) {
102 len = ringbuffer_dequeue_peek(th->rbc, total_len, &buf);
103 if (!len)
104 break;
105
106 /* write as little as possible while blocking */
107 if (force_len && force_len < total_len + len)
108 len = force_len - total_len;
109
110 wlen = write(th->fd, buf, len);
111 if (wlen < 0) {
112 if (errno == EINTR)
113 continue;
114 if ((errno == EAGAIN || errno == EWOULDBLOCK)
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800115 && !force_len) {
116 tty_set_blocked(th, true);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800117 break;
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800118 }
Jeremy Kerrf733c852017-02-07 18:40:10 +0800119 warn("failed writing to local tty; disabling");
120 return -1;
121 }
122
123 total_len += wlen;
124
125 if (force_len && total_len >= force_len)
126 break;
127 }
128
129 ringbuffer_dequeue_commit(th->rbc, total_len);
130
131 if (force_len)
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800132 tty_set_fd_blocking(th, false);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800133
134 return 0;
135}
136
137static enum ringbuffer_poll_ret tty_ringbuffer_poll(void *arg, size_t force_len)
138{
139 struct tty_handler *th = arg;
140 int rc;
141
142 rc = tty_drain_queue(th, force_len);
143 if (rc) {
144 console_poller_unregister(th->console, th->poller);
145 return RINGBUFFER_POLL_REMOVE;
146 }
147
148 return RINGBUFFER_POLL_OK;
149}
150
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800151static enum poller_ret tty_poll(struct handler *handler,
152 int events, void __attribute__((unused)) *data)
153{
154 struct tty_handler *th = to_tty_handler(handler);
155 uint8_t buf[4096];
156 ssize_t len;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800157 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800158
Jeremy Kerrf733c852017-02-07 18:40:10 +0800159 if (events & POLLIN) {
160 len = read(th->fd, buf, sizeof(buf));
Jeremy Kerr67eab042017-07-12 12:08:35 +0800161 if (len <= 0)
162 goto err;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800163
Jeremy Kerrf733c852017-02-07 18:40:10 +0800164 console_data_out(th->console, buf, len);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800165 }
166
Jeremy Kerrf733c852017-02-07 18:40:10 +0800167 if (events & POLLOUT) {
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800168 tty_set_blocked(th, false);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800169 rc = tty_drain_queue(th, 0);
Jeremy Kerr67eab042017-07-12 12:08:35 +0800170 if (rc)
171 goto err;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800172 }
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800173
174 return POLLER_OK;
Jeremy Kerr67eab042017-07-12 12:08:35 +0800175
176err:
177 th->poller = NULL;
178 close(th->fd);
179 ringbuffer_consumer_unregister(th->rbc);
180 return POLLER_REMOVE;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800181}
182
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700183static int set_terminal_baud(struct tty_handler *th, const char *tty_name,
184 const char *desired_baud) {
185 struct termios term_options;
186 speed_t speed;
187
Benjamin Fairfcbdea92018-06-04 14:19:25 -0700188 if (config_parse_baud(&speed, desired_baud) != 0) {
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700189 fprintf(stderr, "%s is not a valid baud rate for terminal %s\n",
190 desired_baud, tty_name);
191 return -1;
192 }
193
194 if (tcgetattr(th->fd, &term_options) < 0) {
195 warn("Can't get config for %s", tty_name);
196 return -1;
197 }
198
199 if (cfsetspeed(&term_options, speed) < 0) {
200 warn("Couldn't set speeds for %s", tty_name);
201 return -1;
202 }
203
204 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
205 warn("Couldn't commit terminal options for %s", tty_name);
206 return -1;
207 }
208 printf("Set %s terminal baud rate to %s\n", tty_name, desired_baud);
209
210 return 0;
211}
212
Xo Wang81408bd2017-04-18 16:43:07 -0700213static int make_terminal_raw(struct tty_handler *th, const char *tty_name) {
214 struct termios term_options;
215
216 if (tcgetattr(th->fd, &term_options) < 0) {
217 warn("Can't get config for %s", tty_name);
218 return -1;
219 }
220
221 /* Disable various input and output processing including character
222 * translation, line edit (canonical) mode, flow control, and special signal
223 * generating characters. */
224 cfmakeraw(&term_options);
225
226 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
227 warn("Couldn't commit terminal options for %s", tty_name);
228 return -1;
229 }
230 printf("Set %s for raw byte handling\n", tty_name);
231
232 return 0;
233}
234
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800235static int tty_init(struct handler *handler, struct console *console,
236 struct config *config __attribute__((unused)))
237{
238 struct tty_handler *th = to_tty_handler(handler);
239 const char *tty_name;
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700240 const char *tty_baud;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800241 char *tty_path;
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800242 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800243
244 tty_name = config_get_value(config, "local-tty");
245 if (!tty_name)
246 return -1;
247
248 rc = asprintf(&tty_path, "/dev/%s", tty_name);
249 if (!rc)
250 return -1;
251
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800252 th->fd = open(tty_path, O_RDWR | O_NONBLOCK);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800253 if (th->fd < 0) {
254 warn("Can't open %s; disabling local tty", tty_name);
255 free(tty_path);
256 return -1;
257 }
258
259 free(tty_path);
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800260 th->fd_flags = fcntl(th->fd, F_GETFL, 0);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800261
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700262 tty_baud = config_get_value(config, "local-tty-baud");
263 if (tty_baud != NULL)
264 if (set_terminal_baud(th, tty_name, tty_baud) != 0)
265 fprintf(stderr, "Couldn't set baud rate for %s to %s\n",
266 tty_name, tty_baud);
267
Xo Wang81408bd2017-04-18 16:43:07 -0700268 if (make_terminal_raw(th, tty_name) != 0)
269 fprintf(stderr, "Couldn't make %s a raw terminal\n", tty_name);
270
Jeremy Kerr55c97122017-02-07 17:06:46 +0800271 th->poller = console_poller_register(console, handler, tty_poll,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800272 th->fd, POLLIN, NULL);
273 th->console = console;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800274 th->rbc = console_ringbuffer_consumer_register(console,
275 tty_ringbuffer_poll, th);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800276
277 return 0;
278}
279
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800280static void tty_fini(struct handler *handler)
281{
282 struct tty_handler *th = to_tty_handler(handler);
283 if (th->poller)
Jeremy Kerr55c97122017-02-07 17:06:46 +0800284 console_poller_unregister(th->console, th->poller);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800285 close(th->fd);
286}
287
288static struct tty_handler tty_handler = {
289 .handler = {
290 .name = "tty",
291 .init = tty_init,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800292 .fini = tty_fini,
293 },
294};
295
Jeremy Kerr55c97122017-02-07 17:06:46 +0800296console_handler_register(&tty_handler.handler);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800297