Hello Denys, I've been resending this patch for a while without receiving any response. I kindly ask to at least know the reason why the patch is not considered.
In the meantime, my patches for MMU-less systems have been accepted and merged in the following projects: can-utils: https://github.com/linux-can/can-utils/commit/5ed3b4ded6cf3e4de6fc8c8739b84231b0285b0e libmnl: https://git.netfilter.org/libmnl/commit/?id=80442c4e32cde0b27b471c5c9688ee8488f14bec buildroot: https://git.busybox.net/buildroot/commit/?id=edb184fe46d3406add0cecae0dc0e4be52dff79c https://git.busybox.net/buildroot/commit/?id=77314408813186716ef3cb73d65da13705842e41 This demonstrates that there was a need to address some gaps for MMU-less systems. I hope that BusyBox also fills this gap. If necessary, I can simplify the patch based on your suggestions. Please, let me know Thanks and regards, Dario On Sat, Nov 11, 2023 at 11:53 AM Dario Binacchi <dario.binac...@amarulasolutions.com> wrote: > > I developed this application to test the Linux kernel series [1]. As > described in it I could not use the iproute2 package since the > microcontroller is without MMU. > > cc: Marc Kleine-Budde <m...@pengutronix.de> > [1] https://marc.info/?l=linux-netdev&m=167999323611710&w=2 > Signed-off-by: Dario Binacchi <dario.binac...@amarulasolutions.com> > --- > configs/TEST_nommu_defconfig | 1 + > networking/ip.c | 84 ++++++++++ > networking/libiproute/iplink.c | 298 ++++++++++++++++++++++++++++++++- > 3 files changed, 374 insertions(+), 9 deletions(-) > > diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig > index 415f5a8027f9..fa3e9632622a 100644 > --- a/configs/TEST_nommu_defconfig > +++ b/configs/TEST_nommu_defconfig > @@ -703,6 +703,7 @@ CONFIG_FEATURE_INETD_RPC=y > CONFIG_IP=y > CONFIG_FEATURE_IP_ADDRESS=y > CONFIG_FEATURE_IP_LINK=y > +CONFIG_FEATURE_IP_LINK_CAN=y > CONFIG_FEATURE_IP_ROUTE=y > CONFIG_FEATURE_IP_TUNNEL=y > CONFIG_FEATURE_IP_RULE=y > diff --git a/networking/ip.c b/networking/ip.c > index 36126b74738c..e5b704df5b85 100644 > --- a/networking/ip.c > +++ b/networking/ip.c > @@ -32,6 +32,14 @@ > //config: help > //config: Short form of "ip link" > //config: > +//config:config IPLINK_CAN > +//config: bool "iplink for CAN (4.6 kb)" > +//config: default n > +//config: depends on IPLINK > +//config: select FEATURE_IP_LINK_CAN > +//config: help > +//config: Short form of "ip link" for CAN > +//config: > //config:config IPROUTE > //config: bool "iproute (15 kb)" > //config: default y > @@ -74,6 +82,13 @@ > //config: help > //config: Configure network devices with "ip". > //config: > +//config:config FEATURE_IP_LINK_CAN > +//config: bool "ip link can" > +//config: default n > +//config: depends on IP_LINK_CAN > +//config: help > +//config: Configure CAN devices with "ip". > +//config: > //config:config FEATURE_IP_ROUTE > //config: bool "ip route" > //config: default y > @@ -122,6 +137,7 @@ > //applet:IF_IP( APPLET_NOEXEC(ip , ip , BB_DIR_SBIN, > BB_SUID_DROP, ip )) > //applet:IF_IPADDR( APPLET_NOEXEC(ipaddr , ipaddr , BB_DIR_SBIN, > BB_SUID_DROP, ipaddr )) > //applet:IF_IPLINK( APPLET_NOEXEC(iplink , iplink , BB_DIR_SBIN, > BB_SUID_DROP, iplink )) > +//applet:IF_IPLINK_CAN(APPLET_NOEXEC(iplinkcan , iplinkcan , BB_DIR_SBIN, > BB_SUID_DROP, iplinkcan)) > //applet:IF_IPROUTE( APPLET_NOEXEC(iproute , iproute , BB_DIR_SBIN, > BB_SUID_DROP, iproute )) > //applet:IF_IPRULE( APPLET_NOEXEC(iprule , iprule , BB_DIR_SBIN, > BB_SUID_DROP, iprule )) > //applet:IF_IPTUNNEL(APPLET_NOEXEC(iptunnel, iptunnel, BB_DIR_SBIN, > BB_SUID_DROP, iptunnel)) > @@ -130,6 +146,7 @@ > //kbuild:lib-$(CONFIG_IP) += ip.o > //kbuild:lib-$(CONFIG_IPADDR) += ip.o > //kbuild:lib-$(CONFIG_IPLINK) += ip.o > +//kbuild:lib-$(CONFIG_IPLINK_CAN) += ip.o > //kbuild:lib-$(CONFIG_IPROUTE) += ip.o > //kbuild:lib-$(CONFIG_IPRULE) += ip.o > //kbuild:lib-$(CONFIG_IPTUNNEL) += ip.o > @@ -149,10 +166,16 @@ > //usage: "ipaddr show|flush [dev IFACE] [scope SCOPE] [to PREFIX] > [label PATTERN]" > //usage: > > //--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 > +//usage:#if ENABLE_FEATURE_IP_LINK_CAN > +//usage:#define iplink_type_usage "\n [type TYPE ARGS]" > +//usage:#else > +//usage:#define iplink_type_usage "" > +//usage:#endif > //usage:#define iplink_trivial_usage > //usage: /*Usage:iplink*/"set IFACE [up|down] [arp on|off] [multicast > on|off]\n" > //usage: " [promisc on|off] [mtu NUM] [name NAME] [qlen NUM] > [address MAC]\n" > //usage: " [master IFACE | nomaster] [netns PID]" > +//usage: IF_FEATURE_IP_LINK(iplink_type_usage) > // * short help shows only "set" command, long help continues (with just one > "\n") > // * and shows all other commands: > //usage:#define iplink_full_usage "\n" > @@ -207,6 +230,59 @@ > // bond_slave | ipvlan | geneve | bridge_slave | vrf } > //usage: > > //--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 > +//usage:#define iplinkcan_trivial_usage > +//usage: /*Usage:iplinkcan*/"set DEVICE type can" > +//usage:#define iplinkcan_full_usage "\n\n" > +//usage: " [bitrate BITRATE [sample-point SAMPLE-POINT]] |\n" > +//usage: " [tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n" > +//usage: " phase-seg2 PHASE-SEG2 [sjw SJW]]\n" > +//usage: "\n" > +//usage: " [dbitrate BITRATE [dsample-point SAMPLE-POINT]] |\n" > +//usage: " [dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n" > +//usage: " dphase-seg2 PHASE-SEG2 [dsjw SJW]]\n" > +//usage: "\n" > +//usage: " [loopback on|off] [listen-only on|off] > [triple-sampling on|off]\n" > +//usage: " [one-shot on|off] [berr-reporting on|off]\n" > +//usage: " [fd on|off] [fd-non-iso on|off] [presume-ack > on|off]\n" > +//usage: "\n" > +//usage: " [restart-ms TIME-MS] [restart]\n" > +//usage: "\n" > +//usage: " [termination 0..65535]\n" > +//usage: > +//upstream man ip-link-can: > +//Usage: ip link set DEVICE type can > +// [ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | > +// [ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1 > +// phase-seg2 PHASE-SEG2 [ sjw SJW ] ] > +// > +// [ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] | > +// [ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1 > +// dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ] > +// > +// [ loopback { on | off } ] > +// [ listen-only { on | off } ] > +// [ triple-sampling { on | off } ] > +// [ one-shot { on | off } ] > +// [ berr-reporting { on | off } ] > +// [ fd { on | off } ] > +// [ fd-non-iso { on | off } ] > +// [ presume-ack { on | off } ] > +// > +// [ restart-ms TIME-MS ] > +// [ restart ] > +// > +// [ termination { 0..65535 } ] > +// > +// Where: BITRATE := { 1..1000000 } > +// SAMPLE-POINT := { 0.000..0.999 } > +// TQ := { NUMBER } > +// PROP-SEG := { 1..8 } > +// PHASE-SEG1 := { 1..8 } > +// PHASE-SEG2 := { 1..8 } > +// SJW := { 1..4 } > +// RESTART-MS := { 0 | NUMBER } > +//usage: > +//--------------123456789.123456789.123456789.123456789.123456789.123456789.123456789.123....79 > //usage:#define iproute_trivial_usage > //usage: "list|flush|add|del|change|append|replace|test ROUTE" > //usage:#define iproute_full_usage "\n\n" > @@ -327,6 +403,7 @@ typedef int FAST_FUNC (*ip_func_ptr_t)(char**); > > #if ENABLE_IPADDR \ > || ENABLE_IPLINK \ > + || ENABLE_IPLINK_CAN \ > || ENABLE_IPROUTE \ > || ENABLE_IPRULE \ > || ENABLE_IPTUNNEL \ > @@ -352,6 +429,13 @@ int iplink_main(int argc UNUSED_PARAM, char **argv) > return ip_do(do_iplink, argv); > } > #endif > +#if ENABLE_IPLINK_CAN > +int iplinkcan_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; > +int iplinkcan_main(int argc UNUSED_PARAM, char **argv) > +{ > + return ip_do(do_iplink, argv); > +} > +#endif > #if ENABLE_IPROUTE > int iproute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; > int iproute_main(int argc UNUSED_PARAM, char **argv) > diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c > index 9eb0b4f5f118..c3be54f841dd 100644 > --- a/networking/libiproute/iplink.c > +++ b/networking/libiproute/iplink.c > @@ -11,10 +11,17 @@ > #include <netinet/if_ether.h> > > #include <linux/if_vlan.h> > +#include <linux/can/netlink.h> > #include "ip_common.h" /* #include "libbb.h" is inside */ > #include "rt_names.h" > #include "utils.h" > > +#if ENABLE_FEATURE_IP_LINK_CAN > +#define ENABLE_FEATURE_IP_LINK_IFACE 1 > +#else > +#define ENABLE_FEATURE_IP_LINK_IFACE 0 > +#endif > + > #undef ETH_P_8021AD > #define ETH_P_8021AD 0x88A8 > #undef VLAN_FLAG_REORDER_HDR > @@ -28,6 +35,11 @@ > #undef IFLA_VLAN_PROTOCOL > #define IFLA_VLAN_PROTOCOL 5 > > +#ifndef NLMSG_TAIL > +#define NLMSG_TAIL(nmsg) \ > + ((struct rtattr *) (((void *) (nmsg)) + > NLMSG_ALIGN((nmsg)->nlmsg_len))) > +#endif > + > #ifndef IFLA_LINKINFO > # define IFLA_LINKINFO 18 > # define IFLA_INFO_KIND 1 > @@ -55,6 +67,13 @@ struct ifla_vlan_flags { > > #define str_on_off "on\0""off\0" > > +enum { > + PARM_on = 0, > + PARM_off > +}; > + > +typedef void FAST_FUNC(*ip_type_set_func_ptr_t)(char*, char**); > + > /* Exits on error */ > static int get_ctl_fd(void) > { > @@ -241,10 +260,261 @@ static void die_must_be_on_off(const char *msg) > bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", > msg); > } > > +#if ENABLE_FEATURE_IP_LINK_CAN > +static float FAST_FUNC get_float(char *arg, const char *errmsg) > +{ > + float ret; > + char *ptr; > + > + if (!arg || !*arg) > + invarg_1_to_2(arg, errmsg); /* does not return */ > + > + ret = strtof(arg, &ptr); > + if (!ptr || ptr == arg || *ptr) > + invarg_1_to_2(arg, errmsg); /* does not return */ > + > + return ret; > +} > + > +static void do_set_can(char *dev, char **argv) > +{ > + struct can_bittiming bt = {}, dbt = {}; > + struct can_ctrlmode cm = {}; > + char *keyword; > + static const char keywords[] ALIGN1 = > + "bitrate\0""sample-point\0""tq\0" > + "prop-seg\0""phase-seg1\0""phase-seg2\0""sjw\0" > + "dbitrate\0""dsample-point\0""dtq\0" > + "dprop-seg\0""dphase-seg1\0""dphase-seg2\0""dsjw\0" > + "loopback\0""listen-only\0""triple-sampling\0" > + "one-shot\0""berr-reporting\0" > + "fd\0""fd-non-iso\0""presume-ack\0" > + "cc-len8-dlc\0""restart\0""restart-ms\0" > + "termination\0"; > + enum { ARG_bitrate = 0, ARG_sample_point, ARG_tq, > + ARG_prop_seg, ARG_phase_seg1, ARG_phase_seg2, ARG_sjw, > + ARG_dbitrate, ARG_dsample_point, ARG_dtq, > + ARG_dprop_seg, ARG_dphase_seg1, ARG_dphase_seg2, ARG_dsjw, > + ARG_loopback, ARG_listen_only, ARG_triple_sampling, > + ARG_one_shot, ARG_berr_reporting, > + ARG_fd, ARG_fd_non_iso, ARG_presume_ack, > + ARG_cc_len8_dlc, ARG_restart, ARG_restart_ms, > + ARG_termination }; > + struct rtnl_handle rth; > + struct { > + struct nlmsghdr n; > + struct ifinfomsg i; > + char buf[1024]; > + } req; > + size_t dev_len; > + struct rtattr *linkinfo, *data; > + smalluint key, param; > + > + memset(&req, 0, sizeof(req)); > + > + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); > + req.n.nlmsg_flags = NLM_F_REQUEST; > + req.n.nlmsg_type = RTM_NEWLINK; > + req.i.ifi_family = preferred_family; > + xrtnl_open(&rth); > + req.i.ifi_index = xll_name_to_index(dev); > + dev_len = strlen(dev); > + if (dev_len < 2 || dev_len > IFNAMSIZ) > + invarg_1_to_2(dev, "dev"); > + > + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, dev, dev_len); > + linkinfo = NLMSG_TAIL(&req.n); > + addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); > + addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, (void *)"can", > + strlen("can")); > + data = NLMSG_TAIL(&req.n); > + addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); > + while (*argv) { > + key = index_in_substrings(keywords, *argv); > + keyword = *argv; > + //printf("%s: key: %d, *argv: %s\n", __func__, key, *argv); > + switch (key) { > + case ARG_bitrate: > + case ARG_tq: > + case ARG_prop_seg: > + case ARG_phase_seg1: > + case ARG_phase_seg2: > + case ARG_sjw: { > + __u32 *val; > + > + NEXT_ARG(); > + if (key == ARG_bitrate) > + val = &bt.bitrate; > + else if (key == ARG_tq) > + val = &bt.tq; > + else if (key == ARG_prop_seg) > + val = &bt.prop_seg; > + else if (key == ARG_phase_seg1) > + val = &bt.phase_seg1; > + else if (key == ARG_phase_seg2) > + val = &bt.phase_seg2; > + else > + val = &bt.sjw; > + > + *val = get_u32(*argv, keyword); > + break; > + } > + case ARG_sample_point: { > + float sp; > + > + NEXT_ARG(); > + sp = get_float(*argv, keyword); > + bt.sample_point = (__u32)(sp * 1000); > + break; > + } > + case ARG_dbitrate: > + case ARG_dtq: > + case ARG_dprop_seg: > + case ARG_dphase_seg1: > + case ARG_dphase_seg2: > + case ARG_dsjw: { > + __u32 *val; > + > + NEXT_ARG(); > + if (key == ARG_dbitrate) > + val = &dbt.bitrate; > + else if (key == ARG_dtq) > + val = &dbt.tq; > + else if (key == ARG_dprop_seg) > + val = &dbt.prop_seg; > + else if (key == ARG_dphase_seg1) > + val = &dbt.phase_seg1; > + else if (key == ARG_dphase_seg2) > + val = &dbt.phase_seg2; > + else > + val = &dbt.sjw; > + > + *val = get_u32(*argv, keyword); > + break; > + } > + case ARG_dsample_point: { > + float sp; > + > + NEXT_ARG(); > + sp = get_float(*argv, keyword); > + dbt.sample_point = (__u32)(sp * 1000); > + break; > + } > + case ARG_loopback: > + case ARG_listen_only: > + case ARG_triple_sampling: > + case ARG_one_shot: > + case ARG_berr_reporting: > + case ARG_fd: > + case ARG_fd_non_iso: > + case ARG_presume_ack: > + case ARG_cc_len8_dlc: { > + __u32 flag = 0; > + > + NEXT_ARG(); > + param = index_in_strings(str_on_off, *argv); > + if (param < 0) > + die_must_be_on_off(keyword); > + > + if (key == ARG_loopback) > + flag = CAN_CTRLMODE_LOOPBACK; > + else if (key == ARG_listen_only) > + flag = CAN_CTRLMODE_LISTENONLY; > + else if (key == ARG_triple_sampling) > + flag = CAN_CTRLMODE_3_SAMPLES; > + else if (key == ARG_one_shot) > + flag = CAN_CTRLMODE_ONE_SHOT; > + else if (key == ARG_berr_reporting) > + flag = CAN_CTRLMODE_BERR_REPORTING; > + else if (key == ARG_fd) > + flag = CAN_CTRLMODE_FD; > + else if (key == ARG_fd_non_iso) > + flag = CAN_CTRLMODE_FD_NON_ISO; > + else if (key == ARG_presume_ack) > + flag = CAN_CTRLMODE_PRESUME_ACK; > + else > +#if defined(CAN_CTRLMODE_CC_LEN8_DLC) > + flag = CAN_CTRLMODE_CC_LEN8_DLC; > +#else > + die_must_be_on_off(keyword); > +#endif > + cm.mask |= flag; > + if (param == PARM_on) > + cm.flags |= flag; > + > + break; > + } > + case ARG_restart: { > + __u32 val = 1; > + > + NEXT_ARG(); > + addattr_l(&req.n, sizeof(req), IFLA_CAN_RESTART, > &val, sizeof(val)); > + break; > + } > + case ARG_restart_ms: { > + __u32 val; > + > + NEXT_ARG(); > + val = get_u32(*argv, keyword); > + addattr_l(&req.n, sizeof(req), IFLA_CAN_RESTART_MS, > &val, sizeof(val)); > + break; > + } > + case ARG_termination: { > + __u16 val; > + > + NEXT_ARG(); > + val = get_u16(*argv, keyword); > + addattr_l(&req.n, sizeof(req), IFLA_CAN_TERMINATION, > &val, sizeof(val)); > + break; > + } > + default: > + break; > + } > + > + argv++; > + } > + > + if (bt.bitrate || bt.tq) > + addattr_l(&req.n, sizeof(req), IFLA_CAN_BITTIMING, &bt, > sizeof(bt)); > + > + if (cm.mask) > + addattr_l(&req.n, sizeof(req), IFLA_CAN_CTRLMODE, &cm, > sizeof(cm)); > + > + data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; > + linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo; > + > + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) > + xfunc_die(); > +} > +#endif > + > +#if ENABLE_FEATURE_IP_LINK_IFACE > +static void do_set_iface(char *type, char *dev, char **argv) > +{ > + static const char keywords[] ALIGN1 = "" > + IF_FEATURE_IP_LINK_CAN("can\0") > + ; > + static const ip_type_set_func_ptr_t funcs[] ALIGN_PTR = { > + IF_FEATURE_IP_LINK_CAN(do_set_can,) > + }; > + ip_type_set_func_ptr_t func; > + int key; > + > + key = index_in_substrings(keywords, type); > + if (key < 0) > + return; > + func = funcs[key]; > + func(dev, argv); > +} > +#endif > + > /* Return value becomes exitcode. It's okay to not return at all */ > static int do_set(char **argv) > { > char *dev = NULL; > +#if ENABLE_FEATURE_IP_LINK_IFACE > + char *type = NULL; > +#endif > uint32_t mask = 0; > uint32_t flags = 0; > int qlen = -1; > @@ -261,18 +531,24 @@ static int do_set(char **argv) > "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" > "arp\0""promisc\0""address\0""netns\0" > "master\0""nomaster\0" > +#if ENABLE_FEATURE_IP_LINK_IFACE > + "type\0" > +#endif > "dev\0" /* must be last */; > enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, > ARG_multicast, > ARG_arp, ARG_promisc, ARG_addr, ARG_netns, > ARG_master, ARG_nomaster, > +#if ENABLE_FEATURE_IP_LINK_IFACE > + ARG_type, > +#endif > ARG_dev }; > - enum { PARM_on = 0, PARM_off }; > smalluint key; > > while (*argv) { > /* substring search ensures that e.g. "addr" and "address" > * are both accepted */ > key = index_in_substrings(keywords, *argv); > + //printf("%s: key: %d, *argv: %s\n", __func__, key, *argv); > if (key == ARG_up) { > mask |= IFF_UP; > flags |= IFF_UP; > @@ -304,6 +580,13 @@ static int do_set(char **argv) > } else if (key == ARG_netns) { > NEXT_ARG(); > netns = get_unsigned(*argv, "netns"); > +#if ENABLE_FEATURE_IP_LINK_IFACE > + } else if (key == ARG_type) { > + NEXT_ARG(); > + type = *argv; > + argv++; > + break; > +#endif > } else if (key >= ARG_dev) { > /* ^^^^^^ ">=" here results in "dev IFACE" treated as > default */ > if (key == ARG_dev) { > @@ -311,6 +594,7 @@ static int do_set(char **argv) > } > if (dev) > duparg2("dev", *argv); > + > dev = *argv; > } else { > /* "on|off" options */ > @@ -496,6 +780,10 @@ static int do_set(char **argv) > } > if (mask) > do_chflags(dev, flags, mask); > +#if ENABLE_FEATURE_IP_LINK_IFACE > + if (type) > + do_set_iface(type, dev, argv); > +#endif > return 0; > } > > @@ -531,10 +819,6 @@ static void vlan_parse_opt(char **argv, struct nlmsghdr > *n, unsigned int size) > PROTO_8021Q = 0, > PROTO_8021AD, > }; > - enum { > - PARM_on = 0, > - PARM_off > - }; > int arg; > uint16_t id, proto; > struct ifla_vlan_flags flags = {}; > @@ -610,10 +894,6 @@ static void vrf_parse_opt(char **argv, struct nlmsghdr > *n, unsigned int size) > addattr_l(n, size, IFLA_VRF_TABLE, &table, sizeof(table)); > } > > -#ifndef NLMSG_TAIL > -#define NLMSG_TAIL(nmsg) \ > - ((struct rtattr *) (((void *) (nmsg)) + > NLMSG_ALIGN((nmsg)->nlmsg_len))) > -#endif > /* Return value becomes exitcode. It's okay to not return at all */ > static int do_add_or_delete(char **argv, const unsigned rtm) > { > -- > 2.42.0 > -- Dario Binacchi Senior Embedded Linux Developer dario.binac...@amarulasolutions.com __________________________________ Amarula Solutions SRL Via Le Canevare 30, 31100 Treviso, Veneto, IT T. +39 042 243 5310 i...@amarulasolutions.com www.amarulasolutions.com _______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox