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