busybox/networking/slattach.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Stripped down version of net-tools for busybox.
   4 *
   5 * Author: Ignacio Garcia Perez (iggarpe at gmail dot com)
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 *
   9 * There are some differences from the standard net-tools slattach:
  10 *
  11 * - The -l option is not supported.
  12 *
  13 * - The -F options allows disabling of RTS/CTS flow control.
  14 */
  15//config:config SLATTACH
  16//config:       bool "slattach (6.2 kb)"
  17//config:       default y
  18//config:       help
  19//config:       slattach configures serial line as SLIP network interface.
  20
  21//applet:IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP))
  22/* shouldn't be NOEXEC: may sleep indefinitely */
  23
  24//kbuild:lib-$(CONFIG_SLATTACH) += slattach.o
  25
  26//usage:#define slattach_trivial_usage
  27//usage:       "[-ehmLF] [-c SCRIPT] [-s BAUD] [-p PROTOCOL] SERIAL_DEVICE"
  28//usage:#define slattach_full_usage "\n\n"
  29//usage:       "Configure serial line as SLIP network interface\n"
  30//usage:     "\n        -p PROT Protocol: slip, cslip (default), slip6, clisp6, adaptive"
  31//usage:     "\n        -s BAUD Line speed"
  32//usage:     "\n        -e      Exit after initialization"
  33//usage:     "\n        -h      Exit if carrier is lost (else never exits)"
  34//usage:     "\n        -c PROG Run PROG on carrier loss"
  35//usage:     "\n        -m      Do NOT set raw 8bit mode"
  36//usage:     "\n        -L      Enable 3-wire operation"
  37//usage:     "\n        -F      Disable RTS/CTS flow control"
  38
  39#include "libbb.h"
  40#include "common_bufsiz.h"
  41#include "libiproute/utils.h" /* invarg_1_to_2() */
  42
  43struct globals {
  44        int saved_disc;
  45        struct termios saved_state;
  46} FIX_ALIASING;
  47#define G (*(struct globals*)bb_common_bufsiz1)
  48#define INIT_G() do { setup_common_bufsiz(); } while (0)
  49
  50#define serial_fd 3
  51
  52static int tcsetattr_serial_or_warn(struct termios *state)
  53{
  54        int ret;
  55
  56        ret = tcsetattr(serial_fd, TCSANOW, state);
  57        if (ret != 0) {
  58                bb_simple_perror_msg("tcsetattr");
  59                return 1; /* used as exitcode */
  60        }
  61        return ret; /* 0 */
  62}
  63
  64static void restore_state_and_exit(int exitcode) NORETURN;
  65static void restore_state_and_exit(int exitcode)
  66{
  67        struct termios state;
  68
  69        /* Restore line discipline */
  70        if (ioctl_or_warn(serial_fd, TIOCSETD, &G.saved_disc)) {
  71                exitcode = 1;
  72        }
  73
  74        /* Hangup */
  75        memcpy(&state, &G.saved_state, sizeof(state));
  76        cfsetispeed(&state, B0);
  77        cfsetospeed(&state, B0);
  78        exitcode |= tcsetattr_serial_or_warn(&state);
  79        sleep1();
  80
  81        /* Restore line status */
  82        if (tcsetattr_serial_or_warn(&G.saved_state))
  83                exit(EXIT_FAILURE);
  84
  85        if (ENABLE_FEATURE_CLEAN_UP)
  86                close(serial_fd);
  87
  88        exit(exitcode);
  89}
  90
  91static void sig_handler(int signo UNUSED_PARAM)
  92{
  93        restore_state_and_exit(EXIT_SUCCESS);
  94}
  95
  96int slattach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  97int slattach_main(int argc UNUSED_PARAM, char **argv)
  98{
  99        /* Line discipline code table */
 100        static const char proto_names[] ALIGN1 =
 101                "slip\0"        /* 0 */
 102                "cslip\0"       /* 1 */
 103                "slip6\0"       /* 2 */
 104                "cslip6\0"      /* 3 */
 105                "adaptive\0"    /* 8 */
 106                ;
 107        static const int int_N_SLIP = N_SLIP;
 108
 109        int encap, opt, fd;
 110        struct termios state;
 111        const char *proto = "cslip";
 112        const char *extcmd;   /* Command to execute after hangup */
 113        const char *baud_str;
 114        int baud_code = baud_code; /* for compiler */
 115
 116        enum {
 117                OPT_p_proto  = 1 << 0,
 118                OPT_s_baud   = 1 << 1,
 119                OPT_c_extcmd = 1 << 2,
 120                OPT_e_quit   = 1 << 3,
 121                OPT_h_watch  = 1 << 4,
 122                OPT_m_nonraw = 1 << 5,
 123                OPT_L_local  = 1 << 6,
 124                OPT_F_noflow = 1 << 7
 125        };
 126
 127        INIT_G();
 128
 129        /* Parse command line options */
 130        opt = getopt32(argv, "^" "p:s:c:ehmLF" "\0" "=1",
 131                                &proto, &baud_str, &extcmd
 132        );
 133        /*argc -= optind;*/
 134        argv += optind;
 135
 136        encap = index_in_strings(proto_names, proto);
 137        if (encap < 0)
 138                invarg_1_to_2(proto, "protocol");
 139        if (encap > 3)
 140                encap = 8;
 141
 142        /* We want to know if the baud rate is valid before we start touching the ttys */
 143        if (opt & OPT_s_baud) {
 144                baud_code = tty_value_to_baud(xatoi(baud_str));
 145                if (baud_code < 0)
 146                        invarg_1_to_2(baud_str, "baud rate");
 147        }
 148
 149        /* Open tty */
 150        fd = open(*argv, O_RDWR | O_NDELAY);
 151        if (fd < 0) {
 152                char *buf = concat_path_file("/dev", *argv);
 153                fd = xopen(buf, O_RDWR | O_NDELAY);
 154                /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */
 155                free(buf);
 156        }
 157        xmove_fd(fd, serial_fd);
 158
 159        /* Save current tty state */
 160        if (tcgetattr(serial_fd, &G.saved_state) != 0)
 161                bb_simple_perror_msg_and_die("tcgetattr");
 162        /* Save line discipline */
 163        xioctl(serial_fd, TIOCGETD, &G.saved_disc);
 164
 165        /* Trap signals in order to restore tty states upon exit */
 166        if (!(opt & OPT_e_quit)) {
 167                bb_signals(0
 168                        + (1 << SIGHUP)
 169                        + (1 << SIGINT)
 170                        + (1 << SIGQUIT)
 171                        + (1 << SIGTERM)
 172                        , sig_handler);
 173        }
 174
 175        /* Configure tty */
 176        memcpy(&state, &G.saved_state, sizeof(state));
 177        if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */
 178                memset(&state.c_cc, 0, sizeof(state.c_cc));
 179                state.c_cc[VMIN] = 1;
 180                state.c_iflag = IGNBRK | IGNPAR;
 181                /*state.c_oflag = 0;*/
 182                /*state.c_lflag = 0;*/
 183                state.c_cflag = CS8 | HUPCL | CREAD
 184                              | ((opt & OPT_L_local) ? CLOCAL : 0)
 185                              | ((opt & OPT_F_noflow) ? 0 : CRTSCTS);
 186                cfsetispeed(&state, cfgetispeed(&G.saved_state));
 187                cfsetospeed(&state, cfgetospeed(&G.saved_state));
 188        }
 189        if (opt & OPT_s_baud) {
 190                cfsetispeed(&state, baud_code);
 191                cfsetospeed(&state, baud_code);
 192        }
 193        /* Set line status */
 194        if (tcsetattr_serial_or_warn(&state))
 195                goto bad;
 196        /* Set line disclipline (N_SLIP always) */
 197        if (ioctl_or_warn(serial_fd, TIOCSETD, (void*)&int_N_SLIP))
 198                goto bad;
 199        /* Set encapsulation (SLIP, CSLIP, etc) */
 200        if (ioctl_or_warn(serial_fd, SIOCSIFENCAP, &encap))
 201                goto bad;
 202
 203        /* Exit now if option -e was passed */
 204        if (opt & OPT_e_quit)
 205                return EXIT_SUCCESS;
 206
 207        /* If we're not requested to watch, just keep descriptor open
 208         * until we are killed */
 209        if (!(opt & OPT_h_watch))
 210                while (1)
 211                        sleep(24*60*60);
 212
 213        /* Watch line for hangup */
 214        while (1) {
 215                int modem_stat;
 216                if (ioctl(serial_fd, TIOCMGET, &modem_stat))
 217                        break;
 218                if (!(modem_stat & TIOCM_CAR))
 219                        break;
 220                sleep(15);
 221        }
 222
 223        /* Execute command on hangup */
 224        if (opt & OPT_c_extcmd)
 225                system(extcmd);
 226
 227        /* Restore states and exit */
 228        restore_state_and_exit(EXIT_SUCCESS);
 229 bad:
 230        restore_state_and_exit(EXIT_FAILURE);
 231}
 232