Annotation of /trunk/mkinitrd-magellan/busybox/networking/ifupdown.c
Parent Directory | Revision Log
Revision 984 -
(hide annotations)
(download)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 34770 byte(s)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 34770 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 | niro | 532 | /* vi: set sw=4 ts=4: */ |
2 | /* | ||
3 | * ifupdown for busybox | ||
4 | niro | 816 | * Copyright (c) 2002 Glenn McGrath |
5 | niro | 532 | * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org> |
6 | * | ||
7 | * Based on ifupdown v 0.6.4 by Anthony Towns | ||
8 | * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au> | ||
9 | * | ||
10 | * Changes to upstream version | ||
11 | * Remove checks for kernel version, assume kernel version 2.2.0 or better. | ||
12 | * Lines in the interfaces file cannot wrap. | ||
13 | niro | 816 | * To adhere to the FHS, the default state file is /var/run/ifstate |
14 | * (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build | ||
15 | * configuration. | ||
16 | niro | 532 | * |
17 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | ||
18 | */ | ||
19 | |||
20 | niro | 984 | #include "libbb.h" |
21 | /* After libbb.h, since it needs sys/types.h on some systems */ | ||
22 | niro | 532 | #include <sys/utsname.h> |
23 | #include <fnmatch.h> | ||
24 | |||
25 | #define MAX_OPT_DEPTH 10 | ||
26 | #define EUNBALBRACK 10001 | ||
27 | #define EUNDEFVAR 10002 | ||
28 | #define EUNBALPER 10000 | ||
29 | |||
30 | #if ENABLE_FEATURE_IFUPDOWN_MAPPING | ||
31 | #define MAX_INTERFACE_LENGTH 10 | ||
32 | #endif | ||
33 | |||
34 | niro | 984 | #define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS |
35 | |||
36 | niro | 532 | #define debug_noise(args...) /*fprintf(stderr, args)*/ |
37 | |||
38 | /* Forward declaration */ | ||
39 | struct interface_defn_t; | ||
40 | |||
41 | typedef int execfn(char *command); | ||
42 | |||
43 | niro | 816 | struct method_t { |
44 | const char *name; | ||
45 | niro | 984 | int (*up)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC; |
46 | int (*down)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC; | ||
47 | niro | 532 | }; |
48 | |||
49 | niro | 816 | struct address_family_t { |
50 | const char *name; | ||
51 | niro | 532 | int n_methods; |
52 | const struct method_t *method; | ||
53 | }; | ||
54 | |||
55 | niro | 816 | struct mapping_defn_t { |
56 | niro | 532 | struct mapping_defn_t *next; |
57 | |||
58 | int max_matches; | ||
59 | int n_matches; | ||
60 | char **match; | ||
61 | |||
62 | char *script; | ||
63 | |||
64 | int max_mappings; | ||
65 | int n_mappings; | ||
66 | char **mapping; | ||
67 | }; | ||
68 | |||
69 | niro | 816 | struct variable_t { |
70 | niro | 532 | char *name; |
71 | char *value; | ||
72 | }; | ||
73 | |||
74 | niro | 816 | struct interface_defn_t { |
75 | niro | 532 | const struct address_family_t *address_family; |
76 | const struct method_t *method; | ||
77 | |||
78 | char *iface; | ||
79 | int max_options; | ||
80 | int n_options; | ||
81 | struct variable_t *option; | ||
82 | }; | ||
83 | |||
84 | niro | 816 | struct interfaces_file_t { |
85 | niro | 532 | llist_t *autointerfaces; |
86 | llist_t *ifaces; | ||
87 | struct mapping_defn_t *mappings; | ||
88 | }; | ||
89 | |||
90 | niro | 984 | |
91 | #define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:" | ||
92 | niro | 532 | enum { |
93 | niro | 984 | OPT_do_all = 0x1, |
94 | OPT_no_act = 0x2, | ||
95 | OPT_verbose = 0x4, | ||
96 | OPT_force = 0x8, | ||
97 | niro | 532 | OPT_no_mappings = 0x10, |
98 | }; | ||
99 | niro | 984 | #define DO_ALL (option_mask32 & OPT_do_all) |
100 | #define NO_ACT (option_mask32 & OPT_no_act) | ||
101 | #define VERBOSE (option_mask32 & OPT_verbose) | ||
102 | #define FORCE (option_mask32 & OPT_force) | ||
103 | niro | 532 | #define NO_MAPPINGS (option_mask32 & OPT_no_mappings) |
104 | |||
105 | |||
106 | niro | 984 | struct globals { |
107 | char **my_environ; | ||
108 | const char *startup_PATH; | ||
109 | }; | ||
110 | #define G (*(struct globals*)&bb_common_bufsiz1) | ||
111 | #define INIT_G() do { } while (0) | ||
112 | niro | 532 | |
113 | niro | 984 | |
114 | niro | 532 | #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6 |
115 | |||
116 | static void addstr(char **bufp, const char *str, size_t str_length) | ||
117 | { | ||
118 | /* xasprintf trick will be smaller, but we are often | ||
119 | * called with str_length == 1 - don't want to have | ||
120 | * THAT much of malloc/freeing! */ | ||
121 | char *buf = *bufp; | ||
122 | int len = (buf ? strlen(buf) : 0); | ||
123 | str_length++; | ||
124 | buf = xrealloc(buf, len + str_length); | ||
125 | /* copies at most str_length-1 chars! */ | ||
126 | safe_strncpy(buf + len, str, str_length); | ||
127 | *bufp = buf; | ||
128 | } | ||
129 | |||
130 | static int strncmpz(const char *l, const char *r, size_t llen) | ||
131 | { | ||
132 | int i = strncmp(l, r, llen); | ||
133 | |||
134 | if (i == 0) | ||
135 | niro | 984 | return - (unsigned char)r[llen]; |
136 | niro | 532 | return i; |
137 | } | ||
138 | |||
139 | static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd) | ||
140 | { | ||
141 | int i; | ||
142 | |||
143 | if (strncmpz(id, "iface", idlen) == 0) { | ||
144 | niro | 984 | // ubuntu's ifup doesn't do this: |
145 | //static char *label_buf; | ||
146 | niro | 816 | //char *result; |
147 | niro | 984 | //free(label_buf); |
148 | //label_buf = xstrdup(ifd->iface); | ||
149 | // Remove virtual iface suffix | ||
150 | niro | 816 | //result = strchrnul(label_buf, ':'); |
151 | //*result = '\0'; | ||
152 | niro | 984 | //return label_buf; |
153 | |||
154 | return ifd->iface; | ||
155 | niro | 532 | } |
156 | if (strncmpz(id, "label", idlen) == 0) { | ||
157 | return ifd->iface; | ||
158 | } | ||
159 | for (i = 0; i < ifd->n_options; i++) { | ||
160 | if (strncmpz(id, ifd->option[i].name, idlen) == 0) { | ||
161 | return ifd->option[i].value; | ||
162 | } | ||
163 | } | ||
164 | return NULL; | ||
165 | } | ||
166 | |||
167 | niro | 816 | #if ENABLE_FEATURE_IFUPDOWN_IP |
168 | static int count_netmask_bits(const char *dotted_quad) | ||
169 | { | ||
170 | // int result; | ||
171 | // unsigned a, b, c, d; | ||
172 | // /* Found a netmask... Check if it is dotted quad */ | ||
173 | // if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) | ||
174 | // return -1; | ||
175 | // if ((a|b|c|d) >> 8) | ||
176 | // return -1; /* one of numbers is >= 256 */ | ||
177 | // d |= (a << 24) | (b << 16) | (c << 8); /* IP */ | ||
178 | // d = ~d; /* 11110000 -> 00001111 */ | ||
179 | |||
180 | /* Shorter version */ | ||
181 | int result; | ||
182 | struct in_addr ip; | ||
183 | unsigned d; | ||
184 | |||
185 | if (inet_aton(dotted_quad, &ip) == 0) | ||
186 | return -1; /* malformed dotted IP */ | ||
187 | d = ntohl(ip.s_addr); /* IP in host order */ | ||
188 | d = ~d; /* 11110000 -> 00001111 */ | ||
189 | if (d & (d+1)) /* check that it is in 00001111 form */ | ||
190 | return -1; /* no it is not */ | ||
191 | result = 32; | ||
192 | while (d) { | ||
193 | d >>= 1; | ||
194 | result--; | ||
195 | } | ||
196 | return result; | ||
197 | } | ||
198 | #endif | ||
199 | |||
200 | niro | 532 | static char *parse(const char *command, struct interface_defn_t *ifd) |
201 | { | ||
202 | size_t old_pos[MAX_OPT_DEPTH] = { 0 }; | ||
203 | int okay[MAX_OPT_DEPTH] = { 1 }; | ||
204 | int opt_depth = 1; | ||
205 | char *result = NULL; | ||
206 | |||
207 | while (*command) { | ||
208 | switch (*command) { | ||
209 | default: | ||
210 | addstr(&result, command, 1); | ||
211 | command++; | ||
212 | break; | ||
213 | case '\\': | ||
214 | if (command[1]) { | ||
215 | addstr(&result, command + 1, 1); | ||
216 | command += 2; | ||
217 | } else { | ||
218 | addstr(&result, command, 1); | ||
219 | command++; | ||
220 | } | ||
221 | break; | ||
222 | case '[': | ||
223 | if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) { | ||
224 | old_pos[opt_depth] = result ? strlen(result) : 0; | ||
225 | okay[opt_depth] = 1; | ||
226 | opt_depth++; | ||
227 | command += 2; | ||
228 | } else { | ||
229 | addstr(&result, "[", 1); | ||
230 | command++; | ||
231 | } | ||
232 | break; | ||
233 | case ']': | ||
234 | if (command[1] == ']' && opt_depth > 1) { | ||
235 | opt_depth--; | ||
236 | if (!okay[opt_depth]) { | ||
237 | result[old_pos[opt_depth]] = '\0'; | ||
238 | } | ||
239 | command += 2; | ||
240 | } else { | ||
241 | addstr(&result, "]", 1); | ||
242 | command++; | ||
243 | } | ||
244 | break; | ||
245 | case '%': | ||
246 | { | ||
247 | char *nextpercent; | ||
248 | char *varvalue; | ||
249 | |||
250 | command++; | ||
251 | nextpercent = strchr(command, '%'); | ||
252 | if (!nextpercent) { | ||
253 | errno = EUNBALPER; | ||
254 | free(result); | ||
255 | return NULL; | ||
256 | } | ||
257 | |||
258 | varvalue = get_var(command, nextpercent - command, ifd); | ||
259 | |||
260 | if (varvalue) { | ||
261 | niro | 816 | #if ENABLE_FEATURE_IFUPDOWN_IP |
262 | /* "hwaddress <class> <address>": | ||
263 | * unlike ifconfig, ip doesnt want <class> | ||
264 | * (usually "ether" keyword). Skip it. */ | ||
265 | if (strncmp(command, "hwaddress", 9) == 0) { | ||
266 | varvalue = skip_whitespace(skip_non_whitespace(varvalue)); | ||
267 | } | ||
268 | #endif | ||
269 | niro | 532 | addstr(&result, varvalue, strlen(varvalue)); |
270 | } else { | ||
271 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
272 | /* Sigh... Add a special case for 'ip' to convert from | ||
273 | * dotted quad to bit count style netmasks. */ | ||
274 | if (strncmp(command, "bnmask", 6) == 0) { | ||
275 | unsigned res; | ||
276 | varvalue = get_var("netmask", 7, ifd); | ||
277 | niro | 816 | if (varvalue) { |
278 | res = count_netmask_bits(varvalue); | ||
279 | if (res > 0) { | ||
280 | const char *argument = utoa(res); | ||
281 | addstr(&result, argument, strlen(argument)); | ||
282 | command = nextpercent + 1; | ||
283 | break; | ||
284 | } | ||
285 | niro | 532 | } |
286 | } | ||
287 | #endif | ||
288 | okay[opt_depth - 1] = 0; | ||
289 | } | ||
290 | |||
291 | command = nextpercent + 1; | ||
292 | } | ||
293 | break; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | if (opt_depth > 1) { | ||
298 | errno = EUNBALBRACK; | ||
299 | free(result); | ||
300 | return NULL; | ||
301 | } | ||
302 | |||
303 | if (!okay[0]) { | ||
304 | errno = EUNDEFVAR; | ||
305 | free(result); | ||
306 | return NULL; | ||
307 | } | ||
308 | |||
309 | return result; | ||
310 | } | ||
311 | |||
312 | /* execute() returns 1 for success and 0 for failure */ | ||
313 | static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec) | ||
314 | { | ||
315 | char *out; | ||
316 | int ret; | ||
317 | |||
318 | out = parse(command, ifd); | ||
319 | if (!out) { | ||
320 | /* parse error? */ | ||
321 | return 0; | ||
322 | } | ||
323 | /* out == "": parsed ok but not all needed variables known, skip */ | ||
324 | ret = out[0] ? (*exec)(out) : 1; | ||
325 | |||
326 | free(out); | ||
327 | if (ret != 1) { | ||
328 | return 0; | ||
329 | } | ||
330 | return 1; | ||
331 | } | ||
332 | #endif | ||
333 | |||
334 | #if ENABLE_FEATURE_IFUPDOWN_IPV6 | ||
335 | niro | 984 | static int FAST_FUNC loopback_up6(struct interface_defn_t *ifd, execfn *exec) |
336 | niro | 532 | { |
337 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
338 | int result; | ||
339 | result = execute("ip addr add ::1 dev %iface%", ifd, exec); | ||
340 | result += execute("ip link set %iface% up", ifd, exec); | ||
341 | return ((result == 2) ? 2 : 0); | ||
342 | #else | ||
343 | return execute("ifconfig %iface% add ::1", ifd, exec); | ||
344 | #endif | ||
345 | } | ||
346 | |||
347 | niro | 984 | static int FAST_FUNC loopback_down6(struct interface_defn_t *ifd, execfn *exec) |
348 | niro | 532 | { |
349 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
350 | return execute("ip link set %iface% down", ifd, exec); | ||
351 | #else | ||
352 | return execute("ifconfig %iface% del ::1", ifd, exec); | ||
353 | #endif | ||
354 | } | ||
355 | |||
356 | niro | 984 | static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec) |
357 | niro | 532 | { |
358 | int result; | ||
359 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
360 | result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec); | ||
361 | niro | 816 | result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); |
362 | niro | 532 | /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */ |
363 | result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); | ||
364 | #else | ||
365 | result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec); | ||
366 | result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec); | ||
367 | result += execute("[[route -A inet6 add ::/0 gw %gateway%]]", ifd, exec); | ||
368 | #endif | ||
369 | return ((result == 3) ? 3 : 0); | ||
370 | } | ||
371 | |||
372 | niro | 984 | static int FAST_FUNC static_down6(struct interface_defn_t *ifd, execfn *exec) |
373 | niro | 532 | { |
374 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
375 | return execute("ip link set %iface% down", ifd, exec); | ||
376 | #else | ||
377 | return execute("ifconfig %iface% down", ifd, exec); | ||
378 | #endif | ||
379 | } | ||
380 | |||
381 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
382 | niro | 984 | static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec) |
383 | niro | 532 | { |
384 | int result; | ||
385 | result = execute("ip tunnel add %iface% mode sit remote " | ||
386 | "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec); | ||
387 | result += execute("ip link set %iface% up", ifd, exec); | ||
388 | result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec); | ||
389 | result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); | ||
390 | return ((result == 4) ? 4 : 0); | ||
391 | } | ||
392 | |||
393 | niro | 984 | static int FAST_FUNC v4tunnel_down(struct interface_defn_t * ifd, execfn * exec) |
394 | niro | 532 | { |
395 | return execute("ip tunnel del %iface%", ifd, exec); | ||
396 | } | ||
397 | #endif | ||
398 | |||
399 | static const struct method_t methods6[] = { | ||
400 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
401 | { "v4tunnel", v4tunnel_up, v4tunnel_down, }, | ||
402 | #endif | ||
403 | { "static", static_up6, static_down6, }, | ||
404 | { "loopback", loopback_up6, loopback_down6, }, | ||
405 | }; | ||
406 | |||
407 | static const struct address_family_t addr_inet6 = { | ||
408 | "inet6", | ||
409 | niro | 816 | ARRAY_SIZE(methods6), |
410 | niro | 532 | methods6 |
411 | }; | ||
412 | #endif /* FEATURE_IFUPDOWN_IPV6 */ | ||
413 | |||
414 | #if ENABLE_FEATURE_IFUPDOWN_IPV4 | ||
415 | niro | 984 | static int FAST_FUNC loopback_up(struct interface_defn_t *ifd, execfn *exec) |
416 | niro | 532 | { |
417 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
418 | int result; | ||
419 | result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec); | ||
420 | result += execute("ip link set %iface% up", ifd, exec); | ||
421 | return ((result == 2) ? 2 : 0); | ||
422 | #else | ||
423 | return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec); | ||
424 | #endif | ||
425 | } | ||
426 | |||
427 | niro | 984 | static int FAST_FUNC loopback_down(struct interface_defn_t *ifd, execfn *exec) |
428 | niro | 532 | { |
429 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
430 | int result; | ||
431 | result = execute("ip addr flush dev %iface%", ifd, exec); | ||
432 | result += execute("ip link set %iface% down", ifd, exec); | ||
433 | return ((result == 2) ? 2 : 0); | ||
434 | #else | ||
435 | return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec); | ||
436 | #endif | ||
437 | } | ||
438 | |||
439 | niro | 984 | static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec) |
440 | niro | 532 | { |
441 | int result; | ||
442 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
443 | result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] " | ||
444 | "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec); | ||
445 | niro | 816 | result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); |
446 | niro | 532 | result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec); |
447 | return ((result == 3) ? 3 : 0); | ||
448 | #else | ||
449 | /* ifconfig said to set iface up before it processes hw %hwaddress%, | ||
450 | * which then of course fails. Thus we run two separate ifconfig */ | ||
451 | result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up", | ||
452 | ifd, exec); | ||
453 | result += execute("ifconfig %iface% %address% netmask %netmask%" | ||
454 | "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ", | ||
455 | ifd, exec); | ||
456 | niro | 816 | result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec); |
457 | niro | 532 | return ((result == 3) ? 3 : 0); |
458 | #endif | ||
459 | } | ||
460 | |||
461 | niro | 984 | static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec) |
462 | niro | 532 | { |
463 | int result; | ||
464 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
465 | result = execute("ip addr flush dev %iface%", ifd, exec); | ||
466 | result += execute("ip link set %iface% down", ifd, exec); | ||
467 | #else | ||
468 | niro | 816 | /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */ |
469 | /* Bringing the interface down deletes the routes in itself. | ||
470 | Otherwise this fails if we reference 'gateway' when using this from dhcp_down */ | ||
471 | result = 1; | ||
472 | niro | 532 | result += execute("ifconfig %iface% down", ifd, exec); |
473 | #endif | ||
474 | return ((result == 2) ? 2 : 0); | ||
475 | } | ||
476 | |||
477 | niro | 816 | #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP |
478 | niro | 984 | struct dhcp_client_t { |
479 | niro | 532 | const char *name; |
480 | const char *startcmd; | ||
481 | const char *stopcmd; | ||
482 | }; | ||
483 | |||
484 | static const struct dhcp_client_t ext_dhcp_clients[] = { | ||
485 | niro | 816 | { "dhcpcd", |
486 | "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%", | ||
487 | "dhcpcd -k %iface%", | ||
488 | niro | 532 | }, |
489 | niro | 816 | { "dhclient", |
490 | "dhclient -pf /var/run/dhclient.%iface%.pid %iface%", | ||
491 | "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", | ||
492 | }, | ||
493 | niro | 532 | { "pump", |
494 | "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]", | ||
495 | "pump -i %iface% -k", | ||
496 | }, | ||
497 | niro | 816 | { "udhcpc", |
498 | niro | 984 | "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]]" |
499 | niro | 816 | "[[ -s %script%]][[ %udhcpc_opts%]]", |
500 | "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", | ||
501 | niro | 532 | }, |
502 | }; | ||
503 | niro | 816 | #endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */ |
504 | niro | 532 | |
505 | niro | 816 | #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP |
506 | niro | 984 | static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec) |
507 | niro | 532 | { |
508 | niro | 816 | unsigned i; |
509 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
510 | /* ip doesn't up iface when it configures it (unlike ifconfig) */ | ||
511 | if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec)) | ||
512 | return 0; | ||
513 | niro | 532 | #else |
514 | niro | 816 | /* needed if we have hwaddress on dhcp iface */ |
515 | if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec)) | ||
516 | return 0; | ||
517 | #endif | ||
518 | for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { | ||
519 | niro | 532 | if (exists_execable(ext_dhcp_clients[i].name)) |
520 | return execute(ext_dhcp_clients[i].startcmd, ifd, exec); | ||
521 | } | ||
522 | bb_error_msg("no dhcp clients found"); | ||
523 | return 0; | ||
524 | niro | 816 | } |
525 | niro | 984 | #elif ENABLE_UDHCPC |
526 | static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec) | ||
527 | niro | 816 | { |
528 | #if ENABLE_FEATURE_IFUPDOWN_IP | ||
529 | /* ip doesn't up iface when it configures it (unlike ifconfig) */ | ||
530 | if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec)) | ||
531 | return 0; | ||
532 | #else | ||
533 | /* needed if we have hwaddress on dhcp iface */ | ||
534 | if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec)) | ||
535 | return 0; | ||
536 | niro | 532 | #endif |
537 | niro | 984 | return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid " |
538 | niro | 816 | "-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]][[ %udhcpc_opts%]]", |
539 | ifd, exec); | ||
540 | niro | 532 | } |
541 | niro | 816 | #else |
542 | niro | 984 | static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM, |
543 | niro | 816 | execfn *exec UNUSED_PARAM) |
544 | { | ||
545 | return 0; /* no dhcp support */ | ||
546 | } | ||
547 | #endif | ||
548 | niro | 532 | |
549 | niro | 816 | #if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP |
550 | niro | 984 | static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec) |
551 | niro | 532 | { |
552 | niro | 816 | int result = 0; |
553 | unsigned i; | ||
554 | |||
555 | for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { | ||
556 | if (exists_execable(ext_dhcp_clients[i].name)) { | ||
557 | niro | 984 | result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec); |
558 | niro | 816 | if (result) |
559 | break; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | if (!result) | ||
564 | bb_error_msg("warning: no dhcp clients found and stopped"); | ||
565 | |||
566 | /* Sleep a bit, otherwise static_down tries to bring down interface too soon, | ||
567 | and it may come back up because udhcpc is still shutting down */ | ||
568 | usleep(100000); | ||
569 | result += static_down(ifd, exec); | ||
570 | return ((result == 3) ? 3 : 0); | ||
571 | } | ||
572 | niro | 984 | #elif ENABLE_UDHCPC |
573 | static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec) | ||
574 | niro | 816 | { |
575 | int result; | ||
576 | result = execute("kill " | ||
577 | niro | 532 | "`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); |
578 | niro | 816 | /* Also bring the hardware interface down since |
579 | killing the dhcp client alone doesn't do it. | ||
580 | This enables consecutive ifup->ifdown->ifup */ | ||
581 | /* Sleep a bit, otherwise static_down tries to bring down interface too soon, | ||
582 | and it may come back up because udhcpc is still shutting down */ | ||
583 | usleep(100000); | ||
584 | result += static_down(ifd, exec); | ||
585 | return ((result == 3) ? 3 : 0); | ||
586 | } | ||
587 | niro | 532 | #else |
588 | niro | 984 | static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM, |
589 | niro | 816 | execfn *exec UNUSED_PARAM) |
590 | { | ||
591 | return 0; /* no dhcp support */ | ||
592 | } | ||
593 | niro | 532 | #endif |
594 | |||
595 | niro | 984 | static int FAST_FUNC manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM) |
596 | niro | 532 | { |
597 | return 1; | ||
598 | } | ||
599 | |||
600 | niro | 984 | static int FAST_FUNC bootp_up(struct interface_defn_t *ifd, execfn *exec) |
601 | niro | 532 | { |
602 | return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%" | ||
603 | niro | 816 | "[[ --server %server%]][[ --hwaddr %hwaddr%]]" |
604 | " --returniffail --serverbcast", ifd, exec); | ||
605 | niro | 532 | } |
606 | |||
607 | niro | 984 | static int FAST_FUNC ppp_up(struct interface_defn_t *ifd, execfn *exec) |
608 | niro | 532 | { |
609 | return execute("pon[[ %provider%]]", ifd, exec); | ||
610 | } | ||
611 | |||
612 | niro | 984 | static int FAST_FUNC ppp_down(struct interface_defn_t *ifd, execfn *exec) |
613 | niro | 532 | { |
614 | return execute("poff[[ %provider%]]", ifd, exec); | ||
615 | } | ||
616 | |||
617 | niro | 984 | static int FAST_FUNC wvdial_up(struct interface_defn_t *ifd, execfn *exec) |
618 | niro | 532 | { |
619 | return execute("start-stop-daemon --start -x wvdial " | ||
620 | "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec); | ||
621 | } | ||
622 | |||
623 | niro | 984 | static int FAST_FUNC wvdial_down(struct interface_defn_t *ifd, execfn *exec) |
624 | niro | 532 | { |
625 | return execute("start-stop-daemon --stop -x wvdial " | ||
626 | "-p /var/run/wvdial.%iface% -s 2", ifd, exec); | ||
627 | } | ||
628 | |||
629 | static const struct method_t methods[] = { | ||
630 | niro | 984 | { "manual" , manual_up_down, manual_up_down, }, |
631 | { "wvdial" , wvdial_up , wvdial_down , }, | ||
632 | { "ppp" , ppp_up , ppp_down , }, | ||
633 | { "static" , static_up , static_down , }, | ||
634 | { "bootp" , bootp_up , static_down , }, | ||
635 | { "dhcp" , dhcp_up , dhcp_down , }, | ||
636 | { "loopback", loopback_up , loopback_down , }, | ||
637 | niro | 532 | }; |
638 | |||
639 | static const struct address_family_t addr_inet = { | ||
640 | "inet", | ||
641 | niro | 816 | ARRAY_SIZE(methods), |
642 | niro | 532 | methods |
643 | }; | ||
644 | |||
645 | #endif /* if ENABLE_FEATURE_IFUPDOWN_IPV4 */ | ||
646 | |||
647 | niro | 984 | /* Returns pointer to the next word, or NULL. |
648 | * In 1st case, advances *buf to the word after this one. | ||
649 | */ | ||
650 | niro | 532 | static char *next_word(char **buf) |
651 | { | ||
652 | niro | 816 | unsigned length; |
653 | niro | 532 | char *word; |
654 | |||
655 | /* Skip over leading whitespace */ | ||
656 | word = skip_whitespace(*buf); | ||
657 | |||
658 | niro | 816 | /* Stop on EOL */ |
659 | if (*word == '\0') | ||
660 | niro | 532 | return NULL; |
661 | |||
662 | niro | 816 | /* Find the length of this word (can't be 0) */ |
663 | niro | 532 | length = strcspn(word, " \t\n"); |
664 | niro | 816 | |
665 | /* Unless we are already at NUL, store NUL and advance */ | ||
666 | if (word[length] != '\0') | ||
667 | word[length++] = '\0'; | ||
668 | |||
669 | niro | 984 | *buf = skip_whitespace(word + length); |
670 | niro | 532 | |
671 | return word; | ||
672 | } | ||
673 | |||
674 | static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name) | ||
675 | { | ||
676 | int i; | ||
677 | |||
678 | if (!name) | ||
679 | return NULL; | ||
680 | |||
681 | for (i = 0; af[i]; i++) { | ||
682 | if (strcmp(af[i]->name, name) == 0) { | ||
683 | return af[i]; | ||
684 | } | ||
685 | } | ||
686 | return NULL; | ||
687 | } | ||
688 | |||
689 | static const struct method_t *get_method(const struct address_family_t *af, char *name) | ||
690 | { | ||
691 | int i; | ||
692 | |||
693 | if (!name) | ||
694 | return NULL; | ||
695 | niro | 816 | /* TODO: use index_in_str_array() */ |
696 | niro | 532 | for (i = 0; i < af->n_methods; i++) { |
697 | if (strcmp(af->method[i].name, name) == 0) { | ||
698 | return &af->method[i]; | ||
699 | } | ||
700 | } | ||
701 | return NULL; | ||
702 | } | ||
703 | |||
704 | static struct interfaces_file_t *read_interfaces(const char *filename) | ||
705 | { | ||
706 | niro | 816 | /* Let's try to be compatible. |
707 | * | ||
708 | * "man 5 interfaces" says: | ||
709 | * Lines starting with "#" are ignored. Note that end-of-line | ||
710 | * comments are NOT supported, comments must be on a line of their own. | ||
711 | * A line may be extended across multiple lines by making | ||
712 | * the last character a backslash. | ||
713 | * | ||
714 | * Seen elsewhere in example config file: | ||
715 | * A first non-blank "#" character makes the rest of the line | ||
716 | * be ignored. Blank lines are ignored. Lines may be indented freely. | ||
717 | * A "\" character at the very end of the line indicates the next line | ||
718 | * should be treated as a continuation of the current one. | ||
719 | */ | ||
720 | niro | 532 | #if ENABLE_FEATURE_IFUPDOWN_MAPPING |
721 | struct mapping_defn_t *currmap = NULL; | ||
722 | #endif | ||
723 | struct interface_defn_t *currif = NULL; | ||
724 | struct interfaces_file_t *defn; | ||
725 | FILE *f; | ||
726 | char *buf; | ||
727 | niro | 816 | char *first_word; |
728 | char *rest_of_line; | ||
729 | niro | 532 | enum { NONE, IFACE, MAPPING } currently_processing = NONE; |
730 | |||
731 | niro | 816 | defn = xzalloc(sizeof(*defn)); |
732 | f = xfopen_for_read(filename); | ||
733 | niro | 532 | |
734 | niro | 816 | while ((buf = xmalloc_fgetline(f)) != NULL) { |
735 | #if ENABLE_DESKTOP | ||
736 | /* Trailing "\" concatenates lines */ | ||
737 | char *p; | ||
738 | while ((p = last_char_is(buf, '\\')) != NULL) { | ||
739 | *p = '\0'; | ||
740 | rest_of_line = xmalloc_fgetline(f); | ||
741 | if (!rest_of_line) | ||
742 | break; | ||
743 | p = xasprintf("%s%s", buf, rest_of_line); | ||
744 | niro | 532 | free(buf); |
745 | niro | 816 | free(rest_of_line); |
746 | buf = p; | ||
747 | niro | 532 | } |
748 | niro | 816 | #endif |
749 | rest_of_line = buf; | ||
750 | first_word = next_word(&rest_of_line); | ||
751 | if (!first_word || *first_word == '#') { | ||
752 | free(buf); | ||
753 | continue; /* blank/comment line */ | ||
754 | } | ||
755 | niro | 532 | |
756 | niro | 816 | if (strcmp(first_word, "mapping") == 0) { |
757 | niro | 532 | #if ENABLE_FEATURE_IFUPDOWN_MAPPING |
758 | niro | 816 | currmap = xzalloc(sizeof(*currmap)); |
759 | niro | 532 | |
760 | niro | 816 | while ((first_word = next_word(&rest_of_line)) != NULL) { |
761 | currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches); | ||
762 | currmap->match[currmap->n_matches++] = xstrdup(first_word); | ||
763 | niro | 532 | } |
764 | niro | 816 | /*currmap->max_mappings = 0; - done by xzalloc */ |
765 | /*currmap->n_mappings = 0;*/ | ||
766 | /*currmap->mapping = NULL;*/ | ||
767 | /*currmap->script = NULL;*/ | ||
768 | niro | 532 | { |
769 | struct mapping_defn_t **where = &defn->mappings; | ||
770 | while (*where != NULL) { | ||
771 | where = &(*where)->next; | ||
772 | } | ||
773 | *where = currmap; | ||
774 | niro | 816 | /*currmap->next = NULL;*/ |
775 | niro | 532 | } |
776 | debug_noise("Added mapping\n"); | ||
777 | #endif | ||
778 | currently_processing = MAPPING; | ||
779 | niro | 816 | } else if (strcmp(first_word, "iface") == 0) { |
780 | niro | 532 | static const struct address_family_t *const addr_fams[] = { |
781 | #if ENABLE_FEATURE_IFUPDOWN_IPV4 | ||
782 | &addr_inet, | ||
783 | #endif | ||
784 | #if ENABLE_FEATURE_IFUPDOWN_IPV6 | ||
785 | &addr_inet6, | ||
786 | #endif | ||
787 | NULL | ||
788 | }; | ||
789 | char *iface_name; | ||
790 | char *address_family_name; | ||
791 | char *method_name; | ||
792 | llist_t *iface_list; | ||
793 | |||
794 | niro | 816 | currif = xzalloc(sizeof(*currif)); |
795 | iface_name = next_word(&rest_of_line); | ||
796 | address_family_name = next_word(&rest_of_line); | ||
797 | method_name = next_word(&rest_of_line); | ||
798 | niro | 532 | |
799 | niro | 816 | if (method_name == NULL) |
800 | bb_error_msg_and_die("too few parameters for line \"%s\"", buf); | ||
801 | niro | 532 | |
802 | /* ship any trailing whitespace */ | ||
803 | niro | 816 | rest_of_line = skip_whitespace(rest_of_line); |
804 | niro | 532 | |
805 | niro | 816 | if (rest_of_line[0] != '\0' /* && rest_of_line[0] != '#' */) |
806 | bb_error_msg_and_die("too many parameters \"%s\"", buf); | ||
807 | niro | 532 | |
808 | currif->iface = xstrdup(iface_name); | ||
809 | |||
810 | currif->address_family = get_address_family(addr_fams, address_family_name); | ||
811 | niro | 816 | if (!currif->address_family) |
812 | bb_error_msg_and_die("unknown address type \"%s\"", address_family_name); | ||
813 | niro | 532 | |
814 | currif->method = get_method(currif->address_family, method_name); | ||
815 | niro | 816 | if (!currif->method) |
816 | bb_error_msg_and_die("unknown method \"%s\"", method_name); | ||
817 | niro | 532 | |
818 | for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) { | ||
819 | struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data; | ||
820 | niro | 816 | if ((strcmp(tmp->iface, currif->iface) == 0) |
821 | && (tmp->address_family == currif->address_family) | ||
822 | ) { | ||
823 | bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface); | ||
824 | niro | 532 | } |
825 | } | ||
826 | llist_add_to_end(&(defn->ifaces), (char*)currif); | ||
827 | |||
828 | debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name); | ||
829 | currently_processing = IFACE; | ||
830 | niro | 816 | } else if (strcmp(first_word, "auto") == 0) { |
831 | while ((first_word = next_word(&rest_of_line)) != NULL) { | ||
832 | niro | 532 | |
833 | /* Check the interface isnt already listed */ | ||
834 | niro | 984 | if (llist_find_str(defn->autointerfaces, first_word)) { |
835 | niro | 532 | bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf); |
836 | } | ||
837 | |||
838 | /* Add the interface to the list */ | ||
839 | niro | 816 | llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word)); |
840 | debug_noise("\nauto %s\n", first_word); | ||
841 | niro | 532 | } |
842 | currently_processing = NONE; | ||
843 | } else { | ||
844 | switch (currently_processing) { | ||
845 | case IFACE: | ||
846 | niro | 816 | if (rest_of_line[0] == '\0') |
847 | bb_error_msg_and_die("option with empty value \"%s\"", buf); | ||
848 | |||
849 | if (strcmp(first_word, "up") != 0 | ||
850 | && strcmp(first_word, "down") != 0 | ||
851 | && strcmp(first_word, "pre-up") != 0 | ||
852 | && strcmp(first_word, "post-down") != 0 | ||
853 | ) { | ||
854 | niro | 532 | int i; |
855 | niro | 816 | for (i = 0; i < currif->n_options; i++) { |
856 | if (strcmp(currif->option[i].name, first_word) == 0) | ||
857 | bb_error_msg_and_die("duplicate option \"%s\"", buf); | ||
858 | niro | 532 | } |
859 | } | ||
860 | if (currif->n_options >= currif->max_options) { | ||
861 | niro | 816 | currif->max_options += 10; |
862 | currif->option = xrealloc(currif->option, | ||
863 | sizeof(*currif->option) * currif->max_options); | ||
864 | niro | 532 | } |
865 | niro | 816 | debug_noise("\t%s=%s\n", first_word, rest_of_line); |
866 | currif->option[currif->n_options].name = xstrdup(first_word); | ||
867 | currif->option[currif->n_options].value = xstrdup(rest_of_line); | ||
868 | niro | 532 | currif->n_options++; |
869 | break; | ||
870 | case MAPPING: | ||
871 | #if ENABLE_FEATURE_IFUPDOWN_MAPPING | ||
872 | niro | 816 | if (strcmp(first_word, "script") == 0) { |
873 | if (currmap->script != NULL) | ||
874 | bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf); | ||
875 | currmap->script = xstrdup(next_word(&rest_of_line)); | ||
876 | } else if (strcmp(first_word, "map") == 0) { | ||
877 | if (currmap->n_mappings >= currmap->max_mappings) { | ||
878 | niro | 532 | currmap->max_mappings = currmap->max_mappings * 2 + 1; |
879 | niro | 816 | currmap->mapping = xrealloc(currmap->mapping, |
880 | sizeof(char *) * currmap->max_mappings); | ||
881 | niro | 532 | } |
882 | niro | 816 | currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line)); |
883 | niro | 532 | currmap->n_mappings++; |
884 | } else { | ||
885 | niro | 816 | bb_error_msg_and_die("misplaced option \"%s\"", buf); |
886 | niro | 532 | } |
887 | #endif | ||
888 | break; | ||
889 | case NONE: | ||
890 | default: | ||
891 | niro | 816 | bb_error_msg_and_die("misplaced option \"%s\"", buf); |
892 | niro | 532 | } |
893 | } | ||
894 | free(buf); | ||
895 | niro | 816 | } /* while (fgets) */ |
896 | |||
897 | niro | 532 | if (ferror(f) != 0) { |
898 | niro | 816 | /* ferror does NOT set errno! */ |
899 | bb_error_msg_and_die("%s: I/O error", filename); | ||
900 | niro | 532 | } |
901 | fclose(f); | ||
902 | |||
903 | return defn; | ||
904 | } | ||
905 | |||
906 | niro | 816 | static char *setlocalenv(const char *format, const char *name, const char *value) |
907 | niro | 532 | { |
908 | char *result; | ||
909 | niro | 984 | char *dst; |
910 | char *src; | ||
911 | char c; | ||
912 | niro | 532 | |
913 | result = xasprintf(format, name, value); | ||
914 | |||
915 | niro | 984 | for (dst = src = result; (c = *src) != '=' && c; src++) { |
916 | if (c == '-') | ||
917 | c = '_'; | ||
918 | if (c >= 'a' && c <= 'z') | ||
919 | c -= ('a' - 'A'); | ||
920 | if (isalnum(c) || c == '_') | ||
921 | *dst++ = c; | ||
922 | niro | 532 | } |
923 | niro | 984 | overlapping_strcpy(dst, src); |
924 | niro | 532 | |
925 | return result; | ||
926 | } | ||
927 | |||
928 | static void set_environ(struct interface_defn_t *iface, const char *mode) | ||
929 | { | ||
930 | int i; | ||
931 | niro | 984 | char **pp; |
932 | niro | 532 | |
933 | niro | 984 | if (G.my_environ != NULL) { |
934 | for (pp = G.my_environ; *pp; pp++) { | ||
935 | free(*pp); | ||
936 | niro | 532 | } |
937 | niro | 984 | free(G.my_environ); |
938 | niro | 532 | } |
939 | |||
940 | niro | 984 | /* note: last element will stay NULL: */ |
941 | G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 6)); | ||
942 | pp = G.my_environ; | ||
943 | |||
944 | niro | 532 | for (i = 0; i < iface->n_options; i++) { |
945 | if (strcmp(iface->option[i].name, "up") == 0 | ||
946 | niro | 816 | || strcmp(iface->option[i].name, "down") == 0 |
947 | || strcmp(iface->option[i].name, "pre-up") == 0 | ||
948 | || strcmp(iface->option[i].name, "post-down") == 0 | ||
949 | ) { | ||
950 | niro | 532 | continue; |
951 | } | ||
952 | niro | 984 | *pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); |
953 | niro | 532 | } |
954 | |||
955 | niro | 984 | *pp++ = setlocalenv("%s=%s", "IFACE", iface->iface); |
956 | *pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name); | ||
957 | *pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name); | ||
958 | *pp++ = setlocalenv("%s=%s", "MODE", mode); | ||
959 | if (G.startup_PATH) | ||
960 | *pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH); | ||
961 | niro | 532 | } |
962 | |||
963 | static int doit(char *str) | ||
964 | { | ||
965 | if (option_mask32 & (OPT_no_act|OPT_verbose)) { | ||
966 | puts(str); | ||
967 | } | ||
968 | if (!(option_mask32 & OPT_no_act)) { | ||
969 | pid_t child; | ||
970 | int status; | ||
971 | |||
972 | niro | 984 | fflush_all(); |
973 | niro | 816 | child = vfork(); |
974 | niro | 532 | switch (child) { |
975 | case -1: /* failure */ | ||
976 | return 0; | ||
977 | case 0: /* child */ | ||
978 | niro | 984 | execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, (char *) NULL, G.my_environ); |
979 | niro | 816 | _exit(127); |
980 | niro | 532 | } |
981 | niro | 816 | safe_waitpid(child, &status, 0); |
982 | niro | 532 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
983 | return 0; | ||
984 | } | ||
985 | } | ||
986 | return 1; | ||
987 | } | ||
988 | |||
989 | static int execute_all(struct interface_defn_t *ifd, const char *opt) | ||
990 | { | ||
991 | int i; | ||
992 | char *buf; | ||
993 | for (i = 0; i < ifd->n_options; i++) { | ||
994 | if (strcmp(ifd->option[i].name, opt) == 0) { | ||
995 | if (!doit(ifd->option[i].value)) { | ||
996 | return 0; | ||
997 | } | ||
998 | } | ||
999 | } | ||
1000 | |||
1001 | buf = xasprintf("run-parts /etc/network/if-%s.d", opt); | ||
1002 | /* heh, we don't bother free'ing it */ | ||
1003 | return doit(buf); | ||
1004 | } | ||
1005 | |||
1006 | static int check(char *str) | ||
1007 | { | ||
1008 | return str != NULL; | ||
1009 | } | ||
1010 | |||
1011 | static int iface_up(struct interface_defn_t *iface) | ||
1012 | { | ||
1013 | if (!iface->method->up(iface, check)) return -1; | ||
1014 | set_environ(iface, "start"); | ||
1015 | if (!execute_all(iface, "pre-up")) return 0; | ||
1016 | if (!iface->method->up(iface, doit)) return 0; | ||
1017 | if (!execute_all(iface, "up")) return 0; | ||
1018 | return 1; | ||
1019 | } | ||
1020 | |||
1021 | static int iface_down(struct interface_defn_t *iface) | ||
1022 | { | ||
1023 | if (!iface->method->down(iface,check)) return -1; | ||
1024 | set_environ(iface, "stop"); | ||
1025 | if (!execute_all(iface, "down")) return 0; | ||
1026 | if (!iface->method->down(iface, doit)) return 0; | ||
1027 | if (!execute_all(iface, "post-down")) return 0; | ||
1028 | return 1; | ||
1029 | } | ||
1030 | |||
1031 | #if ENABLE_FEATURE_IFUPDOWN_MAPPING | ||
1032 | niro | 816 | static int popen2(FILE **in, FILE **out, char *command, char *param) |
1033 | niro | 532 | { |
1034 | niro | 816 | char *argv[3] = { command, param, NULL }; |
1035 | struct fd_pair infd, outfd; | ||
1036 | niro | 532 | pid_t pid; |
1037 | |||
1038 | niro | 816 | xpiped_pair(infd); |
1039 | xpiped_pair(outfd); | ||
1040 | niro | 532 | |
1041 | niro | 984 | fflush_all(); |
1042 | niro | 816 | pid = vfork(); |
1043 | niro | 532 | |
1044 | niro | 816 | switch (pid) { |
1045 | case -1: /* failure */ | ||
1046 | bb_perror_msg_and_die("vfork"); | ||
1047 | case 0: /* child */ | ||
1048 | /* NB: close _first_, then move fds! */ | ||
1049 | close(infd.wr); | ||
1050 | close(outfd.rd); | ||
1051 | xmove_fd(infd.rd, 0); | ||
1052 | xmove_fd(outfd.wr, 1); | ||
1053 | BB_EXECVP(command, argv); | ||
1054 | _exit(127); | ||
1055 | niro | 532 | } |
1056 | niro | 816 | /* parent */ |
1057 | close(infd.rd); | ||
1058 | close(outfd.wr); | ||
1059 | niro | 984 | *in = xfdopen_for_write(infd.wr); |
1060 | *out = xfdopen_for_read(outfd.rd); | ||
1061 | niro | 816 | return pid; |
1062 | niro | 532 | } |
1063 | |||
1064 | niro | 816 | static char *run_mapping(char *physical, struct mapping_defn_t *map) |
1065 | niro | 532 | { |
1066 | FILE *in, *out; | ||
1067 | int i, status; | ||
1068 | pid_t pid; | ||
1069 | |||
1070 | char *logical = xstrdup(physical); | ||
1071 | |||
1072 | niro | 816 | /* Run the mapping script. Never fails. */ |
1073 | pid = popen2(&in, &out, map->script, physical); | ||
1074 | niro | 532 | |
1075 | /* Write mappings to stdin of mapping script. */ | ||
1076 | for (i = 0; i < map->n_mappings; i++) { | ||
1077 | fprintf(in, "%s\n", map->mapping[i]); | ||
1078 | } | ||
1079 | fclose(in); | ||
1080 | niro | 816 | safe_waitpid(pid, &status, 0); |
1081 | niro | 532 | |
1082 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { | ||
1083 | /* If the mapping script exited successfully, try to | ||
1084 | * grab a line of output and use that as the name of the | ||
1085 | * logical interface. */ | ||
1086 | niro | 816 | char *new_logical = xmalloc_fgetline(out); |
1087 | niro | 532 | |
1088 | niro | 816 | if (new_logical) { |
1089 | niro | 532 | /* If we are able to read a line of output from the script, |
1090 | * remove any trailing whitespace and use this value | ||
1091 | * as the name of the logical interface. */ | ||
1092 | char *pch = new_logical + strlen(new_logical) - 1; | ||
1093 | |||
1094 | while (pch >= new_logical && isspace(*pch)) | ||
1095 | *(pch--) = '\0'; | ||
1096 | |||
1097 | free(logical); | ||
1098 | logical = new_logical; | ||
1099 | } | ||
1100 | } | ||
1101 | |||
1102 | fclose(out); | ||
1103 | |||
1104 | return logical; | ||
1105 | } | ||
1106 | #endif /* FEATURE_IFUPDOWN_MAPPING */ | ||
1107 | |||
1108 | static llist_t *find_iface_state(llist_t *state_list, const char *iface) | ||
1109 | { | ||
1110 | niro | 816 | unsigned iface_len = strlen(iface); |
1111 | niro | 532 | llist_t *search = state_list; |
1112 | |||
1113 | while (search) { | ||
1114 | niro | 816 | if ((strncmp(search->data, iface, iface_len) == 0) |
1115 | && (search->data[iface_len] == '=') | ||
1116 | ) { | ||
1117 | niro | 532 | return search; |
1118 | } | ||
1119 | search = search->link; | ||
1120 | } | ||
1121 | return NULL; | ||
1122 | } | ||
1123 | |||
1124 | niro | 816 | /* read the previous state from the state file */ |
1125 | static llist_t *read_iface_state(void) | ||
1126 | { | ||
1127 | llist_t *state_list = NULL; | ||
1128 | FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH); | ||
1129 | |||
1130 | if (state_fp) { | ||
1131 | char *start, *end_ptr; | ||
1132 | while ((start = xmalloc_fgets(state_fp)) != NULL) { | ||
1133 | /* We should only need to check for a single character */ | ||
1134 | end_ptr = start + strcspn(start, " \t\n"); | ||
1135 | *end_ptr = '\0'; | ||
1136 | llist_add_to(&state_list, start); | ||
1137 | } | ||
1138 | fclose(state_fp); | ||
1139 | } | ||
1140 | return state_list; | ||
1141 | } | ||
1142 | |||
1143 | |||
1144 | int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
1145 | niro | 984 | int ifupdown_main(int argc UNUSED_PARAM, char **argv) |
1146 | niro | 532 | { |
1147 | niro | 816 | int (*cmds)(struct interface_defn_t *); |
1148 | niro | 532 | struct interfaces_file_t *defn; |
1149 | llist_t *target_list = NULL; | ||
1150 | const char *interfaces = "/etc/network/interfaces"; | ||
1151 | niro | 816 | bool any_failures = 0; |
1152 | niro | 532 | |
1153 | niro | 984 | INIT_G(); |
1154 | |||
1155 | G.startup_PATH = getenv("PATH"); | ||
1156 | |||
1157 | niro | 532 | cmds = iface_down; |
1158 | if (applet_name[2] == 'u') { | ||
1159 | /* ifup command */ | ||
1160 | cmds = iface_up; | ||
1161 | } | ||
1162 | |||
1163 | niro | 816 | getopt32(argv, OPTION_STR, &interfaces); |
1164 | niro | 984 | argv += optind; |
1165 | if (argv[0]) { | ||
1166 | niro | 532 | if (DO_ALL) bb_show_usage(); |
1167 | } else { | ||
1168 | if (!DO_ALL) bb_show_usage(); | ||
1169 | } | ||
1170 | |||
1171 | debug_noise("reading %s file:\n", interfaces); | ||
1172 | defn = read_interfaces(interfaces); | ||
1173 | debug_noise("\ndone reading %s\n\n", interfaces); | ||
1174 | |||
1175 | /* Create a list of interfaces to work on */ | ||
1176 | if (DO_ALL) { | ||
1177 | niro | 816 | target_list = defn->autointerfaces; |
1178 | niro | 532 | } else { |
1179 | niro | 984 | llist_add_to_end(&target_list, argv[0]); |
1180 | niro | 532 | } |
1181 | |||
1182 | /* Update the interfaces */ | ||
1183 | while (target_list) { | ||
1184 | llist_t *iface_list; | ||
1185 | struct interface_defn_t *currif; | ||
1186 | char *iface; | ||
1187 | char *liface; | ||
1188 | char *pch; | ||
1189 | niro | 816 | bool okay = 0; |
1190 | niro | 532 | int cmds_ret; |
1191 | |||
1192 | iface = xstrdup(target_list->data); | ||
1193 | target_list = target_list->link; | ||
1194 | |||
1195 | pch = strchr(iface, '='); | ||
1196 | if (pch) { | ||
1197 | *pch = '\0'; | ||
1198 | liface = xstrdup(pch + 1); | ||
1199 | } else { | ||
1200 | liface = xstrdup(iface); | ||
1201 | } | ||
1202 | |||
1203 | if (!FORCE) { | ||
1204 | niro | 816 | llist_t *state_list = read_iface_state(); |
1205 | niro | 532 | const llist_t *iface_state = find_iface_state(state_list, iface); |
1206 | |||
1207 | if (cmds == iface_up) { | ||
1208 | /* ifup */ | ||
1209 | if (iface_state) { | ||
1210 | bb_error_msg("interface %s already configured", iface); | ||
1211 | continue; | ||
1212 | } | ||
1213 | } else { | ||
1214 | /* ifdown */ | ||
1215 | niro | 816 | if (!iface_state) { |
1216 | niro | 532 | bb_error_msg("interface %s not configured", iface); |
1217 | continue; | ||
1218 | } | ||
1219 | } | ||
1220 | niro | 816 | llist_free(state_list, free); |
1221 | niro | 532 | } |
1222 | |||
1223 | #if ENABLE_FEATURE_IFUPDOWN_MAPPING | ||
1224 | if ((cmds == iface_up) && !NO_MAPPINGS) { | ||
1225 | struct mapping_defn_t *currmap; | ||
1226 | |||
1227 | for (currmap = defn->mappings; currmap; currmap = currmap->next) { | ||
1228 | int i; | ||
1229 | for (i = 0; i < currmap->n_matches; i++) { | ||
1230 | if (fnmatch(currmap->match[i], liface, 0) != 0) | ||
1231 | continue; | ||
1232 | if (VERBOSE) { | ||
1233 | printf("Running mapping script %s on %s\n", currmap->script, liface); | ||
1234 | } | ||
1235 | liface = run_mapping(iface, currmap); | ||
1236 | break; | ||
1237 | } | ||
1238 | } | ||
1239 | } | ||
1240 | #endif | ||
1241 | |||
1242 | iface_list = defn->ifaces; | ||
1243 | while (iface_list) { | ||
1244 | currif = (struct interface_defn_t *) iface_list->data; | ||
1245 | if (strcmp(liface, currif->iface) == 0) { | ||
1246 | char *oldiface = currif->iface; | ||
1247 | |||
1248 | okay = 1; | ||
1249 | currif->iface = iface; | ||
1250 | |||
1251 | debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name); | ||
1252 | |||
1253 | /* Call the cmds function pointer, does either iface_up() or iface_down() */ | ||
1254 | cmds_ret = cmds(currif); | ||
1255 | if (cmds_ret == -1) { | ||
1256 | bb_error_msg("don't seem to have all the variables for %s/%s", | ||
1257 | liface, currif->address_family->name); | ||
1258 | any_failures = 1; | ||
1259 | } else if (cmds_ret == 0) { | ||
1260 | any_failures = 1; | ||
1261 | } | ||
1262 | |||
1263 | currif->iface = oldiface; | ||
1264 | } | ||
1265 | iface_list = iface_list->link; | ||
1266 | } | ||
1267 | if (VERBOSE) { | ||
1268 | niro | 816 | bb_putchar('\n'); |
1269 | niro | 532 | } |
1270 | |||
1271 | if (!okay && !FORCE) { | ||
1272 | bb_error_msg("ignoring unknown interface %s", liface); | ||
1273 | any_failures = 1; | ||
1274 | niro | 816 | } else if (!NO_ACT) { |
1275 | /* update the state file */ | ||
1276 | FILE *state_fp; | ||
1277 | llist_t *state; | ||
1278 | llist_t *state_list = read_iface_state(); | ||
1279 | niro | 532 | llist_t *iface_state = find_iface_state(state_list, iface); |
1280 | |||
1281 | if (cmds == iface_up) { | ||
1282 | niro | 816 | char * const newiface = xasprintf("%s=%s", iface, liface); |
1283 | niro | 532 | if (iface_state == NULL) { |
1284 | llist_add_to_end(&state_list, newiface); | ||
1285 | } else { | ||
1286 | free(iface_state->data); | ||
1287 | iface_state->data = newiface; | ||
1288 | } | ||
1289 | } else { | ||
1290 | niro | 816 | /* Remove an interface from state_list */ |
1291 | llist_unlink(&state_list, iface_state); | ||
1292 | niro | 532 | free(llist_pop(&iface_state)); |
1293 | } | ||
1294 | |||
1295 | niro | 816 | /* Actually write the new state */ |
1296 | state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH); | ||
1297 | state = state_list; | ||
1298 | while (state) { | ||
1299 | if (state->data) { | ||
1300 | fprintf(state_fp, "%s\n", state->data); | ||
1301 | } | ||
1302 | state = state->link; | ||
1303 | niro | 532 | } |
1304 | niro | 816 | fclose(state_fp); |
1305 | llist_free(state_list, free); | ||
1306 | niro | 532 | } |
1307 | } | ||
1308 | |||
1309 | return any_failures; | ||
1310 | } |