source: MondoRescue/branches/2.2.5/mindi-busybox/networking/ifupdown.c@ 1765

Last change on this file since 1765 was 1765, checked in by Bruno Cornec, 16 years ago

Update to busybox 1.7.2

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