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 tarball for details.
   9 */
  10#include "libbb.h"
  11
  12// set raw tty mode
  13static void xget1(int fd, struct termios *t, struct termios *oldt)
  14{
  15        tcgetattr(fd, oldt);
  16        *t = *oldt;
  17        cfmakeraw(t);
  18//      t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
  19//      t->c_iflag &= ~(BRKINT|IXON|ICRNL);
  20//      t->c_oflag &= ~(ONLCR);
  21//      t->c_cc[VMIN]  = 1;
  22//      t->c_cc[VTIME] = 0;
  23}
  24
  25static int xset1(int fd, struct termios *tio, const char *device)
  26{
  27        int ret = tcsetattr(fd, TCSAFLUSH, tio);
  28
  29        if (ret) {
  30                bb_perror_msg("can't tcsetattr for %s", device);
  31        }
  32        return ret;
  33}
  34
  35int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  36int microcom_main(int argc UNUSED_PARAM, char **argv)
  37{
  38        int sfd;
  39        int nfd;
  40        struct pollfd pfd[2];
  41        struct termios tio0, tiosfd, tio;
  42        char *device_lock_file;
  43        enum {
  44                OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@
  45                OPT_s = 1 << 1, // baudrate
  46                OPT_d = 1 << 2, // wait for device response, ms
  47                OPT_t = 1 << 3, // timeout, ms
  48        };
  49        speed_t speed = 9600;
  50        int delay = -1;
  51        int timeout = -1;
  52        unsigned opts;
  53
  54        // fetch options
  55        opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options
  56        opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout);
  57//      argc -= optind;
  58        argv += optind;
  59
  60        // try to create lock file in /var/lock
  61        device_lock_file = (char *)bb_basename(argv[0]);
  62        device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
  63        sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
  64        if (sfd < 0) {
  65                // device already locked -> bail out
  66                if (errno == EEXIST)
  67                        bb_perror_msg_and_die("can't create %s", device_lock_file);
  68                // can't create lock -> don't care
  69                if (ENABLE_FEATURE_CLEAN_UP)
  70                        free(device_lock_file);
  71                device_lock_file = NULL;
  72        } else {
  73                // %4d to make concurrent mgetty (if any) happy.
  74                // Mgetty treats 4-bytes lock files as binary,
  75                // not text, PID. Making 5+ char file. Brrr...
  76                fdprintf(sfd, "%4d\n", getpid());
  77                close(sfd);
  78        }
  79
  80        // setup signals
  81        bb_signals(0
  82                + (1 << SIGHUP)
  83                + (1 << SIGINT)
  84                + (1 << SIGTERM)
  85                + (1 << SIGPIPE)
  86                , record_signo);
  87
  88        // error exit code if we fail to open the device
  89        bb_got_signal = 1;
  90
  91        // open device
  92        sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
  93        if (sfd < 0)
  94                goto done;
  95        fcntl(sfd, F_SETFL, 0);
  96
  97        // put device to "raw mode"
  98        xget1(sfd, &tio, &tiosfd);
  99        // set device speed
 100        cfsetspeed(&tio, tty_value_to_baud(speed));
 101        if (xset1(sfd, &tio, argv[0]))
 102                goto done;
 103
 104        // put stdin to "raw mode" (if stdin is a TTY),
 105        // handle one character at a time
 106        if (isatty(STDIN_FILENO)) {
 107                xget1(STDIN_FILENO, &tio, &tio0);
 108                if (xset1(STDIN_FILENO, &tio, "stdin"))
 109                        goto done;
 110        }
 111
 112        // main loop: check with poll(), then read/write bytes across
 113        pfd[0].fd = sfd;
 114        pfd[0].events = POLLIN;
 115        pfd[1].fd = STDIN_FILENO;
 116        pfd[1].events = POLLIN;
 117
 118        bb_got_signal = 0;
 119        nfd = 2;
 120        while (!bb_got_signal && safe_poll(pfd, nfd, timeout) > 0) {
 121                if (nfd > 1 && pfd[1].revents) {
 122                        char c;
 123                        // read from stdin -> write to device
 124                        if (safe_read(STDIN_FILENO, &c, 1) < 1) {
 125                                // don't poll stdin anymore if we got EOF/error
 126                                nfd--;
 127                                goto skip_write;
 128                        }
 129                        // do we need special processing?
 130                        if (!(opts & OPT_X)) {
 131                                // ^@ sends Break
 132                                if (VINTR == c) {
 133                                        tcsendbreak(sfd, 0);
 134                                        goto skip_write;
 135                                }
 136                                // ^X exits
 137                                if (24 == c)
 138                                        break;
 139                        }
 140                        write(sfd, &c, 1);
 141                        if (delay >= 0)
 142                                safe_poll(pfd, 1, delay);
 143skip_write: ;
 144                }
 145                if (pfd[0].revents) {
 146#define iobuf bb_common_bufsiz1
 147                        ssize_t len;
 148                        // read from device -> write to stdout
 149                        len = safe_read(sfd, iobuf, sizeof(iobuf));
 150                        if (len > 0)
 151                                full_write(STDOUT_FILENO, iobuf, len);
 152                        else {
 153                                // EOF/error -> bail out
 154                                bb_got_signal = SIGHUP;
 155                                break;
 156                        }
 157                }
 158        }
 159
 160        // restore device mode
 161        tcsetattr(sfd, TCSAFLUSH, &tiosfd);
 162
 163        if (isatty(STDIN_FILENO))
 164                tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
 165
 166done:
 167        if (device_lock_file)
 168                unlink(device_lock_file);
 169
 170        return bb_got_signal;
 171}
 172