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
  16//usage:#define slattach_trivial_usage
  17//usage:       "[-cehmLF] [-s SPEED] [-p PROTOCOL] DEVICE"
  18//usage:#define slattach_full_usage "\n\n"
  19//usage:       "Attach network interface(s) to serial line(s)\n"
  20//usage:     "\n        -p PROT Set protocol (slip, cslip, slip6, clisp6 or adaptive)"
  21//usage:     "\n        -s SPD  Set line speed"
  22//usage:     "\n        -e      Exit after initializing device"
  23//usage:     "\n        -h      Exit when the carrier is lost"
  24//usage:     "\n        -c PROG Run PROG when the line is hung up"
  25//usage:     "\n        -m      Do NOT initialize the line in raw 8 bits mode"
  26//usage:     "\n        -L      Enable 3-wire operation"
  27//usage:     "\n        -F      Disable RTS/CTS flow control"
  28
  29#include "libbb.h"
  30#include "libiproute/utils.h" /* invarg() */
  31
  32struct globals {
  33        int handle;
  34        int saved_disc;
  35        struct termios saved_state;
  36} FIX_ALIASING;
  37#define G (*(struct globals*)&bb_common_bufsiz1)
  38#define handle       (G.handle      )
  39#define saved_disc   (G.saved_disc  )
  40#define saved_state  (G.saved_state )
  41#define INIT_G() do { } while (0)
  42
  43
  44/*
  45 * Save tty state and line discipline
  46 *
  47 * It is fine here to bail out on errors, since we haven modified anything yet
  48 */
  49static void save_state(void)
  50{
  51        /* Save line status */
  52        if (tcgetattr(handle, &saved_state) < 0)
  53                bb_perror_msg_and_die("get state");
  54
  55        /* Save line discipline */
  56        xioctl(handle, TIOCGETD, &saved_disc);
  57}
  58
  59static int set_termios_state_or_warn(struct termios *state)
  60{
  61        int ret;
  62
  63        ret = tcsetattr(handle, TCSANOW, state);
  64        if (ret < 0) {
  65                bb_perror_msg("set state");
  66                return 1; /* used as exitcode */
  67        }
  68        return 0;
  69}
  70
  71/*
  72 * Restore state and line discipline for ALL managed ttys
  73 *
  74 * Restoring ALL managed ttys is the only way to have a single
  75 * hangup delay.
  76 *
  77 * Go on after errors: we want to restore as many controlled ttys
  78 * as possible.
  79 */
  80static void restore_state_and_exit(int exitcode) NORETURN;
  81static void restore_state_and_exit(int exitcode)
  82{
  83        struct termios state;
  84
  85        /* Restore line discipline */
  86        if (ioctl_or_warn(handle, TIOCSETD, &saved_disc) < 0) {
  87                exitcode = 1;
  88        }
  89
  90        /* Hangup */
  91        memcpy(&state, &saved_state, sizeof(state));
  92        cfsetispeed(&state, B0);
  93        cfsetospeed(&state, B0);
  94        if (set_termios_state_or_warn(&state))
  95                exitcode = 1;
  96        sleep(1);
  97
  98        /* Restore line status */
  99        if (set_termios_state_or_warn(&saved_state))
 100                exit(EXIT_FAILURE);
 101        if (ENABLE_FEATURE_CLEAN_UP)
 102                close(handle);
 103
 104        exit(exitcode);
 105}
 106
 107/*
 108 * Set tty state, line discipline and encapsulation
 109 */
 110static void set_state(struct termios *state, int encap)
 111{
 112        int disc;
 113
 114        /* Set line status */
 115        if (set_termios_state_or_warn(state))
 116                goto bad;
 117        /* Set line discliple (N_SLIP always) */
 118        disc = N_SLIP;
 119        if (ioctl_or_warn(handle, TIOCSETD, &disc) < 0) {
 120                goto bad;
 121        }
 122
 123        /* Set encapsulation (SLIP, CSLIP, etc) */
 124        if (ioctl_or_warn(handle, SIOCSIFENCAP, &encap) < 0) {
 125 bad:
 126                restore_state_and_exit(EXIT_FAILURE);
 127        }
 128}
 129
 130static void sig_handler(int signo UNUSED_PARAM)
 131{
 132        restore_state_and_exit(EXIT_SUCCESS);
 133}
 134
 135int slattach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 136int slattach_main(int argc UNUSED_PARAM, char **argv)
 137{
 138        /* Line discipline code table */
 139        static const char proto_names[] ALIGN1 =
 140                "slip\0"        /* 0 */
 141                "cslip\0"       /* 1 */
 142                "slip6\0"       /* 2 */
 143                "cslip6\0"      /* 3 */
 144                "adaptive\0"    /* 8 */
 145                ;
 146
 147        int i, encap, opt;
 148        struct termios state;
 149        const char *proto = "cslip";
 150        const char *extcmd;   /* Command to execute after hangup */
 151        const char *baud_str;
 152        int baud_code = -1;   /* Line baud rate (system code) */
 153
 154        enum {
 155                OPT_p_proto  = 1 << 0,
 156                OPT_s_baud   = 1 << 1,
 157                OPT_c_extcmd = 1 << 2,
 158                OPT_e_quit   = 1 << 3,
 159                OPT_h_watch  = 1 << 4,
 160                OPT_m_nonraw = 1 << 5,
 161                OPT_L_local  = 1 << 6,
 162                OPT_F_noflow = 1 << 7
 163        };
 164
 165        INIT_G();
 166
 167        /* Parse command line options */
 168        opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd);
 169        /*argc -= optind;*/
 170        argv += optind;
 171
 172        if (!*argv)
 173                bb_show_usage();
 174
 175        encap = index_in_strings(proto_names, proto);
 176
 177        if (encap < 0)
 178                invarg(proto, "protocol");
 179        if (encap > 3)
 180                encap = 8;
 181
 182        /* We want to know if the baud rate is valid before we start touching the ttys */
 183        if (opt & OPT_s_baud) {
 184                baud_code = tty_value_to_baud(xatoi(baud_str));
 185                if (baud_code < 0)
 186                        invarg(baud_str, "baud rate");
 187        }
 188
 189        /* Trap signals in order to restore tty states upon exit */
 190        if (!(opt & OPT_e_quit)) {
 191                bb_signals(0
 192                        + (1 << SIGHUP)
 193                        + (1 << SIGINT)
 194                        + (1 << SIGQUIT)
 195                        + (1 << SIGTERM)
 196                        , sig_handler);
 197        }
 198
 199        /* Open tty */
 200        handle = open(*argv, O_RDWR | O_NDELAY);
 201        if (handle < 0) {
 202                char *buf = concat_path_file("/dev", *argv);
 203                handle = xopen(buf, O_RDWR | O_NDELAY);
 204                /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */
 205                free(buf);
 206        }
 207
 208        /* Save current tty state */
 209        save_state();
 210
 211        /* Configure tty */
 212        memcpy(&state, &saved_state, sizeof(state));
 213        if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */
 214                memset(&state.c_cc, 0, sizeof(state.c_cc));
 215                state.c_cc[VMIN] = 1;
 216                state.c_iflag = IGNBRK | IGNPAR;
 217                state.c_oflag = 0;
 218                state.c_lflag = 0;
 219                state.c_cflag = CS8 | HUPCL | CREAD
 220                              | ((opt & OPT_L_local) ? CLOCAL : 0)
 221                              | ((opt & OPT_F_noflow) ? 0 : CRTSCTS);
 222                cfsetispeed(&state, cfgetispeed(&saved_state));
 223                cfsetospeed(&state, cfgetospeed(&saved_state));
 224        }
 225
 226        if (opt & OPT_s_baud) {
 227                cfsetispeed(&state, baud_code);
 228                cfsetospeed(&state, baud_code);
 229        }
 230
 231        set_state(&state, encap);
 232
 233        /* Exit now if option -e was passed */
 234        if (opt & OPT_e_quit)
 235                return 0;
 236
 237        /* If we're not requested to watch, just keep descriptor open
 238         * until we are killed */
 239        if (!(opt & OPT_h_watch))
 240                while (1)
 241                        sleep(24*60*60);
 242
 243        /* Watch line for hangup */
 244        while (1) {
 245                if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR))
 246                        goto no_carrier;
 247                sleep(15);
 248        }
 249
 250 no_carrier:
 251
 252        /* Execute command on hangup */
 253        if (opt & OPT_c_extcmd)
 254                system(extcmd);
 255
 256        /* Restore states and exit */
 257        restore_state_and_exit(EXIT_SUCCESS);
 258}
 259