4 * This is a minimalist terminal program like minicom or cu. The only
5 * thing it does is creating a bidirectional connection between
6 * stdin/stdout and a device (e.g. serial terminal). It can also set
7 * serial line baudrate and manipulate DTR/RTS modem lines.
9 * Copyright 2014 Michal Sojka <sojkam1@fel.cvut.cz>
11 * This program is free software: you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation, either version 3 of the
14 * License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see
23 * <http://www.gnu.org/licenses/>.
27 #include <sys/ioctl.h>
39 #define STRINGIFY(val) #val
40 #define TOSTRING(val) STRINGIFY(val)
41 #define CHECK(cmd) ({ int ret = (cmd); if (ret == -1) { perror(#cmd " line " TOSTRING(__LINE__)); exit(1); }; ret; })
42 #define CHECKPTR(cmd) ({ void *ptr = (cmd); if (ptr == (void*)-1) { perror(#cmd " line " TOSTRING(__LINE__)); exit(1); }; ptr; })
44 #define VERBOSE(format, ...) do { if (verbose) fprintf(stderr, format, ##__VA_ARGS__); } while (0)
48 char template[] = "/var/lock/TMPXXXXXX";
50 struct termios stdin_tio_backup;
52 void rm_file(int status, void *arg)
60 void restore_stdin_term()
62 tcsetattr(0, TCSANOW, &stdin_tio_backup);
65 void sighandler(int arg)
71 int main(int argc, char *argv[])
83 if ((stdin_tty = isatty(0))) {
84 CHECK(tcgetattr(0, &stdin_tio_backup));
85 atexit(restore_stdin_term);
88 while ((opt = getopt(argc, argv, "ndrs:v")) != -1) {
101 #define S(s) case s: speed = B##s; break;
123 fprintf(stderr, "Unknown baud rate %d\n", s);
132 fprintf(stderr, "Usage: %s [-s baudrate] [-v] <device>\n", argv[0]);
141 fprintf(stderr, "No device specified\n");
145 signal(SIGINT, sighandler);
146 signal(SIGTERM, sighandler);
147 signal(SIGHUP, sighandler);
149 if (strncmp(dev, "/dev/", 5) == 0 &&
150 strrchr(dev, '/') == dev + 4 &&
152 { /* Create lock file (to be inter-operable with other programs) */
153 /* This is racy, but what we can do - see also comments in uucp / cu */
154 int tmp = CHECK(mkstemp(template));
155 on_exit(rm_file, template);
157 snprintf(pid, sizeof(pid), "%u", getpid());
158 CHECK(write(tmp, pid, strlen(pid)));
160 snprintf(lockfile, sizeof(lockfile), "/var/lock/LCK..%s", dev + 5);
161 if (link(template, lockfile) == -1) {
165 rm_file(0, template);
166 on_exit(rm_file, lockfile);
169 if ((fd = open(dev, O_RDWR)) < 0) {
175 CHECK(ioctl(fd, TIOCEXCL, NULL));
177 CHECK(tcgetattr(fd, &tio));
182 CHECK(cfsetospeed(&tio, speed));
183 CHECK(cfsetispeed(&tio, speed));
188 tio.c_cflag &= ~HUPCL;
190 CHECK(ioctl(fd, TIOCMGET, &status));
191 if (dtr == +1) status &= ~TIOCM_DTR;
192 if (dtr == -1) status |= TIOCM_DTR;
193 if (rts == +1) status &= ~TIOCM_RTS;
194 if (rts == -1) status |= TIOCM_RTS;
195 CHECK(ioctl(fd, TIOCMSET, &status));
198 CHECK(tcsetattr(fd, TCSANOW, &tio));
199 } else if (speed || dtr || rts) {
200 fprintf(stderr, "Cannot set speed, DTR or RTS on non-terminal %s\n", dev);
204 struct pollfd fds[2] = {
205 { .fd = 0, .events = POLLIN },
206 { .fd = fd, .events = POLLIN },
211 tio = stdin_tio_backup;
214 CHECK(tcsetattr(0, TCSANOW, &tio));
217 VERBOSE("Connected.\n");
220 ret = CHECK(poll(fds, 2, -1));
221 if (fds[0].revents & POLLIN) {
222 r1 = CHECK(read(0, buf, sizeof(buf)));
224 VERBOSE("EOF on stdin\n");
227 r2 = CHECK(write(fd, buf, r1));
229 fprintf(stderr, "Not all data written to %s (%d/%d)\n", dev, r1, r2);
233 if (fds[1].revents & POLLIN) {
234 r1 = CHECK(read(fd, buf, sizeof(buf)));
236 VERBOSE("EOF on %s\n", dev);
239 r2 = CHECK(write(1, buf, r1));
241 fprintf(stderr, "Not all data written to stdout (%d/%d)\n", r1, r2);