1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/* vi: set sw=4 ts=4: */
/*
* bare bones 'talk to modem' program - similar to 'cu -l $device'
* inspired by mgetty's microcom
*
* Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
//config:config MICROCOM
//config: bool "microcom (5.9 kb)"
//config: default y
//config: help
//config: The poor man's minicom utility for chatting with serial port devices.
//applet:IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_MICROCOM) += microcom.o
//usage:#define microcom_trivial_usage
//usage: "[-d DELAY_MS] [-t TIMEOUT_MS ] [-s SPEED] [-X] TTY"
//usage:#define microcom_full_usage "\n\n"
//usage: "Copy bytes from stdin to TTY and from TTY to stdout\n"
//usage: "\n -d DELAY Wait up to DELAY ms for TTY output before sending"
//usage: "\n every next byte to it"
//usage: "\n -t TIMEOUT Exit if both stdin and TTY are silent for TIMEOUT ms"
//usage: "\n -s SPEED Set serial line to SPEED"
//usage: "\n -X Disable special meaning of NUL and Ctrl-X from stdin"
#include "libbb.h"
#include "common_bufsiz.h"
// set raw tty mode
static void xget1(int fd, struct termios *t, struct termios *oldt)
{
get_termios_and_make_raw(fd, t, oldt, 0
| TERMIOS_CLEAR_ISIG /* ^C is ASCII char 3, not "interrupt me!" */
| TERMIOS_RAW_INPUT /* pass all chars verbatim, no special handling or translating CR->NL */
| TERMIOS_RAW_CRNL /* dont convert NL<->CR on output too */
);
}
static int xset1(int fd, struct termios *tio, const char *device)
{
int ret = tcsetattr(fd, TCSAFLUSH, tio);
if (ret) {
bb_perror_msg("can't tcsetattr for %s", device);
}
return ret;
}
int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int microcom_main(int argc UNUSED_PARAM, char **argv)
{
int sfd;
int nfd;
struct pollfd pfd[2];
struct termios tio0, tiosfd, tio;
char *device_lock_file;
enum {
OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@
OPT_s = 1 << 1, // baudrate
OPT_d = 1 << 2, // wait for device response, ms
OPT_t = 1 << 3, // timeout, ms
};
speed_t speed = 9600;
int delay = -1;
int timeout = -1;
unsigned opts;
// fetch options
opts = getopt32(argv, "^" "Xs:+d:+t:+" "\0" "=1",
&speed, &delay, &timeout
);
// argc -= optind;
argv += optind;
// try to create lock file in /var/lock
device_lock_file = (char *)bb_basename(argv[0]);
device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
if (sfd < 0) {
// device already locked -> bail out
if (errno == EEXIST)
bb_perror_msg_and_die("can't create '%s'", device_lock_file);
// can't create lock -> don't care
if (ENABLE_FEATURE_CLEAN_UP)
free(device_lock_file);
device_lock_file = NULL;
} else {
// %4d to make concurrent mgetty (if any) happy.
// Mgetty treats 4-bytes lock files as binary,
// not text, PID. Making 5+ char file. Brrr...
fdprintf(sfd, "%4d\n", getpid());
close(sfd);
}
// setup signals
bb_signals(0
+ (1 << SIGHUP)
+ (1 << SIGINT)
+ (1 << SIGTERM)
+ (1 << SIGPIPE)
, record_signo);
// error exit code if we fail to open the device
bb_got_signal = 1;
// open device
sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
if (sfd < 0)
goto done;
fcntl(sfd, F_SETFL, O_RDWR);
// put device to "raw mode"
xget1(sfd, &tio, &tiosfd);
// set device speed
cfsetspeed(&tio, tty_value_to_baud(speed));
if (xset1(sfd, &tio, argv[0]))
goto done;
// put stdin to "raw mode" (if stdin is a TTY),
// handle one character at a time
if (isatty(STDIN_FILENO)) {
xget1(STDIN_FILENO, &tio, &tio0);
if (xset1(STDIN_FILENO, &tio, "stdin"))
goto done;
}
// main loop: check with poll(), then read/write bytes across
pfd[0].fd = sfd;
pfd[0].events = POLLIN;
pfd[1].fd = STDIN_FILENO;
pfd[1].events = POLLIN;
bb_got_signal = 0;
nfd = 2;
// Not safe_poll: we want to exit on signal
while (!bb_got_signal && poll(pfd, nfd, timeout) > 0) {
if (nfd > 1 && pfd[1].revents) {
char c;
// read from stdin -> write to device
if (safe_read(STDIN_FILENO, &c, 1) < 1) {
// don't poll stdin anymore if we got EOF/error
nfd--;
goto skip_write;
}
// do we need special processing?
if (!(opts & OPT_X)) {
// ^@ sends Break
if (VINTR == c) {
tcsendbreak(sfd, 0);
goto skip_write;
}
// ^X exits
if (24 == c)
break;
}
write(sfd, &c, 1);
if (delay >= 0)
safe_poll(pfd, 1, delay);
skip_write: ;
}
if (pfd[0].revents) {
ssize_t len;
#define iobuf bb_common_bufsiz1
setup_common_bufsiz();
// read from device -> write to stdout
len = safe_read(sfd, iobuf, COMMON_BUFSIZE);
if (len > 0)
full_write(STDOUT_FILENO, iobuf, len);
else {
// EOF/error -> bail out
bb_got_signal = SIGHUP;
break;
}
}
}
// restore device mode
tcsetattr(sfd, TCSAFLUSH, &tiosfd);
if (isatty(STDIN_FILENO))
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
done:
if (device_lock_file)
unlink(device_lock_file);
return bb_got_signal;
}
|