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