i2cdump: don't read block data in non-block modes
[oweals/busybox.git] / miscutils / microcom.c
index 63b07fd699ed68fb26c8589631724c12dab0d215..5e29a1acdd483ba51e4382807c24685f8aa1b577 100644 (file)
@@ -3,23 +3,26 @@
  * bare bones 'talk to modem' program - similar to 'cu -l $device'
  * inspired by mgetty's microcom
  *
- * Copyright (C) 2007 by Vladimir Dronnikov <dronnikov@gmail.ru>
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
-#include "libbb.h"
 
-/* All known arches use small ints for signals */
-static volatile smallint signalled;
+//usage:#define microcom_trivial_usage
+//usage:       "[-d DELAY] [-t TIMEOUT] [-s SPEED] [-X] TTY"
+//usage:#define microcom_full_usage "\n\n"
+//usage:       "Copy bytes for stdin to TTY and from TTY to stdout\n"
+//usage:     "\n       -d      Wait up to DELAY ms for TTY output before sending every"
+//usage:     "\n               next byte to it"
+//usage:     "\n       -t      Exit if both stdin and TTY are silent for TIMEOUT ms"
+//usage:     "\n       -s      Set serial line to SPEED"
+//usage:     "\n       -X      Disable special meaning of NUL and Ctrl-X from stdin"
 
-static void signal_handler(int signo)
-{
-       signalled = signo;
-}
+#include "libbb.h"
 
 // set raw tty mode
 static void xget1(int fd, struct termios *t, struct termios *oldt)
-{ 
+{
        tcgetattr(fd, oldt);
        *t = *oldt;
        cfmakeraw(t);
@@ -41,7 +44,7 @@ static int xset1(int fd, struct termios *tio, const char *device)
 }
 
 int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int microcom_main(int argc, char **argv)
+int microcom_main(int argc UNUSED_PARAM, char **argv)
 {
        int sfd;
        int nfd;
@@ -51,29 +54,17 @@ int microcom_main(int argc, char **argv)
        enum {
                OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@
                OPT_s = 1 << 1, // baudrate
-               OPT_d = 1 << 2, // wait for device response, msecs
+               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;
-
-       // fetch options
-       char *opt_s;
-       char *opt_d;
-       char *opt_t;
        unsigned opts;
-       opt_complementary = "=1"; // exactly one arg should be there
-       opts = getopt32(argv, "Xs:d:t:", &opt_s, &opt_d, &opt_t);
-
-       // apply options
-       if (opts & OPT_s)
-               speed = xatoi_u(opt_s);
-       if (opts & OPT_d)
-               delay = xatoi_u(opt_d);
-       if (opts & OPT_t)
-               timeout = xatoi_u(opt_t);
 
+       // fetch options
+       opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options
+       opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout);
 //     argc -= optind;
        argv += optind;
 
@@ -82,36 +73,44 @@ int microcom_main(int argc, char **argv)
        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);
+                       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;
-       }
-       if (sfd > 0) {
-               // %4d to make mgetty happy. It treats 4-bytes lock files as binary,
+       } 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...
-               char *s = xasprintf("%4d\n", getpid());
-               write(sfd, s, strlen(s));
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       free(s);
+               fdprintf(sfd, "%4d\n", getpid());
                close(sfd);
        }
 
        // setup signals
-       sig_catch(SIGHUP,  signal_handler);
-       sig_catch(SIGINT,  signal_handler);
-       sig_catch(SIGTERM, signal_handler);
-       sig_catch(SIGPIPE, signal_handler);
+       bb_signals(0
+               + (1 << SIGHUP)
+               + (1 << SIGINT)
+               + (1 << SIGTERM)
+               + (1 << SIGPIPE)
+               , record_signo);
 
        // error exit code if we fail to open the device
-       signalled = 1;
+       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 | O_NOCTTY);
+       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
@@ -121,41 +120,30 @@ int microcom_main(int argc, char **argv)
                        goto done;
        }
 
-       // same thing for modem
-       xget1(sfd, &tio, &tiosfd);
-       // order device to hang up at exit
-       tio.c_cflag |= (CREAD|HUPCL);
-//     if (!istty)
-//             tio.c_iflag |= (IGNCR);
-       // set device speed
-       cfsetspeed(&tio, tty_value_to_baud(speed));
-       if (xset1(sfd, &tio, argv[0]))
-               goto restore0_and_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;
 
-       signalled = 0;
+       bb_got_signal = 0;
        nfd = 2;
-       while (!signalled && safe_poll(pfd, nfd, timeout) > 0) {
-               if (pfd[1].revents) {
+       // 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
-                               pfd[1].revents = 0;
                                nfd--;
-                               goto check_stdin;
+                               goto skip_write;
                        }
                        // do we need special processing?
                        if (!(opts & OPT_X)) {
                                // ^@ sends Break
                                if (VINTR == c) {
                                        tcsendbreak(sfd, 0);
-                                       goto check_stdin;
+                                       goto skip_write;
                                }
                                // ^X exits
                                if (24 == c)
@@ -164,21 +152,26 @@ int microcom_main(int argc, char **argv)
                        write(sfd, &c, 1);
                        if (delay >= 0)
                                safe_poll(pfd, 1, delay);
+skip_write: ;
                }
-check_stdin:
                if (pfd[0].revents) {
+#define iobuf bb_common_bufsiz1
                        ssize_t len;
                        // read from device -> write to stdout
-                       len = safe_read(sfd, bb_common_bufsiz1, sizeof(bb_common_bufsiz1));
+                       len = safe_read(sfd, iobuf, sizeof(iobuf));
                        if (len > 0)
-                               full_write(STDOUT_FILENO, bb_common_bufsiz1, len);
-                       // else { EOF/error - what to do? }
+                               full_write(STDOUT_FILENO, iobuf, len);
+                       else {
+                               // EOF/error -> bail out
+                               bb_got_signal = SIGHUP;
+                               break;
+                       }
                }
        }
 
+       // restore device mode
        tcsetattr(sfd, TCSAFLUSH, &tiosfd);
 
-restore0_and_done:
        if (isatty(STDIN_FILENO))
                tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
 
@@ -186,5 +179,5 @@ done:
        if (device_lock_file)
                unlink(device_lock_file);
 
-       return signalled;
+       return bb_got_signal;
 }