1 |
/* vi: set sw=4 ts=4: */ |
/* vi: set sw=4 ts=4: */ |
2 |
/* |
/* |
3 |
* ifupdown for busybox |
* ifupdown for busybox |
4 |
* Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au> |
* Copyright (c) 2002 Glenn McGrath |
5 |
* Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org> |
* Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org> |
6 |
* |
* |
7 |
* Based on ifupdown v 0.6.4 by Anthony Towns |
* Based on ifupdown v 0.6.4 by Anthony Towns |
10 |
* Changes to upstream version |
* Changes to upstream version |
11 |
* Remove checks for kernel version, assume kernel version 2.2.0 or better. |
* Remove checks for kernel version, assume kernel version 2.2.0 or better. |
12 |
* Lines in the interfaces file cannot wrap. |
* Lines in the interfaces file cannot wrap. |
13 |
* To adhere to the FHS, the default state file is /var/run/ifstate. |
* 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 |
* |
* |
17 |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
18 |
*/ |
*/ |
19 |
|
|
|
#include "busybox.h" |
|
20 |
#include <sys/utsname.h> |
#include <sys/utsname.h> |
21 |
#include <fnmatch.h> |
#include <fnmatch.h> |
22 |
#include <getopt.h> |
|
23 |
|
#include "libbb.h" |
24 |
|
|
25 |
#define MAX_OPT_DEPTH 10 |
#define MAX_OPT_DEPTH 10 |
26 |
#define EUNBALBRACK 10001 |
#define EUNBALBRACK 10001 |
38 |
|
|
39 |
typedef int execfn(char *command); |
typedef int execfn(char *command); |
40 |
|
|
41 |
struct method_t |
struct method_t { |
42 |
{ |
const char *name; |
|
char *name; |
|
43 |
int (*up)(struct interface_defn_t *ifd, execfn *e); |
int (*up)(struct interface_defn_t *ifd, execfn *e); |
44 |
int (*down)(struct interface_defn_t *ifd, execfn *e); |
int (*down)(struct interface_defn_t *ifd, execfn *e); |
45 |
}; |
}; |
46 |
|
|
47 |
struct address_family_t |
struct address_family_t { |
48 |
{ |
const char *name; |
|
char *name; |
|
49 |
int n_methods; |
int n_methods; |
50 |
const struct method_t *method; |
const struct method_t *method; |
51 |
}; |
}; |
52 |
|
|
53 |
struct mapping_defn_t |
struct mapping_defn_t { |
|
{ |
|
54 |
struct mapping_defn_t *next; |
struct mapping_defn_t *next; |
55 |
|
|
56 |
int max_matches; |
int max_matches; |
64 |
char **mapping; |
char **mapping; |
65 |
}; |
}; |
66 |
|
|
67 |
struct variable_t |
struct variable_t { |
|
{ |
|
68 |
char *name; |
char *name; |
69 |
char *value; |
char *value; |
70 |
}; |
}; |
71 |
|
|
72 |
struct interface_defn_t |
struct interface_defn_t { |
|
{ |
|
73 |
const struct address_family_t *address_family; |
const struct address_family_t *address_family; |
74 |
const struct method_t *method; |
const struct method_t *method; |
75 |
|
|
79 |
struct variable_t *option; |
struct variable_t *option; |
80 |
}; |
}; |
81 |
|
|
82 |
struct interfaces_file_t |
struct interfaces_file_t { |
|
{ |
|
83 |
llist_t *autointerfaces; |
llist_t *autointerfaces; |
84 |
llist_t *ifaces; |
llist_t *ifaces; |
85 |
struct mapping_defn_t *mappings; |
struct mapping_defn_t *mappings; |
101 |
|
|
102 |
static char **my_environ; |
static char **my_environ; |
103 |
|
|
104 |
static char *startup_PATH; |
static const char *startup_PATH; |
105 |
|
|
106 |
#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6 |
#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6 |
107 |
|
|
|
#if ENABLE_FEATURE_IFUPDOWN_IP |
|
|
|
|
|
static unsigned count_bits(unsigned a) |
|
|
{ |
|
|
unsigned result; |
|
|
result = (a & 0x55) + ((a >> 1) & 0x55); |
|
|
result = (result & 0x33) + ((result >> 2) & 0x33); |
|
|
return (result & 0x0F) + ((result >> 4) & 0x0F); |
|
|
} |
|
|
|
|
|
static int count_netmask_bits(char *dotted_quad) |
|
|
{ |
|
|
unsigned result, a, b, c, d; |
|
|
/* Found a netmask... Check if it is dotted quad */ |
|
|
if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) |
|
|
return -1; |
|
|
// FIXME: will be confused by e.g. 255.0.255.0 |
|
|
result = count_bits(a); |
|
|
result += count_bits(b); |
|
|
result += count_bits(c); |
|
|
result += count_bits(d); |
|
|
return (int)result; |
|
|
} |
|
|
#endif |
|
|
|
|
108 |
static void addstr(char **bufp, const char *str, size_t str_length) |
static void addstr(char **bufp, const char *str, size_t str_length) |
109 |
{ |
{ |
110 |
/* xasprintf trick will be smaller, but we are often |
/* xasprintf trick will be smaller, but we are often |
133 |
int i; |
int i; |
134 |
|
|
135 |
if (strncmpz(id, "iface", idlen) == 0) { |
if (strncmpz(id, "iface", idlen) == 0) { |
136 |
char *result; |
static char *label_buf; |
137 |
static char label_buf[20]; |
//char *result; |
138 |
safe_strncpy(label_buf, ifd->iface, sizeof(label_buf)); |
|
139 |
result = strchr(label_buf, ':'); |
free(label_buf); |
140 |
if (result) { |
label_buf = xstrdup(ifd->iface); |
141 |
*result = '\0'; |
// Remove virtual iface suffix - why? |
142 |
} |
// ubuntu's ifup doesn't do this |
143 |
|
//result = strchrnul(label_buf, ':'); |
144 |
|
//*result = '\0'; |
145 |
return label_buf; |
return label_buf; |
146 |
} |
} |
147 |
if (strncmpz(id, "label", idlen) == 0) { |
if (strncmpz(id, "label", idlen) == 0) { |
155 |
return NULL; |
return NULL; |
156 |
} |
} |
157 |
|
|
158 |
|
#if ENABLE_FEATURE_IFUPDOWN_IP |
159 |
|
static int count_netmask_bits(const char *dotted_quad) |
160 |
|
{ |
161 |
|
// int result; |
162 |
|
// unsigned a, b, c, d; |
163 |
|
// /* Found a netmask... Check if it is dotted quad */ |
164 |
|
// if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) |
165 |
|
// return -1; |
166 |
|
// if ((a|b|c|d) >> 8) |
167 |
|
// return -1; /* one of numbers is >= 256 */ |
168 |
|
// d |= (a << 24) | (b << 16) | (c << 8); /* IP */ |
169 |
|
// d = ~d; /* 11110000 -> 00001111 */ |
170 |
|
|
171 |
|
/* Shorter version */ |
172 |
|
int result; |
173 |
|
struct in_addr ip; |
174 |
|
unsigned d; |
175 |
|
|
176 |
|
if (inet_aton(dotted_quad, &ip) == 0) |
177 |
|
return -1; /* malformed dotted IP */ |
178 |
|
d = ntohl(ip.s_addr); /* IP in host order */ |
179 |
|
d = ~d; /* 11110000 -> 00001111 */ |
180 |
|
if (d & (d+1)) /* check that it is in 00001111 form */ |
181 |
|
return -1; /* no it is not */ |
182 |
|
result = 32; |
183 |
|
while (d) { |
184 |
|
d >>= 1; |
185 |
|
result--; |
186 |
|
} |
187 |
|
return result; |
188 |
|
} |
189 |
|
#endif |
190 |
|
|
191 |
static char *parse(const char *command, struct interface_defn_t *ifd) |
static char *parse(const char *command, struct interface_defn_t *ifd) |
192 |
{ |
{ |
193 |
size_t old_pos[MAX_OPT_DEPTH] = { 0 }; |
size_t old_pos[MAX_OPT_DEPTH] = { 0 }; |
249 |
varvalue = get_var(command, nextpercent - command, ifd); |
varvalue = get_var(command, nextpercent - command, ifd); |
250 |
|
|
251 |
if (varvalue) { |
if (varvalue) { |
252 |
|
#if ENABLE_FEATURE_IFUPDOWN_IP |
253 |
|
/* "hwaddress <class> <address>": |
254 |
|
* unlike ifconfig, ip doesnt want <class> |
255 |
|
* (usually "ether" keyword). Skip it. */ |
256 |
|
if (strncmp(command, "hwaddress", 9) == 0) { |
257 |
|
varvalue = skip_whitespace(skip_non_whitespace(varvalue)); |
258 |
|
} |
259 |
|
#endif |
260 |
addstr(&result, varvalue, strlen(varvalue)); |
addstr(&result, varvalue, strlen(varvalue)); |
261 |
} else { |
} else { |
262 |
#if ENABLE_FEATURE_IFUPDOWN_IP |
#if ENABLE_FEATURE_IFUPDOWN_IP |
265 |
if (strncmp(command, "bnmask", 6) == 0) { |
if (strncmp(command, "bnmask", 6) == 0) { |
266 |
unsigned res; |
unsigned res; |
267 |
varvalue = get_var("netmask", 7, ifd); |
varvalue = get_var("netmask", 7, ifd); |
268 |
if (varvalue && (res = count_netmask_bits(varvalue)) > 0) { |
if (varvalue) { |
269 |
const char *argument = utoa(res); |
res = count_netmask_bits(varvalue); |
270 |
addstr(&result, argument, strlen(argument)); |
if (res > 0) { |
271 |
command = nextpercent + 1; |
const char *argument = utoa(res); |
272 |
break; |
addstr(&result, argument, strlen(argument)); |
273 |
|
command = nextpercent + 1; |
274 |
|
break; |
275 |
|
} |
276 |
} |
} |
277 |
} |
} |
278 |
#endif |
#endif |
349 |
int result; |
int result; |
350 |
#if ENABLE_FEATURE_IFUPDOWN_IP |
#if ENABLE_FEATURE_IFUPDOWN_IP |
351 |
result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec); |
result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec); |
352 |
result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec); |
result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); |
353 |
/* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */ |
/* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */ |
354 |
result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); |
result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec); |
355 |
#else |
#else |
397 |
|
|
398 |
static const struct address_family_t addr_inet6 = { |
static const struct address_family_t addr_inet6 = { |
399 |
"inet6", |
"inet6", |
400 |
sizeof(methods6) / sizeof(struct method_t), |
ARRAY_SIZE(methods6), |
401 |
methods6 |
methods6 |
402 |
}; |
}; |
403 |
#endif /* FEATURE_IFUPDOWN_IPV6 */ |
#endif /* FEATURE_IFUPDOWN_IPV6 */ |
433 |
#if ENABLE_FEATURE_IFUPDOWN_IP |
#if ENABLE_FEATURE_IFUPDOWN_IP |
434 |
result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] " |
result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] " |
435 |
"dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec); |
"dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec); |
436 |
result += execute("ip link set[[ mtu %mtu%]][[ address %hwaddress%]] %iface% up", ifd, exec); |
result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec); |
437 |
result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec); |
result += execute("[[ip route add default via %gateway% dev %iface%]]", ifd, exec); |
438 |
return ((result == 3) ? 3 : 0); |
return ((result == 3) ? 3 : 0); |
439 |
#else |
#else |
444 |
result += execute("ifconfig %iface% %address% netmask %netmask%" |
result += execute("ifconfig %iface% %address% netmask %netmask%" |
445 |
"[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ", |
"[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ", |
446 |
ifd, exec); |
ifd, exec); |
447 |
result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec); |
result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec); |
448 |
return ((result == 3) ? 3 : 0); |
return ((result == 3) ? 3 : 0); |
449 |
#endif |
#endif |
450 |
} |
} |
456 |
result = execute("ip addr flush dev %iface%", ifd, exec); |
result = execute("ip addr flush dev %iface%", ifd, exec); |
457 |
result += execute("ip link set %iface% down", ifd, exec); |
result += execute("ip link set %iface% down", ifd, exec); |
458 |
#else |
#else |
459 |
result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); |
/* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */ |
460 |
|
/* Bringing the interface down deletes the routes in itself. |
461 |
|
Otherwise this fails if we reference 'gateway' when using this from dhcp_down */ |
462 |
|
result = 1; |
463 |
result += execute("ifconfig %iface% down", ifd, exec); |
result += execute("ifconfig %iface% down", ifd, exec); |
464 |
#endif |
#endif |
465 |
return ((result == 2) ? 2 : 0); |
return ((result == 2) ? 2 : 0); |
466 |
} |
} |
467 |
|
|
468 |
#if !ENABLE_APP_UDHCPC |
#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP |
469 |
struct dhcp_client_t |
struct dhcp_client_t |
470 |
{ |
{ |
471 |
const char *name; |
const char *name; |
474 |
}; |
}; |
475 |
|
|
476 |
static const struct dhcp_client_t ext_dhcp_clients[] = { |
static const struct dhcp_client_t ext_dhcp_clients[] = { |
477 |
{ "udhcpc", |
{ "dhcpcd", |
478 |
"udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]", |
"dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%", |
479 |
"kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", |
"dhcpcd -k %iface%", |
|
}, |
|
|
{ "pump", |
|
|
"pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]", |
|
|
"pump -i %iface% -k", |
|
480 |
}, |
}, |
481 |
{ "dhclient", |
{ "dhclient", |
482 |
"dhclient -pf /var/run/dhclient.%iface%.pid %iface%", |
"dhclient -pf /var/run/dhclient.%iface%.pid %iface%", |
483 |
"kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", |
"kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", |
484 |
}, |
}, |
485 |
{ "dhcpcd", |
{ "pump", |
486 |
"dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %clientid%]][[ -l %leasetime%]] %iface%", |
"pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]", |
487 |
"dhcpcd -k %iface%", |
"pump -i %iface% -k", |
488 |
|
}, |
489 |
|
{ "udhcpc", |
490 |
|
"udhcpc -R -n -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %clientid%]]" |
491 |
|
"[[ -s %script%]][[ %udhcpc_opts%]]", |
492 |
|
"kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", |
493 |
}, |
}, |
494 |
}; |
}; |
495 |
#endif |
#endif /* ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCPC */ |
496 |
|
|
497 |
|
#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP |
498 |
static int dhcp_up(struct interface_defn_t *ifd, execfn *exec) |
static int dhcp_up(struct interface_defn_t *ifd, execfn *exec) |
499 |
{ |
{ |
500 |
#if ENABLE_APP_UDHCPC |
unsigned i; |
501 |
return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid " |
#if ENABLE_FEATURE_IFUPDOWN_IP |
502 |
"-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]]", |
/* ip doesn't up iface when it configures it (unlike ifconfig) */ |
503 |
ifd, exec); |
if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec)) |
504 |
|
return 0; |
505 |
#else |
#else |
506 |
int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]); |
/* needed if we have hwaddress on dhcp iface */ |
507 |
for (i = 0; i < nclients; i++) { |
if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec)) |
508 |
|
return 0; |
509 |
|
#endif |
510 |
|
for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { |
511 |
if (exists_execable(ext_dhcp_clients[i].name)) |
if (exists_execable(ext_dhcp_clients[i].name)) |
512 |
return execute(ext_dhcp_clients[i].startcmd, ifd, exec); |
return execute(ext_dhcp_clients[i].startcmd, ifd, exec); |
513 |
} |
} |
514 |
bb_error_msg("no dhcp clients found"); |
bb_error_msg("no dhcp clients found"); |
515 |
return 0; |
return 0; |
516 |
|
} |
517 |
|
#elif ENABLE_APP_UDHCPC |
518 |
|
static int dhcp_up(struct interface_defn_t *ifd, execfn *exec) |
519 |
|
{ |
520 |
|
#if ENABLE_FEATURE_IFUPDOWN_IP |
521 |
|
/* ip doesn't up iface when it configures it (unlike ifconfig) */ |
522 |
|
if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd, exec)) |
523 |
|
return 0; |
524 |
|
#else |
525 |
|
/* needed if we have hwaddress on dhcp iface */ |
526 |
|
if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec)) |
527 |
|
return 0; |
528 |
#endif |
#endif |
529 |
|
return execute("udhcpc -R -n -p /var/run/udhcpc.%iface%.pid " |
530 |
|
"-i %iface%[[ -H %hostname%]][[ -c %clientid%]][[ -s %script%]][[ %udhcpc_opts%]]", |
531 |
|
ifd, exec); |
532 |
|
} |
533 |
|
#else |
534 |
|
static int dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM, |
535 |
|
execfn *exec UNUSED_PARAM) |
536 |
|
{ |
537 |
|
return 0; /* no dhcp support */ |
538 |
} |
} |
539 |
|
#endif |
540 |
|
|
541 |
|
#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP |
542 |
|
static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) |
543 |
|
{ |
544 |
|
int result = 0; |
545 |
|
unsigned i; |
546 |
|
|
547 |
|
for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) { |
548 |
|
if (exists_execable(ext_dhcp_clients[i].name)) { |
549 |
|
result += execute(ext_dhcp_clients[i].stopcmd, ifd, exec); |
550 |
|
if (result) |
551 |
|
break; |
552 |
|
} |
553 |
|
} |
554 |
|
|
555 |
|
if (!result) |
556 |
|
bb_error_msg("warning: no dhcp clients found and stopped"); |
557 |
|
|
558 |
|
/* Sleep a bit, otherwise static_down tries to bring down interface too soon, |
559 |
|
and it may come back up because udhcpc is still shutting down */ |
560 |
|
usleep(100000); |
561 |
|
result += static_down(ifd, exec); |
562 |
|
return ((result == 3) ? 3 : 0); |
563 |
|
} |
564 |
|
#elif ENABLE_APP_UDHCPC |
565 |
static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) |
static int dhcp_down(struct interface_defn_t *ifd, execfn *exec) |
566 |
{ |
{ |
567 |
#if ENABLE_APP_UDHCPC |
int result; |
568 |
return execute("kill -TERM " |
result = execute("kill " |
569 |
"`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); |
"`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec); |
570 |
|
/* Also bring the hardware interface down since |
571 |
|
killing the dhcp client alone doesn't do it. |
572 |
|
This enables consecutive ifup->ifdown->ifup */ |
573 |
|
/* Sleep a bit, otherwise static_down tries to bring down interface too soon, |
574 |
|
and it may come back up because udhcpc is still shutting down */ |
575 |
|
usleep(100000); |
576 |
|
result += static_down(ifd, exec); |
577 |
|
return ((result == 3) ? 3 : 0); |
578 |
|
} |
579 |
#else |
#else |
580 |
int i, nclients = sizeof(ext_dhcp_clients) / sizeof(ext_dhcp_clients[0]); |
static int dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM, |
581 |
for (i = 0; i < nclients; i++) { |
execfn *exec UNUSED_PARAM) |
582 |
if (exists_execable(ext_dhcp_clients[i].name)) |
{ |
583 |
return execute(ext_dhcp_clients[i].stopcmd, ifd, exec); |
return 0; /* no dhcp support */ |
|
} |
|
|
bb_error_msg("no dhcp clients found, using static interface shutdown"); |
|
|
return static_down(ifd, exec); |
|
|
#endif |
|
584 |
} |
} |
585 |
|
#endif |
586 |
|
|
587 |
static int manual_up_down(struct interface_defn_t *ifd, execfn *exec) |
static int manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM) |
588 |
{ |
{ |
589 |
return 1; |
return 1; |
590 |
} |
} |
592 |
static int bootp_up(struct interface_defn_t *ifd, execfn *exec) |
static int bootp_up(struct interface_defn_t *ifd, execfn *exec) |
593 |
{ |
{ |
594 |
return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%" |
return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%" |
595 |
"[[ --server %server%]][[ --hwaddr %hwaddr%]] " |
"[[ --server %server%]][[ --hwaddr %hwaddr%]]" |
596 |
"--returniffail --serverbcast", ifd, exec); |
" --returniffail --serverbcast", ifd, exec); |
597 |
} |
} |
598 |
|
|
599 |
static int ppp_up(struct interface_defn_t *ifd, execfn *exec) |
static int ppp_up(struct interface_defn_t *ifd, execfn *exec) |
630 |
|
|
631 |
static const struct address_family_t addr_inet = { |
static const struct address_family_t addr_inet = { |
632 |
"inet", |
"inet", |
633 |
sizeof(methods) / sizeof(struct method_t), |
ARRAY_SIZE(methods), |
634 |
methods |
methods |
635 |
}; |
}; |
636 |
|
|
638 |
|
|
639 |
static char *next_word(char **buf) |
static char *next_word(char **buf) |
640 |
{ |
{ |
641 |
unsigned short length; |
unsigned length; |
642 |
char *word; |
char *word; |
643 |
|
|
|
if (!buf || !*buf || !**buf) { |
|
|
return NULL; |
|
|
} |
|
|
|
|
644 |
/* Skip over leading whitespace */ |
/* Skip over leading whitespace */ |
645 |
word = skip_whitespace(*buf); |
word = skip_whitespace(*buf); |
646 |
|
|
647 |
/* Skip over comments */ |
/* Stop on EOL */ |
648 |
if (*word == '#') { |
if (*word == '\0') |
649 |
return NULL; |
return NULL; |
|
} |
|
650 |
|
|
651 |
/* Find the length of this word */ |
/* Find the length of this word (can't be 0) */ |
652 |
length = strcspn(word, " \t\n"); |
length = strcspn(word, " \t\n"); |
653 |
if (length == 0) { |
|
654 |
return NULL; |
/* Unless we are already at NUL, store NUL and advance */ |
655 |
} |
if (word[length] != '\0') |
656 |
|
word[length++] = '\0'; |
657 |
|
|
658 |
*buf = word + length; |
*buf = word + length; |
|
/*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */ |
|
|
if (**buf) { |
|
|
**buf = '\0'; |
|
|
(*buf)++; |
|
|
} |
|
659 |
|
|
660 |
return word; |
return word; |
661 |
} |
} |
681 |
|
|
682 |
if (!name) |
if (!name) |
683 |
return NULL; |
return NULL; |
684 |
|
/* TODO: use index_in_str_array() */ |
685 |
for (i = 0; i < af->n_methods; i++) { |
for (i = 0; i < af->n_methods; i++) { |
686 |
if (strcmp(af->method[i].name, name) == 0) { |
if (strcmp(af->method[i].name, name) == 0) { |
687 |
return &af->method[i]; |
return &af->method[i]; |
706 |
|
|
707 |
static struct interfaces_file_t *read_interfaces(const char *filename) |
static struct interfaces_file_t *read_interfaces(const char *filename) |
708 |
{ |
{ |
709 |
|
/* Let's try to be compatible. |
710 |
|
* |
711 |
|
* "man 5 interfaces" says: |
712 |
|
* Lines starting with "#" are ignored. Note that end-of-line |
713 |
|
* comments are NOT supported, comments must be on a line of their own. |
714 |
|
* A line may be extended across multiple lines by making |
715 |
|
* the last character a backslash. |
716 |
|
* |
717 |
|
* Seen elsewhere in example config file: |
718 |
|
* A first non-blank "#" character makes the rest of the line |
719 |
|
* be ignored. Blank lines are ignored. Lines may be indented freely. |
720 |
|
* A "\" character at the very end of the line indicates the next line |
721 |
|
* should be treated as a continuation of the current one. |
722 |
|
*/ |
723 |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
724 |
struct mapping_defn_t *currmap = NULL; |
struct mapping_defn_t *currmap = NULL; |
725 |
#endif |
#endif |
726 |
struct interface_defn_t *currif = NULL; |
struct interface_defn_t *currif = NULL; |
727 |
struct interfaces_file_t *defn; |
struct interfaces_file_t *defn; |
728 |
FILE *f; |
FILE *f; |
|
char *firstword; |
|
729 |
char *buf; |
char *buf; |
730 |
|
char *first_word; |
731 |
|
char *rest_of_line; |
732 |
enum { NONE, IFACE, MAPPING } currently_processing = NONE; |
enum { NONE, IFACE, MAPPING } currently_processing = NONE; |
733 |
|
|
734 |
defn = xzalloc(sizeof(struct interfaces_file_t)); |
defn = xzalloc(sizeof(*defn)); |
735 |
|
f = xfopen_for_read(filename); |
|
f = xfopen(filename, "r"); |
|
736 |
|
|
737 |
while ((buf = xmalloc_getline(f)) != NULL) { |
while ((buf = xmalloc_fgetline(f)) != NULL) { |
738 |
char *buf_ptr = buf; |
#if ENABLE_DESKTOP |
739 |
|
/* Trailing "\" concatenates lines */ |
740 |
firstword = next_word(&buf_ptr); |
char *p; |
741 |
if (firstword == NULL) { |
while ((p = last_char_is(buf, '\\')) != NULL) { |
742 |
|
*p = '\0'; |
743 |
|
rest_of_line = xmalloc_fgetline(f); |
744 |
|
if (!rest_of_line) |
745 |
|
break; |
746 |
|
p = xasprintf("%s%s", buf, rest_of_line); |
747 |
free(buf); |
free(buf); |
748 |
continue; /* blank line */ |
free(rest_of_line); |
749 |
|
buf = p; |
750 |
|
} |
751 |
|
#endif |
752 |
|
rest_of_line = buf; |
753 |
|
first_word = next_word(&rest_of_line); |
754 |
|
if (!first_word || *first_word == '#') { |
755 |
|
free(buf); |
756 |
|
continue; /* blank/comment line */ |
757 |
} |
} |
758 |
|
|
759 |
if (strcmp(firstword, "mapping") == 0) { |
if (strcmp(first_word, "mapping") == 0) { |
760 |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
761 |
currmap = xzalloc(sizeof(struct mapping_defn_t)); |
currmap = xzalloc(sizeof(*currmap)); |
|
|
|
|
while ((firstword = next_word(&buf_ptr)) != NULL) { |
|
|
if (currmap->max_matches == currmap->n_matches) { |
|
|
currmap->max_matches = currmap->max_matches * 2 + 1; |
|
|
currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches); |
|
|
} |
|
762 |
|
|
763 |
currmap->match[currmap->n_matches++] = xstrdup(firstword); |
while ((first_word = next_word(&rest_of_line)) != NULL) { |
764 |
|
currmap->match = xrealloc_vector(currmap->match, 4, currmap->n_matches); |
765 |
|
currmap->match[currmap->n_matches++] = xstrdup(first_word); |
766 |
} |
} |
767 |
currmap->max_mappings = 0; |
/*currmap->max_mappings = 0; - done by xzalloc */ |
768 |
currmap->n_mappings = 0; |
/*currmap->n_mappings = 0;*/ |
769 |
currmap->mapping = NULL; |
/*currmap->mapping = NULL;*/ |
770 |
currmap->script = NULL; |
/*currmap->script = NULL;*/ |
771 |
{ |
{ |
772 |
struct mapping_defn_t **where = &defn->mappings; |
struct mapping_defn_t **where = &defn->mappings; |
773 |
while (*where != NULL) { |
while (*where != NULL) { |
774 |
where = &(*where)->next; |
where = &(*where)->next; |
775 |
} |
} |
776 |
*where = currmap; |
*where = currmap; |
777 |
currmap->next = NULL; |
/*currmap->next = NULL;*/ |
778 |
} |
} |
779 |
debug_noise("Added mapping\n"); |
debug_noise("Added mapping\n"); |
780 |
#endif |
#endif |
781 |
currently_processing = MAPPING; |
currently_processing = MAPPING; |
782 |
} else if (strcmp(firstword, "iface") == 0) { |
} else if (strcmp(first_word, "iface") == 0) { |
783 |
static const struct address_family_t *const addr_fams[] = { |
static const struct address_family_t *const addr_fams[] = { |
784 |
#if ENABLE_FEATURE_IFUPDOWN_IPV4 |
#if ENABLE_FEATURE_IFUPDOWN_IPV4 |
785 |
&addr_inet, |
&addr_inet, |
789 |
#endif |
#endif |
790 |
NULL |
NULL |
791 |
}; |
}; |
|
|
|
792 |
char *iface_name; |
char *iface_name; |
793 |
char *address_family_name; |
char *address_family_name; |
794 |
char *method_name; |
char *method_name; |
795 |
llist_t *iface_list; |
llist_t *iface_list; |
796 |
|
|
797 |
currif = xzalloc(sizeof(struct interface_defn_t)); |
currif = xzalloc(sizeof(*currif)); |
798 |
iface_name = next_word(&buf_ptr); |
iface_name = next_word(&rest_of_line); |
799 |
address_family_name = next_word(&buf_ptr); |
address_family_name = next_word(&rest_of_line); |
800 |
method_name = next_word(&buf_ptr); |
method_name = next_word(&rest_of_line); |
801 |
|
|
802 |
if (buf_ptr == NULL) { |
if (method_name == NULL) |
803 |
bb_error_msg("too few parameters for line \"%s\"", buf); |
bb_error_msg_and_die("too few parameters for line \"%s\"", buf); |
|
return NULL; |
|
|
} |
|
804 |
|
|
805 |
/* ship any trailing whitespace */ |
/* ship any trailing whitespace */ |
806 |
buf_ptr = skip_whitespace(buf_ptr); |
rest_of_line = skip_whitespace(rest_of_line); |
807 |
|
|
808 |
if (buf_ptr[0] != '\0') { |
if (rest_of_line[0] != '\0' /* && rest_of_line[0] != '#' */) |
809 |
bb_error_msg("too many parameters \"%s\"", buf); |
bb_error_msg_and_die("too many parameters \"%s\"", buf); |
|
return NULL; |
|
|
} |
|
810 |
|
|
811 |
currif->iface = xstrdup(iface_name); |
currif->iface = xstrdup(iface_name); |
812 |
|
|
813 |
currif->address_family = get_address_family(addr_fams, address_family_name); |
currif->address_family = get_address_family(addr_fams, address_family_name); |
814 |
if (!currif->address_family) { |
if (!currif->address_family) |
815 |
bb_error_msg("unknown address type \"%s\"", address_family_name); |
bb_error_msg_and_die("unknown address type \"%s\"", address_family_name); |
|
return NULL; |
|
|
} |
|
816 |
|
|
817 |
currif->method = get_method(currif->address_family, method_name); |
currif->method = get_method(currif->address_family, method_name); |
818 |
if (!currif->method) { |
if (!currif->method) |
819 |
bb_error_msg("unknown method \"%s\"", method_name); |
bb_error_msg_and_die("unknown method \"%s\"", method_name); |
|
return NULL; |
|
|
} |
|
820 |
|
|
821 |
for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) { |
for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) { |
822 |
struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data; |
struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data; |
823 |
if ((strcmp(tmp->iface, currif->iface) == 0) && |
if ((strcmp(tmp->iface, currif->iface) == 0) |
824 |
(tmp->address_family == currif->address_family)) { |
&& (tmp->address_family == currif->address_family) |
825 |
bb_error_msg("duplicate interface \"%s\"", tmp->iface); |
) { |
826 |
return NULL; |
bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface); |
827 |
} |
} |
828 |
} |
} |
829 |
llist_add_to_end(&(defn->ifaces), (char*)currif); |
llist_add_to_end(&(defn->ifaces), (char*)currif); |
830 |
|
|
831 |
debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name); |
debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name); |
832 |
currently_processing = IFACE; |
currently_processing = IFACE; |
833 |
} else if (strcmp(firstword, "auto") == 0) { |
} else if (strcmp(first_word, "auto") == 0) { |
834 |
while ((firstword = next_word(&buf_ptr)) != NULL) { |
while ((first_word = next_word(&rest_of_line)) != NULL) { |
835 |
|
|
836 |
/* Check the interface isnt already listed */ |
/* Check the interface isnt already listed */ |
837 |
if (find_list_string(defn->autointerfaces, firstword)) { |
if (find_list_string(defn->autointerfaces, first_word)) { |
838 |
bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf); |
bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf); |
839 |
} |
} |
840 |
|
|
841 |
/* Add the interface to the list */ |
/* Add the interface to the list */ |
842 |
llist_add_to_end(&(defn->autointerfaces), xstrdup(firstword)); |
llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word)); |
843 |
debug_noise("\nauto %s\n", firstword); |
debug_noise("\nauto %s\n", first_word); |
844 |
} |
} |
845 |
currently_processing = NONE; |
currently_processing = NONE; |
846 |
} else { |
} else { |
847 |
switch (currently_processing) { |
switch (currently_processing) { |
848 |
case IFACE: |
case IFACE: |
849 |
{ |
if (rest_of_line[0] == '\0') |
850 |
int i; |
bb_error_msg_and_die("option with empty value \"%s\"", buf); |
|
|
|
|
if (strlen(buf_ptr) == 0) { |
|
|
bb_error_msg("option with empty value \"%s\"", buf); |
|
|
return NULL; |
|
|
} |
|
851 |
|
|
852 |
if (strcmp(firstword, "up") != 0 |
if (strcmp(first_word, "up") != 0 |
853 |
&& strcmp(firstword, "down") != 0 |
&& strcmp(first_word, "down") != 0 |
854 |
&& strcmp(firstword, "pre-up") != 0 |
&& strcmp(first_word, "pre-up") != 0 |
855 |
&& strcmp(firstword, "post-down") != 0) { |
&& strcmp(first_word, "post-down") != 0 |
856 |
for (i = 0; i < currif->n_options; i++) { |
) { |
857 |
if (strcmp(currif->option[i].name, firstword) == 0) { |
int i; |
858 |
bb_error_msg("duplicate option \"%s\"", buf); |
for (i = 0; i < currif->n_options; i++) { |
859 |
return NULL; |
if (strcmp(currif->option[i].name, first_word) == 0) |
860 |
} |
bb_error_msg_and_die("duplicate option \"%s\"", buf); |
|
} |
|
861 |
} |
} |
862 |
} |
} |
863 |
if (currif->n_options >= currif->max_options) { |
if (currif->n_options >= currif->max_options) { |
864 |
struct variable_t *opt; |
currif->max_options += 10; |
865 |
|
currif->option = xrealloc(currif->option, |
866 |
currif->max_options = currif->max_options + 10; |
sizeof(*currif->option) * currif->max_options); |
|
opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options); |
|
|
currif->option = opt; |
|
|
} |
|
|
currif->option[currif->n_options].name = xstrdup(firstword); |
|
|
currif->option[currif->n_options].value = xstrdup(buf_ptr); |
|
|
if (!currif->option[currif->n_options].name) { |
|
|
perror(filename); |
|
|
return NULL; |
|
|
} |
|
|
if (!currif->option[currif->n_options].value) { |
|
|
perror(filename); |
|
|
return NULL; |
|
867 |
} |
} |
868 |
debug_noise("\t%s=%s\n", currif->option[currif->n_options].name, |
debug_noise("\t%s=%s\n", first_word, rest_of_line); |
869 |
currif->option[currif->n_options].value); |
currif->option[currif->n_options].name = xstrdup(first_word); |
870 |
|
currif->option[currif->n_options].value = xstrdup(rest_of_line); |
871 |
currif->n_options++; |
currif->n_options++; |
872 |
break; |
break; |
873 |
case MAPPING: |
case MAPPING: |
874 |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
875 |
if (strcmp(firstword, "script") == 0) { |
if (strcmp(first_word, "script") == 0) { |
876 |
if (currmap->script != NULL) { |
if (currmap->script != NULL) |
877 |
bb_error_msg("duplicate script in mapping \"%s\"", buf); |
bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf); |
878 |
return NULL; |
currmap->script = xstrdup(next_word(&rest_of_line)); |
879 |
} else { |
} else if (strcmp(first_word, "map") == 0) { |
880 |
currmap->script = xstrdup(next_word(&buf_ptr)); |
if (currmap->n_mappings >= currmap->max_mappings) { |
|
} |
|
|
} else if (strcmp(firstword, "map") == 0) { |
|
|
if (currmap->max_mappings == currmap->n_mappings) { |
|
881 |
currmap->max_mappings = currmap->max_mappings * 2 + 1; |
currmap->max_mappings = currmap->max_mappings * 2 + 1; |
882 |
currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings); |
currmap->mapping = xrealloc(currmap->mapping, |
883 |
|
sizeof(char *) * currmap->max_mappings); |
884 |
} |
} |
885 |
currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&buf_ptr)); |
currmap->mapping[currmap->n_mappings] = xstrdup(next_word(&rest_of_line)); |
886 |
currmap->n_mappings++; |
currmap->n_mappings++; |
887 |
} else { |
} else { |
888 |
bb_error_msg("misplaced option \"%s\"", buf); |
bb_error_msg_and_die("misplaced option \"%s\"", buf); |
|
return NULL; |
|
889 |
} |
} |
890 |
#endif |
#endif |
891 |
break; |
break; |
892 |
case NONE: |
case NONE: |
893 |
default: |
default: |
894 |
bb_error_msg("misplaced option \"%s\"", buf); |
bb_error_msg_and_die("misplaced option \"%s\"", buf); |
|
return NULL; |
|
895 |
} |
} |
896 |
} |
} |
897 |
free(buf); |
free(buf); |
898 |
} |
} /* while (fgets) */ |
899 |
|
|
900 |
if (ferror(f) != 0) { |
if (ferror(f) != 0) { |
901 |
bb_perror_msg_and_die("%s", filename); |
/* ferror does NOT set errno! */ |
902 |
|
bb_error_msg_and_die("%s: I/O error", filename); |
903 |
} |
} |
904 |
fclose(f); |
fclose(f); |
905 |
|
|
906 |
return defn; |
return defn; |
907 |
} |
} |
908 |
|
|
909 |
static char *setlocalenv(char *format, const char *name, const char *value) |
static char *setlocalenv(const char *format, const char *name, const char *value) |
910 |
{ |
{ |
911 |
char *result; |
char *result; |
912 |
char *here; |
char *here; |
949 |
|
|
950 |
for (i = 0; i < iface->n_options; i++) { |
for (i = 0; i < iface->n_options; i++) { |
951 |
if (strcmp(iface->option[i].name, "up") == 0 |
if (strcmp(iface->option[i].name, "up") == 0 |
952 |
|| strcmp(iface->option[i].name, "down") == 0 |
|| strcmp(iface->option[i].name, "down") == 0 |
953 |
|| strcmp(iface->option[i].name, "pre-up") == 0 |
|| strcmp(iface->option[i].name, "pre-up") == 0 |
954 |
|| strcmp(iface->option[i].name, "post-down") == 0) { |
|| strcmp(iface->option[i].name, "post-down") == 0 |
955 |
|
) { |
956 |
continue; |
continue; |
957 |
} |
} |
958 |
*(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); |
*(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value); |
975 |
int status; |
int status; |
976 |
|
|
977 |
fflush(NULL); |
fflush(NULL); |
978 |
child = fork(); |
child = vfork(); |
979 |
switch (child) { |
switch (child) { |
980 |
case -1: /* failure */ |
case -1: /* failure */ |
981 |
return 0; |
return 0; |
982 |
case 0: /* child */ |
case 0: /* child */ |
983 |
execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, my_environ); |
execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, my_environ); |
984 |
exit(127); |
_exit(127); |
985 |
} |
} |
986 |
waitpid(child, &status, 0); |
safe_waitpid(child, &status, 0); |
987 |
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
988 |
return 0; |
return 0; |
989 |
} |
} |
1034 |
} |
} |
1035 |
|
|
1036 |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
1037 |
static int popen2(FILE **in, FILE **out, char *command, ...) |
static int popen2(FILE **in, FILE **out, char *command, char *param) |
1038 |
{ |
{ |
1039 |
va_list ap; |
char *argv[3] = { command, param, NULL }; |
1040 |
char *argv[11] = { command }; |
struct fd_pair infd, outfd; |
|
int argc; |
|
|
int infd[2], outfd[2]; |
|
1041 |
pid_t pid; |
pid_t pid; |
1042 |
|
|
1043 |
argc = 1; |
xpiped_pair(infd); |
1044 |
va_start(ap, command); |
xpiped_pair(outfd); |
|
while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) { |
|
|
argc++; |
|
|
} |
|
|
argv[argc] = NULL; /* make sure */ |
|
|
va_end(ap); |
|
|
|
|
|
if (pipe(infd) != 0) { |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (pipe(outfd) != 0) { |
|
|
close(infd[0]); |
|
|
close(infd[1]); |
|
|
return 0; |
|
|
} |
|
1045 |
|
|
1046 |
fflush(NULL); |
fflush(NULL); |
1047 |
switch (pid = fork()) { |
pid = vfork(); |
1048 |
case -1: /* failure */ |
|
1049 |
close(infd[0]); |
switch (pid) { |
1050 |
close(infd[1]); |
case -1: /* failure */ |
1051 |
close(outfd[0]); |
bb_perror_msg_and_die("vfork"); |
1052 |
close(outfd[1]); |
case 0: /* child */ |
1053 |
return 0; |
/* NB: close _first_, then move fds! */ |
1054 |
case 0: /* child */ |
close(infd.wr); |
1055 |
dup2(infd[0], 0); |
close(outfd.rd); |
1056 |
dup2(outfd[1], 1); |
xmove_fd(infd.rd, 0); |
1057 |
close(infd[0]); |
xmove_fd(outfd.wr, 1); |
1058 |
close(infd[1]); |
BB_EXECVP(command, argv); |
1059 |
close(outfd[0]); |
_exit(127); |
1060 |
close(outfd[1]); |
} |
1061 |
execvp(command, argv); |
/* parent */ |
1062 |
exit(127); |
close(infd.rd); |
1063 |
default: /* parent */ |
close(outfd.wr); |
1064 |
*in = fdopen(infd[1], "w"); |
*in = fdopen(infd.wr, "w"); |
1065 |
*out = fdopen(outfd[0], "r"); |
*out = fdopen(outfd.rd, "r"); |
1066 |
close(infd[0]); |
return pid; |
|
close(outfd[1]); |
|
|
return pid; |
|
|
} |
|
|
/* unreached */ |
|
1067 |
} |
} |
1068 |
|
|
1069 |
static char *run_mapping(char *physical, struct mapping_defn_t * map) |
static char *run_mapping(char *physical, struct mapping_defn_t *map) |
1070 |
{ |
{ |
1071 |
FILE *in, *out; |
FILE *in, *out; |
1072 |
int i, status; |
int i, status; |
1074 |
|
|
1075 |
char *logical = xstrdup(physical); |
char *logical = xstrdup(physical); |
1076 |
|
|
1077 |
/* Run the mapping script. */ |
/* Run the mapping script. Never fails. */ |
1078 |
pid = popen2(&in, &out, map->script, physical, NULL); |
pid = popen2(&in, &out, map->script, physical); |
|
|
|
|
/* popen2() returns 0 on failure. */ |
|
|
if (pid == 0) |
|
|
return logical; |
|
1079 |
|
|
1080 |
/* Write mappings to stdin of mapping script. */ |
/* Write mappings to stdin of mapping script. */ |
1081 |
for (i = 0; i < map->n_mappings; i++) { |
for (i = 0; i < map->n_mappings; i++) { |
1082 |
fprintf(in, "%s\n", map->mapping[i]); |
fprintf(in, "%s\n", map->mapping[i]); |
1083 |
} |
} |
1084 |
fclose(in); |
fclose(in); |
1085 |
waitpid(pid, &status, 0); |
safe_waitpid(pid, &status, 0); |
1086 |
|
|
1087 |
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { |
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { |
1088 |
/* If the mapping script exited successfully, try to |
/* If the mapping script exited successfully, try to |
1089 |
* grab a line of output and use that as the name of the |
* grab a line of output and use that as the name of the |
1090 |
* logical interface. */ |
* logical interface. */ |
1091 |
char *new_logical = xmalloc(MAX_INTERFACE_LENGTH); |
char *new_logical = xmalloc_fgetline(out); |
1092 |
|
|
1093 |
if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) { |
if (new_logical) { |
1094 |
/* If we are able to read a line of output from the script, |
/* If we are able to read a line of output from the script, |
1095 |
* remove any trailing whitespace and use this value |
* remove any trailing whitespace and use this value |
1096 |
* as the name of the logical interface. */ |
* as the name of the logical interface. */ |
1101 |
|
|
1102 |
free(logical); |
free(logical); |
1103 |
logical = new_logical; |
logical = new_logical; |
|
} else { |
|
|
/* If we are UNABLE to read a line of output, discard our |
|
|
* freshly allocated memory. */ |
|
|
free(new_logical); |
|
1104 |
} |
} |
1105 |
} |
} |
1106 |
|
|
1112 |
|
|
1113 |
static llist_t *find_iface_state(llist_t *state_list, const char *iface) |
static llist_t *find_iface_state(llist_t *state_list, const char *iface) |
1114 |
{ |
{ |
1115 |
unsigned short iface_len = strlen(iface); |
unsigned iface_len = strlen(iface); |
1116 |
llist_t *search = state_list; |
llist_t *search = state_list; |
1117 |
|
|
1118 |
while (search) { |
while (search) { |
1119 |
if ((strncmp(search->data, iface, iface_len) == 0) && |
if ((strncmp(search->data, iface, iface_len) == 0) |
1120 |
(search->data[iface_len] == '=')) { |
&& (search->data[iface_len] == '=') |
1121 |
|
) { |
1122 |
return search; |
return search; |
1123 |
} |
} |
1124 |
search = search->link; |
search = search->link; |
1126 |
return NULL; |
return NULL; |
1127 |
} |
} |
1128 |
|
|
1129 |
|
/* read the previous state from the state file */ |
1130 |
|
static llist_t *read_iface_state(void) |
1131 |
|
{ |
1132 |
|
llist_t *state_list = NULL; |
1133 |
|
FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH); |
1134 |
|
|
1135 |
|
if (state_fp) { |
1136 |
|
char *start, *end_ptr; |
1137 |
|
while ((start = xmalloc_fgets(state_fp)) != NULL) { |
1138 |
|
/* We should only need to check for a single character */ |
1139 |
|
end_ptr = start + strcspn(start, " \t\n"); |
1140 |
|
*end_ptr = '\0'; |
1141 |
|
llist_add_to(&state_list, start); |
1142 |
|
} |
1143 |
|
fclose(state_fp); |
1144 |
|
} |
1145 |
|
return state_list; |
1146 |
|
} |
1147 |
|
|
1148 |
|
|
1149 |
|
int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1150 |
int ifupdown_main(int argc, char **argv) |
int ifupdown_main(int argc, char **argv) |
1151 |
{ |
{ |
1152 |
int (*cmds)(struct interface_defn_t *) = NULL; |
int (*cmds)(struct interface_defn_t *); |
1153 |
struct interfaces_file_t *defn; |
struct interfaces_file_t *defn; |
|
llist_t *state_list = NULL; |
|
1154 |
llist_t *target_list = NULL; |
llist_t *target_list = NULL; |
1155 |
const char *interfaces = "/etc/network/interfaces"; |
const char *interfaces = "/etc/network/interfaces"; |
1156 |
int any_failures = 0; |
bool any_failures = 0; |
1157 |
|
|
1158 |
cmds = iface_down; |
cmds = iface_down; |
1159 |
if (applet_name[2] == 'u') { |
if (applet_name[2] == 'u') { |
1161 |
cmds = iface_up; |
cmds = iface_up; |
1162 |
} |
} |
1163 |
|
|
1164 |
getopt32(argc, argv, OPTION_STR, &interfaces); |
getopt32(argv, OPTION_STR, &interfaces); |
1165 |
if (argc - optind > 0) { |
if (argc - optind > 0) { |
1166 |
if (DO_ALL) bb_show_usage(); |
if (DO_ALL) bb_show_usage(); |
1167 |
} else { |
} else { |
1172 |
defn = read_interfaces(interfaces); |
defn = read_interfaces(interfaces); |
1173 |
debug_noise("\ndone reading %s\n\n", interfaces); |
debug_noise("\ndone reading %s\n\n", interfaces); |
1174 |
|
|
|
if (!defn) { |
|
|
return EXIT_FAILURE; |
|
|
} |
|
|
|
|
1175 |
startup_PATH = getenv("PATH"); |
startup_PATH = getenv("PATH"); |
1176 |
if (!startup_PATH) startup_PATH = ""; |
if (!startup_PATH) startup_PATH = ""; |
1177 |
|
|
1178 |
/* Create a list of interfaces to work on */ |
/* Create a list of interfaces to work on */ |
1179 |
if (DO_ALL) { |
if (DO_ALL) { |
1180 |
if (cmds == iface_up) { |
target_list = defn->autointerfaces; |
|
target_list = defn->autointerfaces; |
|
|
} else { |
|
|
/* iface_down */ |
|
|
const llist_t *list = state_list; |
|
|
while (list) { |
|
|
llist_add_to_end(&target_list, xstrdup(list->data)); |
|
|
list = list->link; |
|
|
} |
|
|
target_list = defn->autointerfaces; |
|
|
} |
|
1181 |
} else { |
} else { |
1182 |
llist_add_to_end(&target_list, argv[optind]); |
llist_add_to_end(&target_list, argv[optind]); |
1183 |
} |
} |
1189 |
char *iface; |
char *iface; |
1190 |
char *liface; |
char *liface; |
1191 |
char *pch; |
char *pch; |
1192 |
int okay = 0; |
bool okay = 0; |
1193 |
int cmds_ret; |
int cmds_ret; |
1194 |
|
|
1195 |
iface = xstrdup(target_list->data); |
iface = xstrdup(target_list->data); |
1204 |
} |
} |
1205 |
|
|
1206 |
if (!FORCE) { |
if (!FORCE) { |
1207 |
|
llist_t *state_list = read_iface_state(); |
1208 |
const llist_t *iface_state = find_iface_state(state_list, iface); |
const llist_t *iface_state = find_iface_state(state_list, iface); |
1209 |
|
|
1210 |
if (cmds == iface_up) { |
if (cmds == iface_up) { |
1215 |
} |
} |
1216 |
} else { |
} else { |
1217 |
/* ifdown */ |
/* ifdown */ |
1218 |
if (iface_state) { |
if (!iface_state) { |
1219 |
bb_error_msg("interface %s not configured", iface); |
bb_error_msg("interface %s not configured", iface); |
1220 |
continue; |
continue; |
1221 |
} |
} |
1222 |
} |
} |
1223 |
|
llist_free(state_list, free); |
1224 |
} |
} |
1225 |
|
|
1226 |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
#if ENABLE_FEATURE_IFUPDOWN_MAPPING |
1268 |
iface_list = iface_list->link; |
iface_list = iface_list->link; |
1269 |
} |
} |
1270 |
if (VERBOSE) { |
if (VERBOSE) { |
1271 |
puts(""); |
bb_putchar('\n'); |
1272 |
} |
} |
1273 |
|
|
1274 |
if (!okay && !FORCE) { |
if (!okay && !FORCE) { |
1275 |
bb_error_msg("ignoring unknown interface %s", liface); |
bb_error_msg("ignoring unknown interface %s", liface); |
1276 |
any_failures = 1; |
any_failures = 1; |
1277 |
} else { |
} else if (!NO_ACT) { |
1278 |
|
/* update the state file */ |
1279 |
|
FILE *state_fp; |
1280 |
|
llist_t *state; |
1281 |
|
llist_t *state_list = read_iface_state(); |
1282 |
llist_t *iface_state = find_iface_state(state_list, iface); |
llist_t *iface_state = find_iface_state(state_list, iface); |
1283 |
|
|
1284 |
if (cmds == iface_up) { |
if (cmds == iface_up) { |
1285 |
char *newiface = xasprintf("%s=%s", iface, liface); |
char * const newiface = xasprintf("%s=%s", iface, liface); |
1286 |
if (iface_state == NULL) { |
if (iface_state == NULL) { |
1287 |
llist_add_to_end(&state_list, newiface); |
llist_add_to_end(&state_list, newiface); |
1288 |
} else { |
} else { |
1290 |
iface_state->data = newiface; |
iface_state->data = newiface; |
1291 |
} |
} |
1292 |
} else { |
} else { |
1293 |
/* Remove an interface from the linked list */ |
/* Remove an interface from state_list */ |
1294 |
|
llist_unlink(&state_list, iface_state); |
1295 |
free(llist_pop(&iface_state)); |
free(llist_pop(&iface_state)); |
1296 |
} |
} |
|
} |
|
|
} |
|
1297 |
|
|
1298 |
/* Actually write the new state */ |
/* Actually write the new state */ |
1299 |
if (!NO_ACT) { |
state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH); |
1300 |
FILE *state_fp; |
state = state_list; |
1301 |
|
while (state) { |
1302 |
state_fp = xfopen("/var/run/ifstate", "w"); |
if (state->data) { |
1303 |
while (state_list) { |
fprintf(state_fp, "%s\n", state->data); |
1304 |
if (state_list->data) { |
} |
1305 |
fprintf(state_fp, "%s\n", state_list->data); |
state = state->link; |
1306 |
} |
} |
1307 |
state_list = state_list->link; |
fclose(state_fp); |
1308 |
|
llist_free(state_list, free); |
1309 |
} |
} |
|
fclose(state_fp); |
|
1310 |
} |
} |
1311 |
|
|
1312 |
return any_failures; |
return any_failures; |