source: MondoRescue/branches/stable/mindi-busybox/networking/ifupdown.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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.