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