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