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