1 /** 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 #include <assert.h> 18 #include <err.h> 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <termios.h> 26 27 #include "console-server.h" 28 #include "config.h" 29 30 struct tty_handler { 31 struct handler handler; 32 struct console *console; 33 struct ringbuffer_consumer *rbc; 34 struct poller *poller; 35 int fd; 36 int fd_flags; 37 bool blocked; 38 }; 39 40 static struct tty_handler *to_tty_handler(struct handler *handler) 41 { 42 return container_of(handler, struct tty_handler, handler); 43 } 44 45 static void tty_set_fd_blocking(struct tty_handler *th, bool fd_blocking) 46 { 47 int flags; 48 49 flags = th->fd_flags & ~O_NONBLOCK; 50 if (!fd_blocking) { 51 flags |= O_NONBLOCK; 52 } 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 */ 69 static void tty_set_blocked(struct tty_handler *th, bool blocked) 70 { 71 int events; 72 73 if (blocked == th->blocked) { 74 return; 75 } 76 77 th->blocked = blocked; 78 events = POLLIN; 79 80 if (th->blocked) { 81 events |= POLLOUT; 82 } 83 84 console_poller_set_events(th->console, th->poller, events); 85 } 86 87 static int tty_drain_queue(struct tty_handler *th, size_t force_len) 88 { 89 size_t len; 90 size_t total_len; 91 ssize_t wlen; 92 uint8_t *buf; 93 94 /* if we're forcing data, we need to clear non-blocking mode */ 95 if (force_len) { 96 tty_set_fd_blocking(th, true); 97 98 /* no point writing, we'll just see -EAGAIN */ 99 } else if (th->blocked) { 100 return 0; 101 } 102 103 total_len = 0; 104 105 for (;;) { 106 len = ringbuffer_dequeue_peek(th->rbc, total_len, &buf); 107 if (!len) { 108 break; 109 } 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 116 wlen = write(th->fd, buf, len); 117 if (wlen < 0) { 118 if (errno == EINTR) { 119 continue; 120 } 121 if ((errno == EAGAIN || errno == EWOULDBLOCK) && 122 !force_len) { 123 tty_set_blocked(th, true); 124 break; 125 } 126 warn("failed writing to local tty; disabling"); 127 return -1; 128 } 129 130 total_len += wlen; 131 132 if (force_len && total_len >= force_len) { 133 break; 134 } 135 } 136 137 ringbuffer_dequeue_commit(th->rbc, total_len); 138 139 if (force_len) { 140 tty_set_fd_blocking(th, false); 141 } 142 143 return 0; 144 } 145 146 static enum ringbuffer_poll_ret tty_ringbuffer_poll(void *arg, size_t force_len) 147 { 148 struct tty_handler *th = arg; 149 int rc; 150 151 rc = tty_drain_queue(th, force_len); 152 if (rc) { 153 console_poller_unregister(th->console, th->poller); 154 return RINGBUFFER_POLL_REMOVE; 155 } 156 157 return RINGBUFFER_POLL_OK; 158 } 159 160 static enum poller_ret tty_poll(struct handler *handler, int events, 161 void __attribute__((unused)) * data) 162 { 163 struct tty_handler *th = to_tty_handler(handler); 164 uint8_t buf[4096]; 165 ssize_t len; 166 int rc; 167 168 if (events & POLLIN) { 169 len = read(th->fd, buf, sizeof(buf)); 170 if (len <= 0) { 171 goto err; 172 } 173 174 console_data_out(th->console, buf, len); 175 } 176 177 if (events & POLLOUT) { 178 tty_set_blocked(th, false); 179 rc = tty_drain_queue(th, 0); 180 if (rc) { 181 goto err; 182 } 183 } 184 185 return POLLER_OK; 186 187 err: 188 th->poller = NULL; 189 close(th->fd); 190 ringbuffer_consumer_unregister(th->rbc); 191 return POLLER_REMOVE; 192 } 193 194 static int set_terminal_baud(struct tty_handler *th, const char *tty_name, 195 speed_t speed) 196 { 197 struct termios term_options; 198 199 if (tcgetattr(th->fd, &term_options) < 0) { 200 warn("Can't get config for %s", tty_name); 201 return -1; 202 } 203 204 if (cfsetspeed(&term_options, speed) < 0) { 205 warn("Couldn't set speeds for %s", tty_name); 206 return -1; 207 } 208 209 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) { 210 warn("Couldn't commit terminal options for %s", tty_name); 211 return -1; 212 } 213 214 return 0; 215 } 216 217 static int make_terminal_raw(struct tty_handler *th, const char *tty_name) 218 { 219 struct termios term_options; 220 221 if (tcgetattr(th->fd, &term_options) < 0) { 222 warn("Can't get config for %s", tty_name); 223 return -1; 224 } 225 226 /* Disable various input and output processing including character 227 * translation, line edit (canonical) mode, flow control, and special signal 228 * generating characters. */ 229 cfmakeraw(&term_options); 230 231 if (tcsetattr(th->fd, TCSAFLUSH, &term_options) < 0) { 232 warn("Couldn't commit terminal options for %s", tty_name); 233 return -1; 234 } 235 printf("Set %s for raw byte handling\n", tty_name); 236 237 return 0; 238 } 239 240 static struct handler *tty_init(const struct handler_type *type 241 __attribute__((unused)), 242 struct console *console, 243 struct config *config __attribute__((unused))) 244 { 245 struct tty_handler *th; 246 speed_t desired_speed; 247 const char *tty_name; 248 const char *tty_baud; 249 char *tty_path; 250 int rc; 251 252 tty_name = config_get_value(config, "local-tty"); 253 if (!tty_name) { 254 return NULL; 255 } 256 257 rc = asprintf(&tty_path, "/dev/%s", tty_name); 258 if (!rc) { 259 return NULL; 260 } 261 262 th = malloc(sizeof(*th)); 263 if (!th) { 264 return NULL; 265 } 266 267 th->fd = open(tty_path, O_RDWR | O_NONBLOCK); 268 if (th->fd < 0) { 269 warn("Can't open %s; disabling local tty", tty_name); 270 free(tty_path); 271 free(th); 272 return NULL; 273 } 274 275 free(tty_path); 276 th->fd_flags = fcntl(th->fd, F_GETFL, 0); 277 278 tty_baud = config_get_value(config, "local-tty-baud"); 279 if (tty_baud != NULL) { 280 rc = config_parse_baud(&desired_speed, tty_baud); 281 if (rc) { 282 fprintf(stderr, "%s is not a valid baud rate\n", 283 tty_baud); 284 } else { 285 rc = set_terminal_baud(th, tty_name, desired_speed); 286 if (rc) { 287 fprintf(stderr, 288 "Couldn't set baud rate for %s to %s\n", 289 tty_name, tty_baud); 290 } 291 } 292 } 293 294 if (make_terminal_raw(th, tty_name) != 0) { 295 fprintf(stderr, "Couldn't make %s a raw terminal\n", tty_name); 296 } 297 298 th->poller = console_poller_register(console, &th->handler, tty_poll, 299 NULL, th->fd, POLLIN, NULL); 300 th->console = console; 301 th->rbc = console_ringbuffer_consumer_register(console, 302 tty_ringbuffer_poll, th); 303 304 return &th->handler; 305 } 306 307 static void tty_fini(struct handler *handler) 308 { 309 struct tty_handler *th = to_tty_handler(handler); 310 if (th->poller) { 311 console_poller_unregister(th->console, th->poller); 312 } 313 close(th->fd); 314 free(th); 315 } 316 317 static int tty_baudrate(struct handler *handler, speed_t baudrate) 318 { 319 const char *tty_name = "local-tty"; 320 struct tty_handler *th = to_tty_handler(handler); 321 322 if (baudrate == 0) { 323 return -1; 324 } 325 326 if (set_terminal_baud(th, tty_name, baudrate) != 0) { 327 fprintf(stderr, "Couldn't set baud rate for %s to %d\n", 328 tty_name, baudrate); 329 return -1; 330 } 331 return 0; 332 } 333 334 static const struct handler_type tty_handler = { 335 .name = "tty", 336 .init = tty_init, 337 .fini = tty_fini, 338 .baudrate = tty_baudrate, 339 }; 340 341 console_handler_register(&tty_handler); 342