blob: dd348b65ddc4fbb61780d82b79e0ee072f9dbf44 [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
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080017#include <assert.h>
18#include <err.h>
Jeremy Kerrf733c852017-02-07 18:40:10 +080019#include <errno.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080020#include <fcntl.h>
21#include <stdio.h>
22#include <stdlib.h>
Xo Wangc5ef8ea2017-04-17 16:20:43 -070023#include <string.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080024#include <unistd.h>
Xo Wangc5ef8ea2017-04-17 16:20:43 -070025#include <termios.h>
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080026
27#include "console-server.h"
28
29struct tty_handler {
Andrew Jefferya72711a2023-04-18 18:19:41 +093030 struct handler handler;
31 struct console *console;
32 struct ringbuffer_consumer *rbc;
33 struct poller *poller;
34 int fd;
35 int fd_flags;
36 bool blocked;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080037};
38
39static struct tty_handler *to_tty_handler(struct handler *handler)
40{
41 return container_of(handler, struct tty_handler, handler);
42}
43
Jeremy Kerr6b1fed22017-02-07 21:40:38 +080044static void tty_set_fd_blocking(struct tty_handler *th, bool fd_blocking)
45{
46 int flags;
47
48 flags = th->fd_flags & ~O_NONBLOCK;
49 if (!fd_blocking)
50 flags |= O_NONBLOCK;
51
52 if (flags != th->fd_flags) {
53 fcntl(th->fd, F_SETFL, flags);
54 th->fd_flags = flags;
55 }
56}
57
58/*
59 * A "blocked" handler indicates that the last write returned EAGAIN
60 * (==EWOULDBLOCK), so we know not to continue writing (for non-forced output),
61 * as it'll just return EAGAIN again.
62 *
63 * Once we detect this, we watch for POLLOUT in the poller events. A
64 * POLLOUT indicates that the fd is no longer blocking, so we clear
65 * blocked mode and can continue writing.
66 */
67static void tty_set_blocked(struct tty_handler *th, bool blocked)
68{
69 int events;
70
71 if (blocked == th->blocked)
72 return;
73
74 th->blocked = blocked;
75 events = POLLIN;
76
77 if (th->blocked)
78 events |= POLLOUT;
79
80 console_poller_set_events(th->console, th->poller, events);
81}
82
Jeremy Kerrf733c852017-02-07 18:40:10 +080083static int tty_drain_queue(struct tty_handler *th, size_t force_len)
84{
85 size_t len, total_len;
86 ssize_t wlen;
87 uint8_t *buf;
Jeremy Kerrf733c852017-02-07 18:40:10 +080088
89 /* if we're forcing data, we need to clear non-blocking mode */
Jeremy Kerr6b1fed22017-02-07 21:40:38 +080090 if (force_len)
91 tty_set_fd_blocking(th, true);
92
93 /* no point writing, we'll just see -EAGAIN */
94 else if (th->blocked)
95 return 0;
Jeremy Kerrf733c852017-02-07 18:40:10 +080096
97 total_len = 0;
98
99 for (;;) {
100 len = ringbuffer_dequeue_peek(th->rbc, total_len, &buf);
101 if (!len)
102 break;
103
104 /* write as little as possible while blocking */
105 if (force_len && force_len < total_len + len)
106 len = force_len - total_len;
107
108 wlen = write(th->fd, buf, len);
109 if (wlen < 0) {
110 if (errno == EINTR)
111 continue;
Andrew Jefferya72711a2023-04-18 18:19:41 +0930112 if ((errno == EAGAIN || errno == EWOULDBLOCK) &&
113 !force_len) {
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800114 tty_set_blocked(th, true);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800115 break;
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800116 }
Jeremy Kerrf733c852017-02-07 18:40:10 +0800117 warn("failed writing to local tty; disabling");
118 return -1;
119 }
120
121 total_len += wlen;
122
123 if (force_len && total_len >= force_len)
124 break;
125 }
126
127 ringbuffer_dequeue_commit(th->rbc, total_len);
128
129 if (force_len)
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800130 tty_set_fd_blocking(th, false);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800131
132 return 0;
133}
134
135static enum ringbuffer_poll_ret tty_ringbuffer_poll(void *arg, size_t force_len)
136{
137 struct tty_handler *th = arg;
138 int rc;
139
140 rc = tty_drain_queue(th, force_len);
141 if (rc) {
142 console_poller_unregister(th->console, th->poller);
143 return RINGBUFFER_POLL_REMOVE;
144 }
145
146 return RINGBUFFER_POLL_OK;
147}
148
Andrew Jefferya72711a2023-04-18 18:19:41 +0930149static enum poller_ret tty_poll(struct handler *handler, int events,
150 void __attribute__((unused)) * data)
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800151{
152 struct tty_handler *th = to_tty_handler(handler);
153 uint8_t buf[4096];
154 ssize_t len;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800155 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800156
Jeremy Kerrf733c852017-02-07 18:40:10 +0800157 if (events & POLLIN) {
158 len = read(th->fd, buf, sizeof(buf));
Jeremy Kerr67eab042017-07-12 12:08:35 +0800159 if (len <= 0)
160 goto err;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800161
Jeremy Kerrf733c852017-02-07 18:40:10 +0800162 console_data_out(th->console, buf, len);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800163 }
164
Jeremy Kerrf733c852017-02-07 18:40:10 +0800165 if (events & POLLOUT) {
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800166 tty_set_blocked(th, false);
Jeremy Kerrf733c852017-02-07 18:40:10 +0800167 rc = tty_drain_queue(th, 0);
Jeremy Kerr67eab042017-07-12 12:08:35 +0800168 if (rc)
169 goto err;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800170 }
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800171
172 return POLLER_OK;
Jeremy Kerr67eab042017-07-12 12:08:35 +0800173
174err:
175 th->poller = NULL;
176 close(th->fd);
177 ringbuffer_consumer_unregister(th->rbc);
178 return POLLER_REMOVE;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800179}
180
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700181static int set_terminal_baud(struct tty_handler *th, const char *tty_name,
Andrew Jefferya72711a2023-04-18 18:19:41 +0930182 speed_t speed)
183{
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700184 struct termios term_options;
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700185
186 if (tcgetattr(th->fd, &term_options) < 0) {
187 warn("Can't get config for %s", tty_name);
188 return -1;
189 }
190
191 if (cfsetspeed(&term_options, speed) < 0) {
192 warn("Couldn't set speeds for %s", tty_name);
193 return -1;
194 }
195
196 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
197 warn("Couldn't commit terminal options for %s", tty_name);
198 return -1;
199 }
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700200
201 return 0;
202}
203
Andrew Jefferya72711a2023-04-18 18:19:41 +0930204static int make_terminal_raw(struct tty_handler *th, const char *tty_name)
205{
Xo Wang81408bd2017-04-18 16:43:07 -0700206 struct termios term_options;
207
208 if (tcgetattr(th->fd, &term_options) < 0) {
209 warn("Can't get config for %s", tty_name);
210 return -1;
211 }
212
213 /* Disable various input and output processing including character
214 * translation, line edit (canonical) mode, flow control, and special signal
215 * generating characters. */
216 cfmakeraw(&term_options);
217
218 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
219 warn("Couldn't commit terminal options for %s", tty_name);
220 return -1;
221 }
222 printf("Set %s for raw byte handling\n", tty_name);
223
224 return 0;
225}
226
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800227static int tty_init(struct handler *handler, struct console *console,
Andrew Jefferya72711a2023-04-18 18:19:41 +0930228 struct config *config __attribute__((unused)))
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800229{
230 struct tty_handler *th = to_tty_handler(handler);
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800231 speed_t desired_speed;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800232 const char *tty_name;
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700233 const char *tty_baud;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800234 char *tty_path;
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800235 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800236
237 tty_name = config_get_value(config, "local-tty");
238 if (!tty_name)
239 return -1;
240
241 rc = asprintf(&tty_path, "/dev/%s", tty_name);
242 if (!rc)
243 return -1;
244
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800245 th->fd = open(tty_path, O_RDWR | O_NONBLOCK);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800246 if (th->fd < 0) {
247 warn("Can't open %s; disabling local tty", tty_name);
248 free(tty_path);
249 return -1;
250 }
251
252 free(tty_path);
Jeremy Kerr6b1fed22017-02-07 21:40:38 +0800253 th->fd_flags = fcntl(th->fd, F_GETFL, 0);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800254
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700255 tty_baud = config_get_value(config, "local-tty-baud");
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800256 if (tty_baud != NULL) {
257 rc = config_parse_baud(&desired_speed, tty_baud);
258 if (rc) {
259 fprintf(stderr, "%s is not a valid baud rate\n",
260 tty_baud);
261 } else {
262 rc = set_terminal_baud(th, tty_name, desired_speed);
263 if (rc)
Andrew Jefferya72711a2023-04-18 18:19:41 +0930264 fprintf(stderr,
265 "Couldn't set baud rate for %s to %s\n",
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700266 tty_name, tty_baud);
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800267 }
268 }
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700269
Xo Wang81408bd2017-04-18 16:43:07 -0700270 if (make_terminal_raw(th, tty_name) != 0)
271 fprintf(stderr, "Couldn't make %s a raw terminal\n", tty_name);
272
Johnathan Mantey1cecc5d2019-02-28 15:01:46 -0800273 th->poller = console_poller_register(console, handler, tty_poll, NULL,
Andrew Jefferya72711a2023-04-18 18:19:41 +0930274 th->fd, POLLIN, NULL);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800275 th->console = console;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800276 th->rbc = console_ringbuffer_consumer_register(console,
Andrew Jefferya72711a2023-04-18 18:19:41 +0930277 tty_ringbuffer_poll, th);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800278
279 return 0;
280}
281
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800282static void tty_fini(struct handler *handler)
283{
284 struct tty_handler *th = to_tty_handler(handler);
285 if (th->poller)
Jeremy Kerr55c97122017-02-07 17:06:46 +0800286 console_poller_unregister(th->console, th->poller);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800287 close(th->fd);
288}
289
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800290static int tty_baudrate(struct handler *handler, speed_t baudrate)
291{
292 const char *tty_name = "local-tty";
293 struct tty_handler *th = to_tty_handler(handler);
294
295 if (baudrate == 0) {
296 return -1;
297 }
298
299 if (set_terminal_baud(th, tty_name, baudrate) != 0) {
300 fprintf(stderr, "Couldn't set baud rate for %s to %d\n",
301 tty_name, baudrate);
302 return -1;
303 }
304 return 0;
305}
306
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800307static struct tty_handler tty_handler = {
308 .handler = {
309 .name = "tty",
310 .init = tty_init,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800311 .fini = tty_fini,
Cheng C Yangf9c8f6c2019-03-04 18:39:52 +0800312 .baudrate = tty_baudrate,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800313 },
314};
315
Jeremy Kerr55c97122017-02-07 17:06:46 +0800316console_handler_register(&tty_handler.handler);