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