2 * tio - a simple TTY terminal I/O application
4 * Copyright (c) 2014-2016 Martin Lund
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 #include <sys/types.h>
29 #include <sys/param.h>
36 #include "tio/print.h"
37 #include "tio/options.h"
40 #include "tio/error.h"
42 static struct termios new_stdout, old_stdout, old_tio;
43 static long rx_total = 0, tx_total = 0;
44 static bool connected = false;
45 static bool tainted = false;
48 void handle_command_sequence(char input_char, char previous_char, char *output_char, bool *forward)
53 /* Ignore unused arguments */
54 if (output_char == NULL)
55 output_char = &unused_char;
58 forward = &unused_bool;
60 /* Handle escape key commands */
61 if (previous_char == KEY_CTRL_T)
66 /* Exit upon ctrl-t q sequence */
69 /* Send ctrl-t key code upon ctrl-t t sequence */
70 *output_char = KEY_CTRL_T;
73 /* Show tx/rx statistics upon ctrl-t s sequence */
76 color_printf("[tio %s] Sent %ld bytes, received %ld bytes", current_time(), tx_total, rx_total);
81 /* Ignore unknown ctrl-t escaped keys */
88 void wait_for_tty_device(void)
93 static char input_char, previous_char = 0;
94 static bool first = true;
96 /* Loop until device pops up */
101 /* Don't wait first time */
107 /* Wait up to 1 second */
113 FD_SET(STDIN_FILENO, &rdfs);
115 /* Block until input becomes available or timeout */
116 status = select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
119 /* Input from stdin ready */
121 /* Read one character */
122 status = read(STDIN_FILENO, &input_char, 1);
125 error_printf("Could not read from stdin");
129 /* Handle commands */
130 handle_command_sequence(input_char, previous_char, NULL, NULL);
132 previous_char = input_char;
134 } else if (status == -1)
136 error_printf("select() failed (%s)", strerror(errno));
140 /* Test for accessible device file */
141 if (access(option.tty_device, R_OK) == 0)
146 void configure_stdout(void)
148 /* Save current stdout settings */
149 if (tcgetattr(STDOUT_FILENO, &old_stdout) < 0)
151 error_printf("Saving current stdio settings failed");
155 /* Prepare new stdout settings */
156 bzero(&new_stdout, sizeof(new_stdout));
158 /* Control, input, output, local modes for stdout */
159 new_stdout.c_cflag = 0;
160 new_stdout.c_iflag = 0;
161 new_stdout.c_oflag = 0;
162 new_stdout.c_lflag = 0;
164 /* Control characters */
165 new_stdout.c_cc[VTIME] = 0; /* Inter-character timer unused */
166 new_stdout.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
168 /* Activate new stdout settings */
169 tcsetattr(STDOUT_FILENO, TCSANOW, &new_stdout);
170 tcsetattr(STDOUT_FILENO, TCSAFLUSH, &new_stdout);
172 /* Make sure we restore old stdout settings on exit */
173 atexit(&restore_stdout);
176 void restore_stdout(void)
178 tcsetattr(STDOUT_FILENO, TCSANOW, &old_stdout);
179 tcsetattr(STDOUT_FILENO, TCSAFLUSH, &old_stdout);
182 void disconnect_tty(void)
189 color_printf("[tio %s] Disconnected", current_time());
196 void restore_tty(void)
198 tcsetattr(fd, TCSANOW, &old_tio);
199 tcsetattr(fd, TCSAFLUSH, &old_tio);
205 int connect_tty(void)
207 fd_set rdfs; /* Read file descriptor set */
208 int maxfd; /* Maximum file descriptor used */
209 char input_char, output_char;
210 static char previous_char = 0;
211 static bool first = true;
214 /* Open tty device */
215 fd = open(option.tty_device, O_RDWR | O_NOCTTY );
218 error_printf("Could not open tty device (%s)", strerror(errno));
222 /* Make sure device is of tty type */
225 error_printf("Not a tty device");
229 /* Lock device file */
230 status = flock(fd, LOCK_EX | LOCK_NB);
231 if ((status == -1) && (errno == EWOULDBLOCK))
233 printf("Error: Device file is locked by another process\r\n");
237 /* Flush stale I/O data (if any) */
238 tcflush(fd, TCIOFLUSH);
240 /* Print connect status */
241 color_printf("[tio %s] Connected", current_time());
245 /* Save current port settings */
246 if (tcgetattr(fd, &old_tio) < 0)
247 goto error_tcgetattr;
249 /* Make sure we restore tty settings on exit */
252 atexit(&restore_tty);
256 /* Control, input, output, local modes for tty device */
257 option.tio.c_cflag |= CLOCAL | CREAD;
258 option.tio.c_oflag = 0;
259 option.tio.c_lflag = 0;
261 /* Control characters */
262 option.tio.c_cc[VTIME] = 0; /* Inter-character timer unused */
263 option.tio.c_cc[VMIN] = 1; /* Blocking read until 1 character received */
265 /* Activate new port settings */
266 tcsetattr(fd, TCSANOW, &option.tio);
267 tcsetattr(fd, TCSAFLUSH, &option.tio);
269 maxfd = MAX(fd, STDIN_FILENO) + 1; /* Maximum bit entry (fd) to test */
276 FD_SET(STDIN_FILENO, &rdfs);
278 /* Block until input becomes available */
279 status = select(maxfd, &rdfs, NULL, NULL, NULL);
282 if (FD_ISSET(fd, &rdfs))
284 /* Input from tty device ready */
285 if (read(fd, &input_char, 1) > 0)
287 /* Update receive statistics */
290 /* Print received tty character to stdout */
296 log_write(input_char);
302 /* Error reading - device is likely unplugged */
303 error_printf("Could not read from tty device");
307 if (FD_ISSET(STDIN_FILENO, &rdfs))
311 /* Input from stdin ready */
312 status = read(STDIN_FILENO, &input_char, 1);
315 error_printf("Could not read from stdin");
319 /* Forward input to output except ctrl-t key */
320 output_char = input_char;
321 if (input_char == KEY_CTRL_T)
324 /* Handle commands */
325 handle_command_sequence(input_char, previous_char, &output_char, &forward);
329 /* Send output to tty device */
330 status = write(fd, &output_char, 1);
332 warning_printf("Could not write to tty device");
336 log_write(output_char);
338 /* Update transmit statistics */
341 /* Insert output delay */
342 delay(option.output_delay);
345 /* Save previous key */
346 previous_char = input_char;
349 } else if (status == -1)
351 error_printf("Error: select() failed (%s)", strerror(errno));