1 /* vi: set sw=4 ts=4: */
4 * Copyright (c) 2002 Glenn McGrath
5 * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
7 * Based on ifupdown v 0.6.4 by Anthony Towns
8 * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
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 * 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
17 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
20 //usage:#define ifup_trivial_usage
21 //usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
22 //usage:#define ifup_full_usage "\n\n"
23 //usage: " -a De/configure all interfaces automatically"
24 //usage: "\n -i FILE Use FILE for interface definitions"
25 //usage: "\n -n Print out what would happen, but don't do it"
26 //usage: IF_FEATURE_IFUPDOWN_MAPPING(
27 //usage: "\n (note: doesn't disable mappings)"
28 //usage: "\n -m Don't run any mappings"
30 //usage: "\n -v Print out what would happen before doing it"
31 //usage: "\n -f Force de/configuration"
33 //usage:#define ifdown_trivial_usage
34 //usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
35 //usage:#define ifdown_full_usage "\n\n"
36 //usage: " -a De/configure all interfaces automatically"
37 //usage: "\n -i FILE Use FILE for interface definitions"
38 //usage: "\n -n Print out what would happen, but don't do it"
39 //usage: IF_FEATURE_IFUPDOWN_MAPPING(
40 //usage: "\n (note: doesn't disable mappings)"
41 //usage: "\n -m Don't run any mappings"
43 //usage: "\n -v Print out what would happen before doing it"
44 //usage: "\n -f Force de/configuration"
47 /* After libbb.h, since it needs sys/types.h on some systems */
48 #include <sys/utsname.h>
51 #define MAX_OPT_DEPTH 10
53 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
54 #define MAX_INTERFACE_LENGTH 10
57 #define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
59 #define debug_noise(args...) /*fprintf(stderr, args)*/
61 /* Forward declaration */
62 struct interface_defn_t
;
64 typedef int execfn(char *command
);
68 int (*up
)(struct interface_defn_t
*ifd
, execfn
*e
) FAST_FUNC
;
69 int (*down
)(struct interface_defn_t
*ifd
, execfn
*e
) FAST_FUNC
;
72 struct address_family_t
{
75 const struct method_t
*method
;
78 struct mapping_defn_t
{
79 struct mapping_defn_t
*next
;
96 struct interface_defn_t
{
97 const struct address_family_t
*address_family
;
98 const struct method_t
*method
;
102 struct variable_t
*option
;
105 struct interfaces_file_t
{
106 llist_t
*autointerfaces
;
108 struct mapping_defn_t
*mappings
;
112 #define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:"
118 OPT_no_mappings
= 0x10,
120 #define DO_ALL (option_mask32 & OPT_do_all)
121 #define NO_ACT (option_mask32 & OPT_no_act)
122 #define VERBOSE (option_mask32 & OPT_verbose)
123 #define FORCE (option_mask32 & OPT_force)
124 #define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
129 const char *startup_PATH
;
132 #define G (*(struct globals*)&bb_common_bufsiz1)
133 #define INIT_G() do { } while (0)
136 static const char keywords_up_down
[] ALIGN1
=
144 #if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
146 static void addstr(char **bufp
, const char *str
, size_t str_length
)
148 /* xasprintf trick will be smaller, but we are often
149 * called with str_length == 1 - don't want to have
150 * THAT much of malloc/freeing! */
152 int len
= (buf
? strlen(buf
) : 0);
154 buf
= xrealloc(buf
, len
+ str_length
);
155 /* copies at most str_length-1 chars! */
156 safe_strncpy(buf
+ len
, str
, str_length
);
160 static int strncmpz(const char *l
, const char *r
, size_t llen
)
162 int i
= strncmp(l
, r
, llen
);
165 return - (unsigned char)r
[llen
];
169 static char *get_var(const char *id
, size_t idlen
, struct interface_defn_t
*ifd
)
173 if (strncmpz(id
, "iface", idlen
) == 0) {
174 // ubuntu's ifup doesn't do this:
175 //static char *label_buf;
178 //label_buf = xstrdup(ifd->iface);
179 // Remove virtual iface suffix
180 //result = strchrnul(label_buf, ':');
186 if (strncmpz(id
, "label", idlen
) == 0) {
189 for (i
= 0; i
< ifd
->n_options
; i
++) {
190 if (strncmpz(id
, ifd
->option
[i
].name
, idlen
) == 0) {
191 return ifd
->option
[i
].value
;
197 # if ENABLE_FEATURE_IFUPDOWN_IP
198 static int count_netmask_bits(const char *dotted_quad
)
201 // unsigned a, b, c, d;
202 // /* Found a netmask... Check if it is dotted quad */
203 // if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
205 // if ((a|b|c|d) >> 8)
206 // return -1; /* one of numbers is >= 256 */
207 // d |= (a << 24) | (b << 16) | (c << 8); /* IP */
208 // d = ~d; /* 11110000 -> 00001111 */
210 /* Shorter version */
215 if (inet_aton(dotted_quad
, &ip
) == 0)
216 return -1; /* malformed dotted IP */
217 d
= ntohl(ip
.s_addr
); /* IP in host order */
218 d
= ~d
; /* 11110000 -> 00001111 */
219 if (d
& (d
+1)) /* check that it is in 00001111 form */
220 return -1; /* no it is not */
230 static char *parse(const char *command
, struct interface_defn_t
*ifd
)
232 size_t old_pos
[MAX_OPT_DEPTH
] = { 0 };
233 smallint okay
[MAX_OPT_DEPTH
] = { 1 };
240 addstr(&result
, command
, 1);
246 addstr(&result
, command
, 1);
250 if (command
[1] == '[' && opt_depth
< MAX_OPT_DEPTH
) {
251 old_pos
[opt_depth
] = result
? strlen(result
) : 0;
256 addstr(&result
, command
, 1);
261 if (command
[1] == ']' && opt_depth
> 1) {
263 if (!okay
[opt_depth
]) {
264 result
[old_pos
[opt_depth
]] = '\0';
268 addstr(&result
, command
, 1);
278 nextpercent
= strchr(command
, '%');
280 /* Unterminated %var% */
285 varvalue
= get_var(command
, nextpercent
- command
, ifd
);
288 # if ENABLE_FEATURE_IFUPDOWN_IP
289 /* "hwaddress <class> <address>":
290 * unlike ifconfig, ip doesnt want <class>
291 * (usually "ether" keyword). Skip it. */
292 if (strncmp(command
, "hwaddress", 9) == 0) {
293 varvalue
= skip_whitespace(skip_non_whitespace(varvalue
));
296 addstr(&result
, varvalue
, strlen(varvalue
));
298 # if ENABLE_FEATURE_IFUPDOWN_IP
299 /* Sigh... Add a special case for 'ip' to convert from
300 * dotted quad to bit count style netmasks. */
301 if (strncmp(command
, "bnmask", 6) == 0) {
303 varvalue
= get_var("netmask", 7, ifd
);
305 res
= count_netmask_bits(varvalue
);
307 const char *argument
= utoa(res
);
308 addstr(&result
, argument
, strlen(argument
));
309 command
= nextpercent
+ 1;
315 okay
[opt_depth
- 1] = 0;
318 command
= nextpercent
+ 1;
325 /* Unbalanced bracket */
331 /* Undefined variable and we aren't in a bracket */
339 /* execute() returns 1 for success and 0 for failure */
340 static int execute(const char *command
, struct interface_defn_t
*ifd
, execfn
*exec
)
345 out
= parse(command
, ifd
);
350 /* out == "": parsed ok but not all needed variables known, skip */
351 ret
= out
[0] ? (*exec
)(out
) : 1;
360 #endif /* FEATURE_IFUPDOWN_IPV4 || FEATURE_IFUPDOWN_IPV6 */
363 #if ENABLE_FEATURE_IFUPDOWN_IPV6
365 static int FAST_FUNC
loopback_up6(struct interface_defn_t
*ifd
, execfn
*exec
)
367 # if ENABLE_FEATURE_IFUPDOWN_IP
369 result
= execute("ip addr add ::1 dev %iface%", ifd
, exec
);
370 result
+= execute("ip link set %iface% up", ifd
, exec
);
371 return ((result
== 2) ? 2 : 0);
373 return execute("ifconfig %iface% add ::1", ifd
, exec
);
377 static int FAST_FUNC
loopback_down6(struct interface_defn_t
*ifd
, execfn
*exec
)
379 # if ENABLE_FEATURE_IFUPDOWN_IP
380 return execute("ip link set %iface% down", ifd
, exec
);
382 return execute("ifconfig %iface% del ::1", ifd
, exec
);
386 static int FAST_FUNC
manual_up_down6(struct interface_defn_t
*ifd UNUSED_PARAM
, execfn
*exec UNUSED_PARAM
)
391 static int FAST_FUNC
static_up6(struct interface_defn_t
*ifd
, execfn
*exec
)
394 # if ENABLE_FEATURE_IFUPDOWN_IP
395 result
= execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd
, exec
);
396 result
+= execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd
, exec
);
397 /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
398 result
+= execute("[[ip route add ::/0 via %gateway%]][[ prio %metric%]]", ifd
, exec
);
400 result
= execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd
, exec
);
401 result
+= execute("ifconfig %iface% add %address%/%netmask%", ifd
, exec
);
402 result
+= execute("[[route -A inet6 add ::/0 gw %gateway%[[ metric %metric%]]]]", ifd
, exec
);
404 return ((result
== 3) ? 3 : 0);
407 static int FAST_FUNC
static_down6(struct interface_defn_t
*ifd
, execfn
*exec
)
409 # if ENABLE_FEATURE_IFUPDOWN_IP
410 return execute("ip link set %iface% down", ifd
, exec
);
412 return execute("ifconfig %iface% down", ifd
, exec
);
416 # if ENABLE_FEATURE_IFUPDOWN_IP
417 static int FAST_FUNC
v4tunnel_up(struct interface_defn_t
*ifd
, execfn
*exec
)
420 result
= execute("ip tunnel add %iface% mode sit remote "
421 "%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd
, exec
);
422 result
+= execute("ip link set %iface% up", ifd
, exec
);
423 result
+= execute("ip addr add %address%/%netmask% dev %iface%", ifd
, exec
);
424 result
+= execute("[[ip route add ::/0 via %gateway%]]", ifd
, exec
);
425 return ((result
== 4) ? 4 : 0);
428 static int FAST_FUNC
v4tunnel_down(struct interface_defn_t
* ifd
, execfn
* exec
)
430 return execute("ip tunnel del %iface%", ifd
, exec
);
434 static const struct method_t methods6
[] = {
435 # if ENABLE_FEATURE_IFUPDOWN_IP
436 { "v4tunnel" , v4tunnel_up
, v4tunnel_down
, },
438 { "static" , static_up6
, static_down6
, },
439 { "manual" , manual_up_down6
, manual_up_down6
, },
440 { "loopback" , loopback_up6
, loopback_down6
, },
443 static const struct address_family_t addr_inet6
= {
445 ARRAY_SIZE(methods6
),
449 #endif /* FEATURE_IFUPDOWN_IPV6 */
452 #if ENABLE_FEATURE_IFUPDOWN_IPV4
454 static int FAST_FUNC
loopback_up(struct interface_defn_t
*ifd
, execfn
*exec
)
456 # if ENABLE_FEATURE_IFUPDOWN_IP
458 result
= execute("ip addr add 127.0.0.1/8 dev %iface%", ifd
, exec
);
459 result
+= execute("ip link set %iface% up", ifd
, exec
);
460 return ((result
== 2) ? 2 : 0);
462 return execute("ifconfig %iface% 127.0.0.1 up", ifd
, exec
);
466 static int FAST_FUNC
loopback_down(struct interface_defn_t
*ifd
, execfn
*exec
)
468 # if ENABLE_FEATURE_IFUPDOWN_IP
470 result
= execute("ip addr flush dev %iface%", ifd
, exec
);
471 result
+= execute("ip link set %iface% down", ifd
, exec
);
472 return ((result
== 2) ? 2 : 0);
474 return execute("ifconfig %iface% 127.0.0.1 down", ifd
, exec
);
478 static int FAST_FUNC
static_up(struct interface_defn_t
*ifd
, execfn
*exec
)
481 # if ENABLE_FEATURE_IFUPDOWN_IP
482 result
= execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
483 "dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd
, exec
);
484 result
+= execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd
, exec
);
485 result
+= execute("[[ip route add default via %gateway% dev %iface%[[ prio %metric%]]]]", ifd
, exec
);
486 return ((result
== 3) ? 3 : 0);
488 /* ifconfig said to set iface up before it processes hw %hwaddress%,
489 * which then of course fails. Thus we run two separate ifconfig */
490 result
= execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
492 result
+= execute("ifconfig %iface% %address% netmask %netmask%"
493 "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
495 result
+= execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd
, exec
);
496 return ((result
== 3) ? 3 : 0);
500 static int FAST_FUNC
static_down(struct interface_defn_t
*ifd
, execfn
*exec
)
503 # if ENABLE_FEATURE_IFUPDOWN_IP
504 result
= execute("ip addr flush dev %iface%", ifd
, exec
);
505 result
+= execute("ip link set %iface% down", ifd
, exec
);
507 /* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
508 /* Bringing the interface down deletes the routes in itself.
509 Otherwise this fails if we reference 'gateway' when using this from dhcp_down */
511 result
+= execute("ifconfig %iface% down", ifd
, exec
);
513 return ((result
== 2) ? 2 : 0);
516 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
517 struct dhcp_client_t
{
519 const char *startcmd
;
523 static const struct dhcp_client_t ext_dhcp_clients
[] = {
525 "dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
529 "dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
530 "kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
533 "pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
534 "pump -i %iface% -k",
537 "udhcpc " UDHCPC_CMD_OPTIONS
" -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]"
538 "[[ -s %script%]][[ %udhcpc_opts%]]",
539 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
542 # endif /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
544 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
545 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
548 # if ENABLE_FEATURE_IFUPDOWN_IP
549 /* ip doesn't up iface when it configures it (unlike ifconfig) */
550 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd
, exec
))
553 /* needed if we have hwaddress on dhcp iface */
554 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd
, exec
))
557 for (i
= 0; i
< ARRAY_SIZE(ext_dhcp_clients
); i
++) {
558 if (executable_exists(ext_dhcp_clients
[i
].name
))
559 return execute(ext_dhcp_clients
[i
].startcmd
, ifd
, exec
);
561 bb_error_msg("no dhcp clients found");
565 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
567 # if ENABLE_FEATURE_IFUPDOWN_IP
568 /* ip doesn't up iface when it configures it (unlike ifconfig) */
569 if (!execute("ip link set[[ addr %hwaddress%]] %iface% up", ifd
, exec
))
572 /* needed if we have hwaddress on dhcp iface */
573 if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd
, exec
))
576 return execute("udhcpc " UDHCPC_CMD_OPTIONS
" -p /var/run/udhcpc.%iface%.pid "
577 "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
581 static int FAST_FUNC
dhcp_up(struct interface_defn_t
*ifd UNUSED_PARAM
,
582 execfn
*exec UNUSED_PARAM
)
584 return 0; /* no dhcp support */
588 # if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
589 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
594 for (i
= 0; i
< ARRAY_SIZE(ext_dhcp_clients
); i
++) {
595 if (executable_exists(ext_dhcp_clients
[i
].name
)) {
596 result
= execute(ext_dhcp_clients
[i
].stopcmd
, ifd
, exec
);
603 bb_error_msg("warning: no dhcp clients found and stopped");
605 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
606 and it may come back up because udhcpc is still shutting down */
608 result
+= static_down(ifd
, exec
);
609 return ((result
== 3) ? 3 : 0);
612 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
616 "test -f /var/run/udhcpc.%iface%.pid && "
617 "kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
619 /* Also bring the hardware interface down since
620 killing the dhcp client alone doesn't do it.
621 This enables consecutive ifup->ifdown->ifup */
622 /* Sleep a bit, otherwise static_down tries to bring down interface too soon,
623 and it may come back up because udhcpc is still shutting down */
625 result
+= static_down(ifd
, exec
);
626 return ((result
== 3) ? 3 : 0);
629 static int FAST_FUNC
dhcp_down(struct interface_defn_t
*ifd UNUSED_PARAM
,
630 execfn
*exec UNUSED_PARAM
)
632 return 0; /* no dhcp support */
636 static int FAST_FUNC
manual_up_down(struct interface_defn_t
*ifd UNUSED_PARAM
, execfn
*exec UNUSED_PARAM
)
641 static int FAST_FUNC
bootp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
643 return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
644 "[[ --server %server%]][[ --hwaddr %hwaddr%]]"
645 " --returniffail --serverbcast", ifd
, exec
);
648 static int FAST_FUNC
ppp_up(struct interface_defn_t
*ifd
, execfn
*exec
)
650 return execute("pon[[ %provider%]]", ifd
, exec
);
653 static int FAST_FUNC
ppp_down(struct interface_defn_t
*ifd
, execfn
*exec
)
655 return execute("poff[[ %provider%]]", ifd
, exec
);
658 static int FAST_FUNC
wvdial_up(struct interface_defn_t
*ifd
, execfn
*exec
)
660 return execute("start-stop-daemon --start -x wvdial "
661 "-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd
, exec
);
664 static int FAST_FUNC
wvdial_down(struct interface_defn_t
*ifd
, execfn
*exec
)
666 return execute("start-stop-daemon --stop -x wvdial "
667 "-p /var/run/wvdial.%iface% -s 2", ifd
, exec
);
670 static const struct method_t methods
[] = {
671 { "manual" , manual_up_down
, manual_up_down
, },
672 { "wvdial" , wvdial_up
, wvdial_down
, },
673 { "ppp" , ppp_up
, ppp_down
, },
674 { "static" , static_up
, static_down
, },
675 { "bootp" , bootp_up
, static_down
, },
676 { "dhcp" , dhcp_up
, dhcp_down
, },
677 { "loopback", loopback_up
, loopback_down
, },
680 static const struct address_family_t addr_inet
= {
686 #endif /* FEATURE_IFUPDOWN_IPV4 */
688 static int FAST_FUNC
link_up_down(struct interface_defn_t
*ifd UNUSED_PARAM
, execfn
*exec UNUSED_PARAM
)
693 static const struct method_t link_methods
[] = {
694 { "none", link_up_down
, link_up_down
}
697 static const struct address_family_t addr_link
= {
698 "link", ARRAY_SIZE(link_methods
), link_methods
701 /* Returns pointer to the next word, or NULL.
702 * In 1st case, advances *buf to the word after this one.
704 static char *next_word(char **buf
)
709 /* Skip over leading whitespace */
710 word
= skip_whitespace(*buf
);
716 /* Find the length of this word (can't be 0) */
717 length
= strcspn(word
, " \t\n");
719 /* Unless we are already at NUL, store NUL and advance */
720 if (word
[length
] != '\0')
721 word
[length
++] = '\0';
723 *buf
= skip_whitespace(word
+ length
);
728 static const struct address_family_t
*get_address_family(const struct address_family_t
*const af
[], char *name
)
735 for (i
= 0; af
[i
]; i
++) {
736 if (strcmp(af
[i
]->name
, name
) == 0) {
743 static const struct method_t
*get_method(const struct address_family_t
*af
, char *name
)
749 /* TODO: use index_in_str_array() */
750 for (i
= 0; i
< af
->n_methods
; i
++) {
751 if (strcmp(af
->method
[i
].name
, name
) == 0) {
752 return &af
->method
[i
];
758 static struct interfaces_file_t
*read_interfaces(const char *filename
, struct interfaces_file_t
*defn
)
760 /* Let's try to be compatible.
762 * "man 5 interfaces" says:
763 * Lines starting with "#" are ignored. Note that end-of-line
764 * comments are NOT supported, comments must be on a line of their own.
765 * A line may be extended across multiple lines by making
766 * the last character a backslash.
768 * Seen elsewhere in example config file:
769 * A first non-blank "#" character makes the rest of the line
770 * be ignored. Blank lines are ignored. Lines may be indented freely.
771 * A "\" character at the very end of the line indicates the next line
772 * should be treated as a continuation of the current one.
774 * Lines beginning with "source" are used to include stanzas from
775 * other files, so configuration can be split into many files.
776 * The word "source" is followed by the path of file to be sourced.
778 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
779 struct mapping_defn_t
*currmap
= NULL
;
781 struct interface_defn_t
*currif
= NULL
;
786 enum { NONE
, IFACE
, MAPPING
} currently_processing
= NONE
;
789 defn
= xzalloc(sizeof(*defn
));
791 debug_noise("reading %s file:\n", filename
);
792 f
= xfopen_for_read(filename
);
794 while ((buf
= xmalloc_fgetline(f
)) != NULL
) {
796 /* Trailing "\" concatenates lines */
798 while ((p
= last_char_is(buf
, '\\')) != NULL
) {
800 rest_of_line
= xmalloc_fgetline(f
);
803 p
= xasprintf("%s%s", buf
, rest_of_line
);
810 first_word
= next_word(&rest_of_line
);
811 if (!first_word
|| *first_word
== '#') {
813 continue; /* blank/comment line */
816 if (strcmp(first_word
, "mapping") == 0) {
817 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
818 currmap
= xzalloc(sizeof(*currmap
));
820 while ((first_word
= next_word(&rest_of_line
)) != NULL
) {
821 currmap
->match
= xrealloc_vector(currmap
->match
, 4, currmap
->n_matches
);
822 currmap
->match
[currmap
->n_matches
++] = xstrdup(first_word
);
824 /*currmap->n_mappings = 0;*/
825 /*currmap->mapping = NULL;*/
826 /*currmap->script = NULL;*/
828 struct mapping_defn_t
**where
= &defn
->mappings
;
829 while (*where
!= NULL
) {
830 where
= &(*where
)->next
;
833 /*currmap->next = NULL;*/
835 debug_noise("Added mapping\n");
837 currently_processing
= MAPPING
;
838 } else if (strcmp(first_word
, "iface") == 0) {
839 static const struct address_family_t
*const addr_fams
[] = {
840 #if ENABLE_FEATURE_IFUPDOWN_IPV4
843 #if ENABLE_FEATURE_IFUPDOWN_IPV6
850 char *address_family_name
;
854 currif
= xzalloc(sizeof(*currif
));
855 iface_name
= next_word(&rest_of_line
);
856 address_family_name
= next_word(&rest_of_line
);
857 method_name
= next_word(&rest_of_line
);
859 if (method_name
== NULL
)
860 bb_error_msg_and_die("too few parameters for line \"%s\"", buf
);
862 /* ship any trailing whitespace */
863 rest_of_line
= skip_whitespace(rest_of_line
);
865 if (rest_of_line
[0] != '\0' /* && rest_of_line[0] != '#' */)
866 bb_error_msg_and_die("too many parameters \"%s\"", buf
);
868 currif
->iface
= xstrdup(iface_name
);
870 currif
->address_family
= get_address_family(addr_fams
, address_family_name
);
871 if (!currif
->address_family
)
872 bb_error_msg_and_die("unknown address type \"%s\"", address_family_name
);
874 currif
->method
= get_method(currif
->address_family
, method_name
);
876 bb_error_msg_and_die("unknown method \"%s\"", method_name
);
878 for (iface_list
= defn
->ifaces
; iface_list
; iface_list
= iface_list
->link
) {
879 struct interface_defn_t
*tmp
= (struct interface_defn_t
*) iface_list
->data
;
880 if ((strcmp(tmp
->iface
, currif
->iface
) == 0)
881 && (tmp
->address_family
== currif
->address_family
)
883 bb_error_msg_and_die("duplicate interface \"%s\"", tmp
->iface
);
886 llist_add_to_end(&(defn
->ifaces
), (char*)currif
);
888 debug_noise("iface %s %s %s\n", currif
->iface
, address_family_name
, method_name
);
889 currently_processing
= IFACE
;
890 } else if (strcmp(first_word
, "auto") == 0) {
891 while ((first_word
= next_word(&rest_of_line
)) != NULL
) {
893 /* Check the interface isnt already listed */
894 if (llist_find_str(defn
->autointerfaces
, first_word
)) {
895 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf
);
898 /* Add the interface to the list */
899 llist_add_to_end(&(defn
->autointerfaces
), xstrdup(first_word
));
900 debug_noise("\nauto %s\n", first_word
);
902 currently_processing
= NONE
;
903 } else if (strcmp(first_word
, "source") == 0) {
904 read_interfaces(next_word(&rest_of_line
), defn
);
906 switch (currently_processing
) {
908 if (rest_of_line
[0] == '\0')
909 bb_error_msg_and_die("option with empty value \"%s\"", buf
);
911 if (strcmp(first_word
, "post-up") == 0)
912 first_word
+= 5; /* "up" */
913 else if (strcmp(first_word
, "pre-down") == 0)
914 first_word
+= 4; /* "down" */
916 /* If not one of "up", "down",... words... */
917 if (index_in_strings(keywords_up_down
, first_word
) < 0) {
919 for (i
= 0; i
< currif
->n_options
; i
++) {
920 if (strcmp(currif
->option
[i
].name
, first_word
) == 0)
921 bb_error_msg_and_die("duplicate option \"%s\"", buf
);
924 debug_noise("\t%s=%s\n", first_word
, rest_of_line
);
925 currif
->option
= xrealloc_vector(currif
->option
, 4, currif
->n_options
);
926 currif
->option
[currif
->n_options
].name
= xstrdup(first_word
);
927 currif
->option
[currif
->n_options
].value
= xstrdup(rest_of_line
);
931 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
932 if (strcmp(first_word
, "script") == 0) {
933 if (currmap
->script
!= NULL
)
934 bb_error_msg_and_die("duplicate script in mapping \"%s\"", buf
);
935 currmap
->script
= xstrdup(next_word(&rest_of_line
));
936 } else if (strcmp(first_word
, "map") == 0) {
937 currmap
->mapping
= xrealloc_vector(currmap
->mapping
, 2, currmap
->n_mappings
);
938 currmap
->mapping
[currmap
->n_mappings
] = xstrdup(next_word(&rest_of_line
));
939 currmap
->n_mappings
++;
941 bb_error_msg_and_die("misplaced option \"%s\"", buf
);
947 bb_error_msg_and_die("misplaced option \"%s\"", buf
);
951 } /* while (fgets) */
953 if (ferror(f
) != 0) {
954 /* ferror does NOT set errno! */
955 bb_error_msg_and_die("%s: I/O error", filename
);
958 debug_noise("\ndone reading %s\n\n", filename
);
963 static char *setlocalenv(const char *format
, const char *name
, const char *value
)
970 result
= xasprintf(format
, name
, value
);
972 for (dst
= src
= result
; (c
= *src
) != '=' && c
; src
++) {
975 if (c
>= 'a' && c
<= 'z')
977 if (isalnum(c
) || c
== '_')
980 overlapping_strcpy(dst
, src
);
985 static void set_environ(struct interface_defn_t
*iface
, const char *mode
, const char *opt
)
990 if (G
.my_environ
!= NULL
) {
991 for (pp
= G
.my_environ
; *pp
; pp
++) {
997 /* note: last element will stay NULL: */
998 G
.my_environ
= xzalloc(sizeof(char *) * (iface
->n_options
+ 7));
1001 for (i
= 0; i
< iface
->n_options
; i
++) {
1002 if (index_in_strings(keywords_up_down
, iface
->option
[i
].name
) >= 0) {
1005 *pp
++ = setlocalenv("IF_%s=%s", iface
->option
[i
].name
, iface
->option
[i
].value
);
1008 *pp
++ = setlocalenv("%s=%s", "IFACE", iface
->iface
);
1009 *pp
++ = setlocalenv("%s=%s", "ADDRFAM", iface
->address_family
->name
);
1010 *pp
++ = setlocalenv("%s=%s", "METHOD", iface
->method
->name
);
1011 *pp
++ = setlocalenv("%s=%s", "MODE", mode
);
1012 *pp
++ = setlocalenv("%s=%s", "PHASE", opt
);
1014 *pp
++ = setlocalenv("%s=%s", "PATH", G
.startup_PATH
);
1017 static int doit(char *str
)
1019 if (option_mask32
& (OPT_no_act
|OPT_verbose
)) {
1022 if (!(option_mask32
& OPT_no_act
)) {
1028 if (child
< 0) /* failure */
1030 if (child
== 0) { /* child */
1031 execle(G
.shell
, G
.shell
, "-c", str
, (char *) NULL
, G
.my_environ
);
1034 safe_waitpid(child
, &status
, 0);
1035 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
1042 static int execute_all(struct interface_defn_t
*ifd
, const char *opt
)
1046 for (i
= 0; i
< ifd
->n_options
; i
++) {
1047 if (strcmp(ifd
->option
[i
].name
, opt
) == 0) {
1048 if (!doit(ifd
->option
[i
].value
)) {
1054 buf
= xasprintf("run-parts /etc/network/if-%s.d", opt
);
1055 /* heh, we don't bother free'ing it */
1059 static int check(char *str
)
1064 static int iface_up(struct interface_defn_t
*iface
)
1066 if (!iface
->method
->up(iface
, check
)) return -1;
1067 set_environ(iface
, "start", "pre-up");
1068 if (!execute_all(iface
, "pre-up")) return 0;
1069 if (!iface
->method
->up(iface
, doit
)) return 0;
1070 set_environ(iface
, "start", "post-up");
1071 if (!execute_all(iface
, "up")) return 0;
1075 static int iface_down(struct interface_defn_t
*iface
)
1077 if (!iface
->method
->down(iface
, check
)) return -1;
1078 set_environ(iface
, "stop", "pre-down");
1079 if (!execute_all(iface
, "down")) return 0;
1080 if (!iface
->method
->down(iface
, doit
)) return 0;
1081 set_environ(iface
, "stop", "post-down");
1082 if (!execute_all(iface
, "post-down")) return 0;
1086 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
1087 static int popen2(FILE **in
, FILE **out
, char *command
, char *param
)
1089 char *argv
[3] = { command
, param
, NULL
};
1090 struct fd_pair infd
, outfd
;
1101 /* NB: close _first_, then move fds! */
1104 xmove_fd(infd
.rd
, 0);
1105 xmove_fd(outfd
.wr
, 1);
1106 BB_EXECVP_or_die(argv
);
1111 *in
= xfdopen_for_write(infd
.wr
);
1112 *out
= xfdopen_for_read(outfd
.rd
);
1116 static char *run_mapping(char *physical
, struct mapping_defn_t
*map
)
1122 char *logical
= xstrdup(physical
);
1124 /* Run the mapping script. Never fails. */
1125 pid
= popen2(&in
, &out
, map
->script
, physical
);
1127 /* Write mappings to stdin of mapping script. */
1128 for (i
= 0; i
< map
->n_mappings
; i
++) {
1129 fprintf(in
, "%s\n", map
->mapping
[i
]);
1132 safe_waitpid(pid
, &status
, 0);
1134 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0) {
1135 /* If the mapping script exited successfully, try to
1136 * grab a line of output and use that as the name of the
1137 * logical interface. */
1138 char *new_logical
= xmalloc_fgetline(out
);
1141 /* If we are able to read a line of output from the script,
1142 * remove any trailing whitespace and use this value
1143 * as the name of the logical interface. */
1144 char *pch
= new_logical
+ strlen(new_logical
) - 1;
1146 while (pch
>= new_logical
&& isspace(*pch
))
1150 logical
= new_logical
;
1158 #endif /* FEATURE_IFUPDOWN_MAPPING */
1160 static llist_t
*find_iface_state(llist_t
*state_list
, const char *iface
)
1162 unsigned iface_len
= strlen(iface
);
1163 llist_t
*search
= state_list
;
1166 if ((strncmp(search
->data
, iface
, iface_len
) == 0)
1167 && (search
->data
[iface_len
] == '=')
1171 search
= search
->link
;
1176 /* read the previous state from the state file */
1177 static llist_t
*read_iface_state(void)
1179 llist_t
*state_list
= NULL
;
1180 FILE *state_fp
= fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH
);
1183 char *start
, *end_ptr
;
1184 while ((start
= xmalloc_fgets(state_fp
)) != NULL
) {
1185 /* We should only need to check for a single character */
1186 end_ptr
= start
+ strcspn(start
, " \t\n");
1188 llist_add_to(&state_list
, start
);
1196 int ifupdown_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
1197 int ifupdown_main(int argc UNUSED_PARAM
, char **argv
)
1199 int (*cmds
)(struct interface_defn_t
*);
1200 struct interfaces_file_t
*defn
;
1201 llist_t
*target_list
= NULL
;
1202 const char *interfaces
= "/etc/network/interfaces";
1203 bool any_failures
= 0;
1207 G
.startup_PATH
= getenv("PATH");
1208 G
.shell
= xstrdup(get_shell_name());
1211 if (applet_name
[2] == 'u') {
1216 getopt32(argv
, OPTION_STR
, &interfaces
);
1219 if (DO_ALL
) bb_show_usage();
1221 if (!DO_ALL
) bb_show_usage();
1224 defn
= read_interfaces(interfaces
, NULL
);
1226 /* Create a list of interfaces to work on */
1228 target_list
= defn
->autointerfaces
;
1230 llist_add_to_end(&target_list
, argv
[0]);
1233 /* Update the interfaces */
1234 while (target_list
) {
1235 llist_t
*iface_list
;
1236 struct interface_defn_t
*currif
;
1243 iface
= xstrdup(target_list
->data
);
1244 target_list
= target_list
->link
;
1246 pch
= strchr(iface
, '=');
1249 liface
= xstrdup(pch
+ 1);
1251 liface
= xstrdup(iface
);
1255 llist_t
*state_list
= read_iface_state();
1256 const llist_t
*iface_state
= find_iface_state(state_list
, iface
);
1258 if (cmds
== iface_up
) {
1261 bb_error_msg("interface %s already configured", iface
);
1267 bb_error_msg("interface %s not configured", iface
);
1271 llist_free(state_list
, free
);
1274 #if ENABLE_FEATURE_IFUPDOWN_MAPPING
1275 if ((cmds
== iface_up
) && !NO_MAPPINGS
) {
1276 struct mapping_defn_t
*currmap
;
1278 for (currmap
= defn
->mappings
; currmap
; currmap
= currmap
->next
) {
1280 for (i
= 0; i
< currmap
->n_matches
; i
++) {
1281 if (fnmatch(currmap
->match
[i
], liface
, 0) != 0)
1284 printf("Running mapping script %s on %s\n", currmap
->script
, liface
);
1286 liface
= run_mapping(iface
, currmap
);
1293 iface_list
= defn
->ifaces
;
1294 while (iface_list
) {
1295 currif
= (struct interface_defn_t
*) iface_list
->data
;
1296 if (strcmp(liface
, currif
->iface
) == 0) {
1297 char *oldiface
= currif
->iface
;
1300 currif
->iface
= iface
;
1302 debug_noise("\nConfiguring interface %s (%s)\n", liface
, currif
->address_family
->name
);
1304 /* Call the cmds function pointer, does either iface_up() or iface_down() */
1305 cmds_ret
= cmds(currif
);
1306 if (cmds_ret
== -1) {
1307 bb_error_msg("don't seem to have all the variables for %s/%s",
1308 liface
, currif
->address_family
->name
);
1310 } else if (cmds_ret
== 0) {
1314 currif
->iface
= oldiface
;
1316 iface_list
= iface_list
->link
;
1322 if (!okay
&& !FORCE
) {
1323 bb_error_msg("ignoring unknown interface %s", liface
);
1325 } else if (!NO_ACT
) {
1326 /* update the state file */
1329 llist_t
*state_list
= read_iface_state();
1330 llist_t
*iface_state
= find_iface_state(state_list
, iface
);
1332 if (cmds
== iface_up
&& !any_failures
) {
1333 char *newiface
= xasprintf("%s=%s", iface
, liface
);
1335 llist_add_to_end(&state_list
, newiface
);
1337 free(iface_state
->data
);
1338 iface_state
->data
= newiface
;
1341 /* Remove an interface from state_list */
1342 llist_unlink(&state_list
, iface_state
);
1343 free(llist_pop(&iface_state
));
1346 /* Actually write the new state */
1347 state_fp
= xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH
);
1351 fprintf(state_fp
, "%s\n", state
->data
);
1353 state
= state
->link
;
1356 llist_free(state_list
, free
);
1363 return any_failures
;