blob: 22f9a751636c3c028e93a4ec903d98a9fec31d9d [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 Kerrbc1e8932016-04-28 12:27:30 +080037};
38
Xo Wangc5ef8ea2017-04-17 16:20:43 -070039struct terminal_speed_name {
40 speed_t speed;
41 const char *name;
42};
43
Jeremy Kerrbc1e8932016-04-28 12:27:30 +080044static struct tty_handler *to_tty_handler(struct handler *handler)
45{
46 return container_of(handler, struct tty_handler, handler);
47}
48
Jeremy Kerrf733c852017-02-07 18:40:10 +080049static int tty_drain_queue(struct tty_handler *th, size_t force_len)
50{
51 size_t len, total_len;
52 ssize_t wlen;
53 uint8_t *buf;
54 int flags;
55
56 /* if we're forcing data, we need to clear non-blocking mode */
57 if (force_len) {
58 flags = fcntl(th->fd, F_GETFL, 0);
59 if (flags & O_NONBLOCK) {
60 flags &= ~O_NONBLOCK;
61 fcntl(th->fd, F_SETFL, flags);
62 }
63 }
64
65 total_len = 0;
66
67 for (;;) {
68 len = ringbuffer_dequeue_peek(th->rbc, total_len, &buf);
69 if (!len)
70 break;
71
72 /* write as little as possible while blocking */
73 if (force_len && force_len < total_len + len)
74 len = force_len - total_len;
75
76 wlen = write(th->fd, buf, len);
77 if (wlen < 0) {
78 if (errno == EINTR)
79 continue;
80 if ((errno == EAGAIN || errno == EWOULDBLOCK)
81 && !force_len)
82 break;
83 warn("failed writing to local tty; disabling");
84 return -1;
85 }
86
87 total_len += wlen;
88
89 if (force_len && total_len >= force_len)
90 break;
91 }
92
93 ringbuffer_dequeue_commit(th->rbc, total_len);
94
95 if (force_len)
96 fcntl(th->fd, F_SETFL, flags | O_NONBLOCK);
97
98 return 0;
99}
100
101static enum ringbuffer_poll_ret tty_ringbuffer_poll(void *arg, size_t force_len)
102{
103 struct tty_handler *th = arg;
104 int rc;
105
106 rc = tty_drain_queue(th, force_len);
107 if (rc) {
108 console_poller_unregister(th->console, th->poller);
109 return RINGBUFFER_POLL_REMOVE;
110 }
111
112 return RINGBUFFER_POLL_OK;
113}
114
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800115static enum poller_ret tty_poll(struct handler *handler,
116 int events, void __attribute__((unused)) *data)
117{
118 struct tty_handler *th = to_tty_handler(handler);
119 uint8_t buf[4096];
120 ssize_t len;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800121 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800122
Jeremy Kerrf733c852017-02-07 18:40:10 +0800123 if (events & POLLIN) {
124 len = read(th->fd, buf, sizeof(buf));
125 if (len <= 0) {
126 th->poller = NULL;
127 close(th->fd);
128 return POLLER_REMOVE;
129 }
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800130
Jeremy Kerrf733c852017-02-07 18:40:10 +0800131 console_data_out(th->console, buf, len);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800132 }
133
Jeremy Kerrf733c852017-02-07 18:40:10 +0800134 if (events & POLLOUT) {
135 rc = tty_drain_queue(th, 0);
136 if (rc) {
137 ringbuffer_consumer_unregister(th->rbc);
138 return POLLER_REMOVE;
139 }
140 }
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800141
142 return POLLER_OK;
143}
144
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700145static int baud_string_to_speed(speed_t *speed, const char *baud_string) {
146 const struct terminal_speed_name terminal_speeds[] = {
147 { B50, "50" },
148 { B75, "75" },
149 { B110, "110" },
150 { B134, "134" },
151 { B150, "150" },
152 { B200, "200" },
153 { B300, "300" },
154 { B600, "600" },
155 { B1200, "1200" },
156 { B1800, "1800" },
157 { B2400, "2400" },
158 { B4800, "4800" },
159 { B9600, "9600" },
160 { B19200, "19200" },
161 { B38400, "38400" },
162 { B57600, "57600" },
163 { B115200, "115200" },
164 { B230400, "230400" },
165 { B460800, "460800" },
166 { B500000, "500000" },
167 { B576000, "576000" },
168 { B921600, "921600" },
169 { B1000000, "1000000" },
170 { B1152000, "1152000" },
171 { B1500000, "1500000" },
172 { B2000000, "2000000" },
173 { B2500000, "2500000" },
174 { B3000000, "3000000" },
175 { B3500000, "3500000" },
176 { B4000000, "4000000" },
177 };
178 const size_t num_terminal_speeds = sizeof(terminal_speeds) /
179 sizeof(struct terminal_speed_name);
180 size_t i;
181
182 for (i = 0; i < num_terminal_speeds; i++) {
183 if (strcmp(baud_string, terminal_speeds[i].name) == 0) {
184 *speed = terminal_speeds[i].speed;
185 return 0;
186 }
187 }
188 return -1;
189}
190
191static int set_terminal_baud(struct tty_handler *th, const char *tty_name,
192 const char *desired_baud) {
193 struct termios term_options;
194 speed_t speed;
195
196 if (baud_string_to_speed(&speed, desired_baud) != 0) {
197 fprintf(stderr, "%s is not a valid baud rate for terminal %s\n",
198 desired_baud, tty_name);
199 return -1;
200 }
201
202 if (tcgetattr(th->fd, &term_options) < 0) {
203 warn("Can't get config for %s", tty_name);
204 return -1;
205 }
206
207 if (cfsetspeed(&term_options, speed) < 0) {
208 warn("Couldn't set speeds for %s", tty_name);
209 return -1;
210 }
211
212 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
213 warn("Couldn't commit terminal options for %s", tty_name);
214 return -1;
215 }
216 printf("Set %s terminal baud rate to %s\n", tty_name, desired_baud);
217
218 return 0;
219}
220
Xo Wang81408bd2017-04-18 16:43:07 -0700221static int make_terminal_raw(struct tty_handler *th, const char *tty_name) {
222 struct termios term_options;
223
224 if (tcgetattr(th->fd, &term_options) < 0) {
225 warn("Can't get config for %s", tty_name);
226 return -1;
227 }
228
229 /* Disable various input and output processing including character
230 * translation, line edit (canonical) mode, flow control, and special signal
231 * generating characters. */
232 cfmakeraw(&term_options);
233
234 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) {
235 warn("Couldn't commit terminal options for %s", tty_name);
236 return -1;
237 }
238 printf("Set %s for raw byte handling\n", tty_name);
239
240 return 0;
241}
242
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800243static int tty_init(struct handler *handler, struct console *console,
244 struct config *config __attribute__((unused)))
245{
246 struct tty_handler *th = to_tty_handler(handler);
247 const char *tty_name;
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700248 const char *tty_baud;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800249 char *tty_path;
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800250 int rc;
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800251
252 tty_name = config_get_value(config, "local-tty");
253 if (!tty_name)
254 return -1;
255
256 rc = asprintf(&tty_path, "/dev/%s", tty_name);
257 if (!rc)
258 return -1;
259
Jeremy Kerrbc506fd2017-02-07 09:24:48 +0800260 th->fd = open(tty_path, O_RDWR | O_NONBLOCK);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800261 if (th->fd < 0) {
262 warn("Can't open %s; disabling local tty", tty_name);
263 free(tty_path);
264 return -1;
265 }
266
267 free(tty_path);
268
Xo Wangc5ef8ea2017-04-17 16:20:43 -0700269 tty_baud = config_get_value(config, "local-tty-baud");
270 if (tty_baud != NULL)
271 if (set_terminal_baud(th, tty_name, tty_baud) != 0)
272 fprintf(stderr, "Couldn't set baud rate for %s to %s\n",
273 tty_name, tty_baud);
274
Xo Wang81408bd2017-04-18 16:43:07 -0700275 if (make_terminal_raw(th, tty_name) != 0)
276 fprintf(stderr, "Couldn't make %s a raw terminal\n", tty_name);
277
Jeremy Kerr55c97122017-02-07 17:06:46 +0800278 th->poller = console_poller_register(console, handler, tty_poll,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800279 th->fd, POLLIN, NULL);
280 th->console = console;
Jeremy Kerrf733c852017-02-07 18:40:10 +0800281 th->rbc = console_ringbuffer_consumer_register(console,
282 tty_ringbuffer_poll, th);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800283
284 return 0;
285}
286
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800287static void tty_fini(struct handler *handler)
288{
289 struct tty_handler *th = to_tty_handler(handler);
290 if (th->poller)
Jeremy Kerr55c97122017-02-07 17:06:46 +0800291 console_poller_unregister(th->console, th->poller);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800292 close(th->fd);
293}
294
295static struct tty_handler tty_handler = {
296 .handler = {
297 .name = "tty",
298 .init = tty_init,
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800299 .fini = tty_fini,
300 },
301};
302
Jeremy Kerr55c97122017-02-07 17:06:46 +0800303console_handler_register(&tty_handler.handler);
Jeremy Kerrbc1e8932016-04-28 12:27:30 +0800304