| /* 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" |
| //config: default y |
| //config: select PLATFORM_LINUX |
| //config: help |
| //config: slattach is a small utility to attach network interfaces to serial |
| //config: lines. |
| |
| //applet:IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP)) |
| |
| //kbuild:lib-$(CONFIG_SLATTACH) += slattach.o |
| |
| //usage:#define slattach_trivial_usage |
| //usage: "[-cehmLF] [-s SPEED] [-p PROTOCOL] DEVICE" |
| //usage:#define slattach_full_usage "\n\n" |
| //usage: "Attach network interface(s) to serial line(s)\n" |
| //usage: "\n -p PROT Set protocol (slip, cslip, slip6, clisp6 or adaptive)" |
| //usage: "\n -s SPD Set line speed" |
| //usage: "\n -e Exit after initializing device" |
| //usage: "\n -h Exit when the carrier is lost" |
| //usage: "\n -c PROG Run PROG when the line is hung up" |
| //usage: "\n -m Do NOT initialize the line in raw 8 bits 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 handle; |
| int saved_disc; |
| struct termios saved_state; |
| } FIX_ALIASING; |
| #define G (*(struct globals*)bb_common_bufsiz1) |
| #define handle (G.handle ) |
| #define saved_disc (G.saved_disc ) |
| #define saved_state (G.saved_state ) |
| #define INIT_G() do { setup_common_bufsiz(); } while (0) |
| |
| |
| /* |
| * Save tty state and line discipline |
| * |
| * It is fine here to bail out on errors, since we haven modified anything yet |
| */ |
| static void save_state(void) |
| { |
| /* Save line status */ |
| if (tcgetattr(handle, &saved_state) < 0) |
| bb_perror_msg_and_die("get state"); |
| |
| /* Save line discipline */ |
| xioctl(handle, TIOCGETD, &saved_disc); |
| } |
| |
| static int set_termios_state_or_warn(struct termios *state) |
| { |
| int ret; |
| |
| ret = tcsetattr(handle, TCSANOW, state); |
| if (ret < 0) { |
| bb_perror_msg("set state"); |
| return 1; /* used as exitcode */ |
| } |
| return 0; |
| } |
| |
| /* |
| * Restore state and line discipline for ALL managed ttys |
| * |
| * Restoring ALL managed ttys is the only way to have a single |
| * hangup delay. |
| * |
| * Go on after errors: we want to restore as many controlled ttys |
| * as possible. |
| */ |
| 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(handle, TIOCSETD, &saved_disc) < 0) { |
| exitcode = 1; |
| } |
| |
| /* Hangup */ |
| memcpy(&state, &saved_state, sizeof(state)); |
| cfsetispeed(&state, B0); |
| cfsetospeed(&state, B0); |
| if (set_termios_state_or_warn(&state)) |
| exitcode = 1; |
| sleep(1); |
| |
| /* Restore line status */ |
| if (set_termios_state_or_warn(&saved_state)) |
| exit(EXIT_FAILURE); |
| if (ENABLE_FEATURE_CLEAN_UP) |
| close(handle); |
| |
| exit(exitcode); |
| } |
| |
| /* |
| * Set tty state, line discipline and encapsulation |
| */ |
| static void set_state(struct termios *state, int encap) |
| { |
| int disc; |
| |
| /* Set line status */ |
| if (set_termios_state_or_warn(state)) |
| goto bad; |
| /* Set line discliple (N_SLIP always) */ |
| disc = N_SLIP; |
| if (ioctl_or_warn(handle, TIOCSETD, &disc) < 0) { |
| goto bad; |
| } |
| |
| /* Set encapsulation (SLIP, CSLIP, etc) */ |
| if (ioctl_or_warn(handle, SIOCSIFENCAP, &encap) < 0) { |
| bad: |
| restore_state_and_exit(EXIT_FAILURE); |
| } |
| } |
| |
| 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 */ |
| ; |
| |
| int i, encap, opt; |
| struct termios state; |
| const char *proto = "cslip"; |
| const char *extcmd; /* Command to execute after hangup */ |
| const char *baud_str; |
| int baud_code = -1; /* Line baud rate (system code) */ |
| |
| 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", &proto, &baud_str, &extcmd); |
| /*argc -= optind;*/ |
| argv += optind; |
| |
| if (!*argv) |
| bb_show_usage(); |
| |
| 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"); |
| } |
| |
| /* 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); |
| } |
| |
| /* Open tty */ |
| handle = open(*argv, O_RDWR | O_NDELAY); |
| if (handle < 0) { |
| char *buf = concat_path_file("/dev", *argv); |
| handle = xopen(buf, O_RDWR | O_NDELAY); |
| /* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */ |
| free(buf); |
| } |
| |
| /* Save current tty state */ |
| save_state(); |
| |
| /* Configure tty */ |
| memcpy(&state, &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(&saved_state)); |
| cfsetospeed(&state, cfgetospeed(&saved_state)); |
| } |
| |
| if (opt & OPT_s_baud) { |
| cfsetispeed(&state, baud_code); |
| cfsetospeed(&state, baud_code); |
| } |
| |
| set_state(&state, encap); |
| |
| /* Exit now if option -e was passed */ |
| if (opt & OPT_e_quit) |
| return 0; |
| |
| /* 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) { |
| if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR)) |
| goto no_carrier; |
| sleep(15); |
| } |
| |
| no_carrier: |
| |
| /* Execute command on hangup */ |
| if (opt & OPT_c_extcmd) |
| system(extcmd); |
| |
| /* Restore states and exit */ |
| restore_state_and_exit(EXIT_SUCCESS); |
| } |