blob: 00a49837ea65feca0b1dab9c1074b8553b3ab6bc [file] [log] [blame]
Bernhard Reutner-Fischerf588f702007-09-22 20:35:32 +00001/* vi: set sw=4 ts=4: */
2/*
Denis Vlasenko70766952007-09-21 17:42:40 +00003 * bare bones 'talk to modem' program - similar to 'cu -l $device'
4 * inspired by mgetty's microcom
5 *
Denis Vlasenkof34e8212007-09-22 20:23:57 +00006 * Copyright (C) 2007 by Vladimir Dronnikov <dronnikov@gmail.ru>
7 *
8 * Licensed under GPLv2, see file LICENSE in this tarball for details.
Denis Vlasenko70766952007-09-21 17:42:40 +00009 */
Denis Vlasenko9a4e08e2007-09-22 21:37:27 +000010#include "libbb.h"
Denis Vlasenko70766952007-09-21 17:42:40 +000011
Denis Vlasenkoef67c572008-01-27 22:40:39 +000012/* All known arches use small ints for signals */
13static volatile smallint signalled;
14
15static void signal_handler(int signo)
16{
17 signalled = signo;
18}
19
20// set canonical tty mode
21static void xget1(int fd, struct termios *t, struct termios *oldt)
22{
23 tcgetattr(fd, oldt);
24 *t = *oldt;
25 cfmakeraw(t);
26// t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
27// t->c_iflag &= ~(BRKINT|IXON|ICRNL);
28// t->c_oflag &= ~(ONLCR);
29// t->c_cc[VMIN] = 1;
30// t->c_cc[VTIME] = 0;
31}
32
33static int xset1(int fd, struct termios *tio, const char *device)
34{
35 int ret = tcsetattr(fd, TCSANOW, tio);
36
37 if (ret) {
38 bb_perror_msg("can't tcsetattr for %s", device);
39 }
40 return ret;
41}
42
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000043int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko70766952007-09-21 17:42:40 +000044int microcom_main(int argc, char **argv)
45{
46 struct pollfd pfd[2];
Denis Vlasenkoef67c572008-01-27 22:40:39 +000047 int nfd;
Denis Vlasenko0effc242008-01-28 09:39:30 +000048 int sfd;
49/* #define sfd (pfd[0].fd) - gcc 4.2.1 is still not smart enough */
Denis Vlasenkoef67c572008-01-27 22:40:39 +000050 char *device_lock_file;
Denis Vlasenko70766952007-09-21 17:42:40 +000051 const char *opt_s = "9600";
Denis Vlasenkoef67c572008-01-27 22:40:39 +000052 speed_t speed;
53 const char *opt_t = "100"; // 0.1 sec timeout
54 unsigned timeout;
Denis Vlasenko70766952007-09-21 17:42:40 +000055 struct termios tio0, tiosfd, tio;
Denis Vlasenkoef67c572008-01-27 22:40:39 +000056 bool istty;
Denis Vlasenko70766952007-09-21 17:42:40 +000057
Denis Vlasenkoef67c572008-01-27 22:40:39 +000058 // fetch options
59 opt_complementary = "-1"; /* at least one arg should be there */
60 getopt32(argv, "s:t:", &opt_s, &opt_t);
Denis Vlasenko70766952007-09-21 17:42:40 +000061 argc -= optind;
62 argv += optind;
Denis Vlasenkoef67c572008-01-27 22:40:39 +000063
64 // check sanity
Denis Vlasenko70766952007-09-21 17:42:40 +000065 speed = xatou(opt_s);
Denis Vlasenkoef67c572008-01-27 22:40:39 +000066 timeout = xatou(opt_t);
67 device_lock_file = (char *)bb_basename(argv[0]);
Denis Vlasenko70766952007-09-21 17:42:40 +000068
69 // try to create lock file in /var/lock
Denis Vlasenkoef67c572008-01-27 22:40:39 +000070 device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
Denis Vlasenko70766952007-09-21 17:42:40 +000071 sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
72 if (sfd < 0) {
Denis Vlasenko70766952007-09-21 17:42:40 +000073 if (errno == EEXIST)
74 bb_perror_msg_and_die("can't lock device");
75 // We don't abort on other errors: /var/lock can be
76 // non-writable or non-existent
Denis Vlasenkoef67c572008-01-27 22:40:39 +000077 if (ENABLE_FEATURE_CLEAN_UP)
78 free(device_lock_file);
79 device_lock_file = NULL;
Denis Vlasenko70766952007-09-21 17:42:40 +000080 } else {
81 // %4d to make mgetty happy. It treats 4-bytes lock files as binary,
82 // not text, PID. Making 5+ char file. Brrr...
Denis Vlasenkoef67c572008-01-27 22:40:39 +000083 char *s = xasprintf("%4d\n", getpid());
Denis Vlasenko70766952007-09-21 17:42:40 +000084 write(sfd, s, strlen(s));
85 if (ENABLE_FEATURE_CLEAN_UP)
Denis Vlasenkoef67c572008-01-27 22:40:39 +000086 free(s);
Denis Vlasenko70766952007-09-21 17:42:40 +000087 close(sfd);
88 }
89
Denis Vlasenkoef67c572008-01-27 22:40:39 +000090 // setup signals
91 sig_catch(SIGHUP, signal_handler);
92 sig_catch(SIGINT, signal_handler);
93 sig_catch(SIGTERM, signal_handler);
94 sig_catch(SIGPIPE, signal_handler);
95
96 // error exit code if we fail to open the device
97 signalled = 1;
98
Denis Vlasenko70766952007-09-21 17:42:40 +000099 // open device
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000100 sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
101 if (sfd < 0)
Denis Vlasenko70766952007-09-21 17:42:40 +0000102 goto unlock_and_exit;
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000103 fcntl(sfd, F_SETFL, O_RDWR | O_NOCTTY);
104
105 /* put stdin to "raw mode" (if stdin is a TTY),
106 handle one character at a time */
107 istty = isatty(STDIN_FILENO);
108 if (istty) {
109 xget1(STDIN_FILENO, &tio, &tio0);
110 if (xset1(STDIN_FILENO, &tio, "stdin"))
111 goto close_unlock_and_exit;
112// tcflush(STDIN_FILENO, TCIFLUSH);
113 timeout = -1; // tty input? -> set infinite timeout for poll()
Denis Vlasenko70766952007-09-21 17:42:40 +0000114 }
Denis Vlasenko70766952007-09-21 17:42:40 +0000115
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000116 // same thing for modem
117 xget1(sfd, &tio, &tiosfd);
118 // order device to hang up at exit
119 tio.c_cflag |= (CREAD|HUPCL);
120 // set device speed
121 cfsetspeed(&tio, tty_value_to_baud(speed));
122 if (xset1(sfd, &tio, argv[0]))
123 goto restore0_close_unlock_and_exit;
Denis Vlasenko70766952007-09-21 17:42:40 +0000124
125 // main loop: check with poll(), then read/write bytes across
Denis Vlasenko0effc242008-01-28 09:39:30 +0000126 pfd[0].fd = sfd;
Denis Vlasenko70766952007-09-21 17:42:40 +0000127 pfd[0].events = POLLIN;
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000128 pfd[1].fd = STDIN_FILENO;
Denis Vlasenko70766952007-09-21 17:42:40 +0000129 pfd[1].events = POLLIN;
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000130
131 // TODO: on piped input should we treat NL as CRNL?!
132
133 signalled = 0;
134 // initially we have to poll() both stdin and device
135 nfd = 2;
136 while (!signalled && nfd && safe_poll(pfd, nfd, timeout) > 0) {
Denis Vlasenko70766952007-09-21 17:42:40 +0000137 int i;
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000138
139 for (i = 0; i < nfd; ++i) {
140 if (pfd[i].revents & (POLLIN | POLLHUP)) {
141// int fd;
142 char c;
143 // read a byte
144 if (safe_read(pfd[i].fd, &c, 1) < 1) {
145 // this can occur at the end of piped input
146 // from now we only poll() for device
147 nfd--;
148 continue;
Denis Vlasenko70766952007-09-21 17:42:40 +0000149 }
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000150// fd = STDOUT_FILENO;
151 // stdin requires additional processing
152 // (TODO: must be controlled by command line switch)
153 if (i) {
154 // ^@ sends Break
155 if (0 == c) {
156 tcsendbreak(sfd, 0);
157 continue;
158 }
159 // ^X exits
160 if (24 == c)
161 goto done;
162// fd = sfd;
163 }
164 // write the byte
165 write(i ? sfd : STDOUT_FILENO, &c, 1);
166// write(fd, &c, 1);
167 // give device a chance to get data
168 // wait 0.01 msec
169 // (TODO: seems to be arbitrary to me)
170 if (i)
171 usleep(10);
Denis Vlasenko70766952007-09-21 17:42:40 +0000172 }
173 }
174 }
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000175done:
Denis Vlasenko70766952007-09-21 17:42:40 +0000176 tcsetattr(sfd, TCSANOW, &tiosfd);
Denis Vlasenko70766952007-09-21 17:42:40 +0000177
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000178restore0_close_unlock_and_exit:
179 if (istty) {
180// tcflush(STDIN_FILENO, TCIFLUSH);
181 tcsetattr(STDIN_FILENO, TCSANOW, &tio0);
182 }
183
184close_unlock_and_exit:
Denis Vlasenko70766952007-09-21 17:42:40 +0000185 if (ENABLE_FEATURE_CLEAN_UP)
186 close(sfd);
Denis Vlasenko70766952007-09-21 17:42:40 +0000187
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000188unlock_and_exit:
Denis Vlasenko70766952007-09-21 17:42:40 +0000189 // delete lock file
190 if (device_lock_file) {
191 unlink(device_lock_file);
192 if (ENABLE_FEATURE_CLEAN_UP)
193 free(device_lock_file);
194 }
Denis Vlasenkoef67c572008-01-27 22:40:39 +0000195 return signalled;
Denis Vlasenko70766952007-09-21 17:42:40 +0000196}