| /* vi: set sw=4 ts=4: */ | |
| /* | |
| * Stripped down version of net-tools for busybox. | |
| * | |
| * Author: Ignacio Garcia Perez (iggarpe at gmail dot com) | |
| * | |
| * Licensed under GPLv2 or later, see file LICENSE in this source tree. | |
| * | |
| * There are some differences from the standard net-tools slattach: | |
| * | |
| * - The -l option is not supported. | |
| * | |
| * - The -F options allows disabling of RTS/CTS flow control. | |
| */ | |
| //config:config SLATTACH | |
| //config: bool "slattach (6.2 kb)" | |
| //config: default y | |
| //config: select PLATFORM_LINUX | |
| //config: help | |
| //config: slattach configures serial line as SLIP network interface. | |
| //applet:IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP)) | |
| /* shouldn't be NOEXEC: may sleep indefinitely */ | |
| //kbuild:lib-$(CONFIG_SLATTACH) += slattach.o | |
| //usage:#define slattach_trivial_usage | |
| //usage: "[-ehmLF] [-c SCRIPT] [-s BAUD] [-p PROTOCOL] SERIAL_DEVICE" | |
| //usage:#define slattach_full_usage "\n\n" | |
| //usage: "Configure serial line as SLIP network interface\n" | |
| //usage: "\n -p PROT Protocol: slip, cslip (default), slip6, clisp6, adaptive" | |
| //usage: "\n -s BAUD Line speed" | |
| //usage: "\n -e Exit after initialization" | |
| //usage: "\n -h Exit if carrier is lost (else never exits)" | |
| //usage: "\n -c PROG Run PROG on carrier loss" | |
| //usage: "\n -m Do NOT set raw 8bit mode" | |
| //usage: "\n -L Enable 3-wire operation" | |
| //usage: "\n -F Disable RTS/CTS flow control" | |
| #include "libbb.h" | |
| #include "common_bufsiz.h" | |
| #include "libiproute/utils.h" /* invarg_1_to_2() */ | |
| struct globals { | |
| int saved_disc; | |
| struct termios saved_state; | |
| } FIX_ALIASING; | |
| #define G (*(struct globals*)bb_common_bufsiz1) | |
| #define INIT_G() do { setup_common_bufsiz(); } while (0) | |
| #define serial_fd 3 | |
| static int tcsetattr_serial_or_warn(struct termios *state) | |
| { | |
| int ret; | |
| ret = tcsetattr(serial_fd, TCSANOW, state); | |
| if (ret != 0) { | |
| bb_simple_perror_msg("tcsetattr"); | |
| return 1; /* used as exitcode */ | |
| } | |
| return ret; /* 0 */ | |
| } | |
| static void restore_state_and_exit(int exitcode) NORETURN; | |
| static void restore_state_and_exit(int exitcode) | |
| { | |
| struct termios state; | |
| /* Restore line discipline */ | |
| if (ioctl_or_warn(serial_fd, TIOCSETD, &G.saved_disc)) { | |
| exitcode = 1; | |
| } | |
| /* Hangup */ | |
| memcpy(&state, &G.saved_state, sizeof(state)); | |
| cfsetispeed(&state, B0); | |
| cfsetospeed(&state, B0); | |
| exitcode |= tcsetattr_serial_or_warn(&state); | |
| sleep(1); | |
| /* Restore line status */ | |
| if (tcsetattr_serial_or_warn(&G.saved_state)) | |
| exit(EXIT_FAILURE); | |
| if (ENABLE_FEATURE_CLEAN_UP) | |
| close(serial_fd); | |
| exit(exitcode); | |
| } | |
| static void sig_handler(int signo UNUSED_PARAM) | |
| { | |
| restore_state_and_exit(EXIT_SUCCESS); | |
| } | |
| int slattach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| int slattach_main(int argc UNUSED_PARAM, char **argv) | |
| { | |
| /* Line discipline code table */ | |
| static const char proto_names[] ALIGN1 = | |
| "slip\0" /* 0 */ | |
| "cslip\0" /* 1 */ | |
| "slip6\0" /* 2 */ | |
| "cslip6\0" /* 3 */ | |
| "adaptive\0" /* 8 */ | |
| ; | |
| static const int int_N_SLIP = N_SLIP; | |
| int encap, opt, fd; | |
| struct termios state; | |
| const char *proto = "cslip"; | |
| const char *extcmd; /* Command to execute after hangup */ | |
| const char *baud_str; | |
| int baud_code = baud_code; /* for compiler */ | |
| enum { | |
| OPT_p_proto = 1 << 0, | |
| OPT_s_baud = 1 << 1, | |
| OPT_c_extcmd = 1 << 2, | |
| OPT_e_quit = 1 << 3, | |
| OPT_h_watch = 1 << 4, | |
| OPT_m_nonraw = 1 << 5, | |
| OPT_L_local = 1 << 6, | |
| OPT_F_noflow = 1 << 7 | |
| }; | |
| INIT_G(); | |
| /* Parse command line options */ | |
| opt = getopt32(argv, "^" "p:s:c:ehmLF" "\0" "=1", | |
| &proto, &baud_str, &extcmd | |
| ); | |
| /*argc -= optind;*/ | |
| argv += optind; | |
| encap = index_in_strings(proto_names, proto); | |
| if (encap < 0) | |
| invarg_1_to_2(proto, "protocol"); | |
| if (encap > 3) | |
| encap = 8; | |
| /* We want to know if the baud rate is valid before we start touching the ttys */ | |
| if (opt & OPT_s_baud) { | |
| baud_code = tty_value_to_baud(xatoi(baud_str)); | |
| if (baud_code < 0) | |
| invarg_1_to_2(baud_str, "baud rate"); | |
| } | |
| /* Open tty */ | |
| fd = open(*argv, O_RDWR | O_NDELAY); | |
| if (fd < 0) { | |
| char *buf = concat_path_file("/dev", *argv); | |
| fd = xopen(buf, O_RDWR | O_NDELAY); | |
| /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */ | |
| free(buf); | |
| } | |
| xmove_fd(fd, serial_fd); | |
| /* Save current tty state */ | |
| if (tcgetattr(serial_fd, &G.saved_state) != 0) | |
| bb_simple_perror_msg_and_die("tcgetattr"); | |
| /* Save line discipline */ | |
| xioctl(serial_fd, TIOCGETD, &G.saved_disc); | |
| /* Trap signals in order to restore tty states upon exit */ | |
| if (!(opt & OPT_e_quit)) { | |
| bb_signals(0 | |
| + (1 << SIGHUP) | |
| + (1 << SIGINT) | |
| + (1 << SIGQUIT) | |
| + (1 << SIGTERM) | |
| , sig_handler); | |
| } | |
| /* Configure tty */ | |
| memcpy(&state, &G.saved_state, sizeof(state)); | |
| if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */ | |
| memset(&state.c_cc, 0, sizeof(state.c_cc)); | |
| state.c_cc[VMIN] = 1; | |
| state.c_iflag = IGNBRK | IGNPAR; | |
| /*state.c_oflag = 0;*/ | |
| /*state.c_lflag = 0;*/ | |
| state.c_cflag = CS8 | HUPCL | CREAD | |
| | ((opt & OPT_L_local) ? CLOCAL : 0) | |
| | ((opt & OPT_F_noflow) ? 0 : CRTSCTS); | |
| cfsetispeed(&state, cfgetispeed(&G.saved_state)); | |
| cfsetospeed(&state, cfgetospeed(&G.saved_state)); | |
| } | |
| if (opt & OPT_s_baud) { | |
| cfsetispeed(&state, baud_code); | |
| cfsetospeed(&state, baud_code); | |
| } | |
| /* Set line status */ | |
| if (tcsetattr_serial_or_warn(&state)) | |
| goto bad; | |
| /* Set line disclipline (N_SLIP always) */ | |
| if (ioctl_or_warn(serial_fd, TIOCSETD, (void*)&int_N_SLIP)) | |
| goto bad; | |
| /* Set encapsulation (SLIP, CSLIP, etc) */ | |
| if (ioctl_or_warn(serial_fd, SIOCSIFENCAP, &encap)) | |
| goto bad; | |
| /* Exit now if option -e was passed */ | |
| if (opt & OPT_e_quit) | |
| return EXIT_SUCCESS; | |
| /* If we're not requested to watch, just keep descriptor open | |
| * until we are killed */ | |
| if (!(opt & OPT_h_watch)) | |
| while (1) | |
| sleep(24*60*60); | |
| /* Watch line for hangup */ | |
| while (1) { | |
| int modem_stat; | |
| if (ioctl(serial_fd, TIOCMGET, &modem_stat)) | |
| break; | |
| if (!(modem_stat & TIOCM_CAR)) | |
| break; | |
| sleep(15); | |
| } | |
| /* Execute command on hangup */ | |
| if (opt & OPT_c_extcmd) | |
| system(extcmd); | |
| /* Restore states and exit */ | |
| restore_state_and_exit(EXIT_SUCCESS); | |
| bad: | |
| restore_state_and_exit(EXIT_FAILURE); | |
| } |