1 /* vi: set sw=4 ts=4: */
3 * bare bones 'talk to modem' program - similar to 'cu -l $device'
4 * inspired by mgetty's microcom
6 * Copyright (C) 2007 by Vladimir Dronnikov <dronnikov@gmail.ru>
8 * Licensed under GPLv2, see file LICENSE in this tarball for details.
12 /* All known arches use small ints for signals */
13 static volatile smallint signalled;
15 static void signal_handler(int signo)
20 // set canonical tty mode
21 static void xget1(int fd, struct termios *t, struct termios *oldt)
26 // t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
27 // t->c_iflag &= ~(BRKINT|IXON|ICRNL);
28 // t->c_oflag &= ~(ONLCR);
30 // t->c_cc[VTIME] = 0;
33 static int xset1(int fd, struct termios *tio, const char *device)
35 int ret = tcsetattr(fd, TCSANOW, tio);
38 bb_perror_msg("can't tcsetattr for %s", device);
43 int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
44 int microcom_main(int argc, char **argv)
49 /* #define sfd (pfd[0].fd) - gcc 4.2.1 is still not smart enough */
50 char *device_lock_file;
51 const char *opt_s = "9600";
53 const char *opt_t = "100"; // 0.1 sec timeout
55 struct termios tio0, tiosfd, tio;
59 opt_complementary = "-1"; /* at least one arg should be there */
60 getopt32(argv, "s:t:", &opt_s, &opt_t);
66 timeout = xatou(opt_t);
67 device_lock_file = (char *)bb_basename(argv[0]);
69 // try to create lock file in /var/lock
70 device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
71 sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
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
77 if (ENABLE_FEATURE_CLEAN_UP)
78 free(device_lock_file);
79 device_lock_file = NULL;
81 // %4d to make mgetty happy. It treats 4-bytes lock files as binary,
82 // not text, PID. Making 5+ char file. Brrr...
83 char *s = xasprintf("%4d\n", getpid());
84 write(sfd, s, strlen(s));
85 if (ENABLE_FEATURE_CLEAN_UP)
91 sig_catch(SIGHUP, signal_handler);
92 sig_catch(SIGINT, signal_handler);
93 sig_catch(SIGTERM, signal_handler);
94 sig_catch(SIGPIPE, signal_handler);
96 // error exit code if we fail to open the device
100 sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
102 goto unlock_and_exit;
103 fcntl(sfd, F_SETFL, O_RDWR | O_NOCTTY);
105 /* put stdin to "raw mode" (if stdin is a TTY),
106 handle one character at a time */
107 istty = isatty(STDIN_FILENO);
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()
116 // same thing for modem
117 xget1(sfd, &tio, &tiosfd);
118 // order device to hang up at exit
119 tio.c_cflag |= (CREAD|HUPCL);
121 cfsetspeed(&tio, tty_value_to_baud(speed));
122 if (xset1(sfd, &tio, argv[0]))
123 goto restore0_close_unlock_and_exit;
125 // main loop: check with poll(), then read/write bytes across
127 pfd[0].events = POLLIN;
128 pfd[1].fd = STDIN_FILENO;
129 pfd[1].events = POLLIN;
131 // TODO: on piped input should we treat NL as CRNL?!
134 // initially we have to poll() both stdin and device
136 while (!signalled && nfd && safe_poll(pfd, nfd, timeout) > 0) {
139 for (i = 0; i < nfd; ++i) {
140 if (pfd[i].revents & (POLLIN | POLLHUP)) {
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
150 // fd = STDOUT_FILENO;
151 // stdin requires additional processing
152 // (TODO: must be controlled by command line switch)
165 write(i ? sfd : STDOUT_FILENO, &c, 1);
167 // give device a chance to get data
169 // (TODO: seems to be arbitrary to me)
176 tcsetattr(sfd, TCSANOW, &tiosfd);
178 restore0_close_unlock_and_exit:
180 // tcflush(STDIN_FILENO, TCIFLUSH);
181 tcsetattr(STDIN_FILENO, TCSANOW, &tio0);
184 close_unlock_and_exit:
185 if (ENABLE_FEATURE_CLEAN_UP)
190 if (device_lock_file) {
191 unlink(device_lock_file);
192 if (ENABLE_FEATURE_CLEAN_UP)
193 free(device_lock_file);