From c37d4c67e85782001953a8bae8a9c65ccdcb2f88 Mon Sep 17 00:00:00 2001 From: Nigel Hathaway Date: Tue, 26 Apr 2011 02:38:29 +0200 Subject: dhcpc: export unrecognized options in "optNN=XXXXXXXXX" form function old new delta udhcp_run_script 609 818 +209 Signed-off-by: Nigel Hathaway Signed-off-by: Denys Vlasenko --- networking/udhcp/dhcpc.c | 57 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index ca82d37e6..510c3a1d0 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -300,6 +300,14 @@ static char **fill_envp(struct dhcp_packet *packet) uint8_t *temp; uint8_t overload = 0; +#define BITMAP unsigned +#define BBITS (sizeof(BITMAP) * 8) +#define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1))) +#define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS]) + BITMAP found_opts[256 / BBITS]; + + memset(found_opts, 0, sizeof(found_opts)); + /* We need 6 elements for: * "interface=IFACE" * "ip=N.N.N.N" from packet->yiaddr @@ -311,18 +319,21 @@ static char **fill_envp(struct dhcp_packet *packet) envc = 6; /* +1 element for each option, +2 for subnet option: */ if (packet) { - for (i = 0; dhcp_optflags[i].code; i++) { - if (udhcp_get_option(packet, dhcp_optflags[i].code)) { - if (dhcp_optflags[i].code == DHCP_SUBNET) + /* note: do not search for "pad" (0) and "end" (255) options */ + for (i = 1; i < 255; i++) { + temp = udhcp_get_option(packet, i); + if (temp) { + if (i == DHCP_OPTION_OVERLOAD) + overload = *temp; + else if (i == DHCP_SUBNET) envc++; /* for mton */ envc++; + /*if (i != DHCP_MESSAGE_TYPE)*/ + FOUND_OPTS(i) |= BMASK(i); } } - temp = udhcp_get_option(packet, DHCP_OPTION_OVERLOAD); - if (temp) - overload = *temp; } - curr = envp = xzalloc(sizeof(char *) * envc); + curr = envp = xzalloc(sizeof(envp[0]) * envc); *curr = xasprintf("interface=%s", client_config.interface); putenv(*curr++); @@ -337,12 +348,16 @@ static char **fill_envp(struct dhcp_packet *packet) opt_name = dhcp_option_strings; i = 0; while (*opt_name) { - temp = udhcp_get_option(packet, dhcp_optflags[i].code); - if (!temp) + uint8_t code = dhcp_optflags[i].code; + BITMAP *found_ptr = &FOUND_OPTS(code); + BITMAP found_mask = BMASK(code); + if (!(*found_ptr & found_mask)) goto next; + *found_ptr &= ~found_mask; /* leave only unknown options */ + temp = udhcp_get_option(packet, code); *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name); putenv(*curr++); - if (dhcp_optflags[i].code == DHCP_SUBNET) { + if (code == DHCP_SUBNET) { /* Subnet option: make things like "$ip/$mask" possible */ uint32_t subnet; move_from_unaligned32(subnet, temp); @@ -368,6 +383,28 @@ static char **fill_envp(struct dhcp_packet *packet) *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname); putenv(*curr++); } + /* Handle unknown options */ + for (i = 0; i < 256;) { + BITMAP bitmap = FOUND_OPTS(i); + if (!bitmap) { + i += BBITS; + continue; + } + if (bitmap & BMASK(i)) { + unsigned len, ofs; + + temp = udhcp_get_option(packet, i); + /* udhcp_get_option returns ptr to data portion, + * need to go back to get len + */ + len = temp[-OPT_DATA + OPT_LEN]; + *curr = xmalloc(sizeof("optNNN=") + 1 + len*2); + ofs = sprintf(*curr, "opt%u=", i); + bin2hex(*curr + ofs, (void*) temp, len)[0] = '\0'; + putenv(*curr++); + } + i++; + } return envp; } -- cgit v1.2.3-55-g6feb From fb6b173ff786648f5f43d23ff3c4f7721e9cb8b5 Mon Sep 17 00:00:00 2001 From: Nigel Hathaway Date: Tue, 26 Apr 2011 02:50:00 +0200 Subject: udhcp: add FEATURE_UDHCP_8021Q option function old new delta dhcp_option_strings 217 237 +20 dhcp_optflags 64 68 +4 Signed-off-by: Nigel Hathaway Signed-off-by: Denys Vlasenko --- examples/udhcp/udhcpd.conf | 9 +++++++++ networking/udhcp/Config.src | 8 ++++++++ networking/udhcp/common.c | 8 ++++++++ networking/udhcp/common.h | 2 ++ 4 files changed, 27 insertions(+) diff --git a/examples/udhcp/udhcpd.conf b/examples/udhcp/udhcpd.conf index 23fc834b7..43b49df89 100644 --- a/examples/udhcp/udhcpd.conf +++ b/examples/udhcp/udhcpd.conf @@ -90,6 +90,8 @@ option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4" #opt wpad STRING #opt serverid IP # default: server's IP #opt message STRING # error message (udhcpd sends it on success too) +#opt vlanid NUM # 802.1P VLAN ID +#opt vlanpriority NUM # 802.1Q VLAN priority # Options specifying server(s) #opt dns IP_LIST #opt wins IP_LIST @@ -97,8 +99,15 @@ option 0x08 01020304 # option 8: "cookie server IP addr: 1.2.3.4" #opt ntpsrv IP_LIST #opt lprsrv IP_LIST #opt swapsrv IP +# Options specifying routes +#opt routes IP_PAIR_LIST # Obsolete options, no longer supported #opt logsrv IP_LIST # 704/UDP log server (not syslog!) #opt namesrv IP_LIST # IEN 116 name server, obsolete (August 1979!!!) #opt cookiesrv IP_LIST # RFC 865 "quote of the day" server, rarely (never?) used #opt timesrv IP_LIST # RFC 868 time server, rarely (never?) used +# TODO: in development +#opt userclass STRING # RFC 3004. set of LASCII strings. "I am a printer" etc +#opt sipserv STRING LIST # RFC 3361. flag byte, then: 0: domain names, 1: IP addrs +#opt staticroutes STATIC_ROUTES +#opt msstaticroutes STATIC_ROUTES diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src index 9cd8cbbae..6bfa398ea 100644 --- a/networking/udhcp/Config.src +++ b/networking/udhcp/Config.src @@ -113,6 +113,14 @@ config FEATURE_UDHCP_RFC3397 search lists via option 119, specified in RFC 3397, and SIP servers option 120, specified in RFC 3361. +config FEATURE_UDHCP_8021Q + bool "Support for 802.1Q VLAN parameters" + default y + depends on UDHCPD || UDHCPC + help + If selected, both client and server will support passing of VLAN + ID and priority via options 132 and 133 as per 802.1Q. + config UDHCPC_DEFAULT_SCRIPT string "Absolute path to config script" default "/usr/share/udhcpc/default.script" diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c index 0a60261ab..70e34614c 100644 --- a/networking/udhcp/common.c +++ b/networking/udhcp/common.c @@ -55,6 +55,10 @@ const struct dhcp_optflag dhcp_optflags[] = { { OPTION_SIP_SERVERS , 0x78 }, /* DHCP_SIP_SERVERS */ #endif { OPTION_STATIC_ROUTES , 0x79 }, /* DHCP_STATIC_ROUTES */ +#if ENABLE_FEATURE_UDHCP_8021Q + { OPTION_U16 , 0x84 }, /* DHCP_VLAN_ID */ + { OPTION_U8 , 0x85 }, /* DHCP_VLAN_PRIORITY */ +#endif { OPTION_STATIC_ROUTES , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */ { OPTION_STRING , 0xfc }, /* DHCP_WPAD */ @@ -118,6 +122,10 @@ const char dhcp_option_strings[] ALIGN1 = // doesn't work in udhcpd.conf since OPTION_STATIC_ROUTES // is not handled yet by "string->option" conversion code: "staticroutes" "\0"/* DHCP_STATIC_ROUTES */ +#if ENABLE_FEATURE_UDHCP_8021Q + "vlanid" "\0" /* DHCP_VLAN_ID */ + "vlanpriority" "\0"/* DHCP_VLAN_PRIORITY */ +#endif "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */ "wpad" "\0" /* DHCP_WPAD */ ; diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h index f8f18ff01..ad6991c94 100644 --- a/networking/udhcp/common.h +++ b/networking/udhcp/common.h @@ -145,6 +145,8 @@ enum { //#define DHCP_DOMAIN_SEARCH 0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */ //#define DHCP_SIP_SERVERS 0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */ //#define DHCP_STATIC_ROUTES 0x79 /* RFC 3442. (mask,ip,router) tuples */ +#define DHCP_VLAN_ID 0x84 /* 802.1P VLAN ID */ +#define DHCP_VLAN_PRIORITY 0x85 /* 802.1Q VLAN priority */ //#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */ //#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */ #define DHCP_END 0xff -- cgit v1.2.3-55-g6feb From ab940af5c00fdf3fc46f1d073447cc1aed85efe7 Mon Sep 17 00:00:00 2001 From: Reuben Dowle Date: Mon, 4 Apr 2011 10:55:55 +1200 Subject: Fix ubirsvol usage information The usage information for the ubirsvol applet is incorrect. This fixes the mistake. Signed-off-by: Reuben Dowle Signed-off-by: Denys Vlasenko --- miscutils/ubi_attach_detach.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_attach_detach.c index 9007f8c3f..e82dc3848 100644 --- a/miscutils/ubi_attach_detach.c +++ b/miscutils/ubi_attach_detach.c @@ -102,11 +102,11 @@ //usage: "\n -n VOLID Volume ID" //usage: //usage:#define ubirsvol_trivial_usage -//usage: "UBI_DEVICE -N NAME -s SIZE" +//usage: "UBI_DEVICE -n VOLID -s SIZE" //usage:#define ubirsvol_full_usage "\n\n" //usage: "Resize UBI Volume\n" //usage: "\nOptions:" -//usage: "\n -N NAME Volume name" +//usage: "\n -n VOLID Volume ID to resize" //usage: "\n -s SIZE Size in bytes" int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -- cgit v1.2.3-55-g6feb From a6108423228e5dcd5f3feb31f68c6e46a8ee3123 Mon Sep 17 00:00:00 2001 From: Reuben Dowle Date: Tue, 26 Apr 2011 04:27:48 +0200 Subject: ubiupdatevol: new applet ubi_tools_main 658 1046 +388 packed_usage 28274 28304 +30 applet_names 2396 2409 +13 applet_main 1396 1400 +4 applet_nameofs 698 700 +2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 5/0 up/down: 437/0) Total: 437 bytes Signed-off-by: Reuben Dowle Signed-off-by: Denys Vlasenko --- miscutils/ubi_attach_detach.c | 69 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_attach_detach.c index e82dc3848..3f7f56e04 100644 --- a/miscutils/ubi_attach_detach.c +++ b/miscutils/ubi_attach_detach.c @@ -37,18 +37,27 @@ //config: select PLATFORM_LINUX //config: help //config: Resize a UBI volume. +//config: +//config:config UBIUPDATEVOL +//config: bool "ubiupdatevol" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Update a UBI volume. //applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach)) //applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach)) //applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol)) //applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) //applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) +//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol)) //kbuild:lib-$(CONFIG_UBIATTACH) += ubi_attach_detach.o //kbuild:lib-$(CONFIG_UBIDETACH) += ubi_attach_detach.o //kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_attach_detach.o //kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_attach_detach.o //kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_attach_detach.o +//kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_attach_detach.o #include "libbb.h" #include @@ -66,6 +75,7 @@ #define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') #define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') #define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') +#define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u') //usage:#define ubiattach_trivial_usage //usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" @@ -85,7 +95,7 @@ //usage:#define ubimkvol_trivial_usage //usage: "UBI_DEVICE -N NAME -s SIZE" //usage:#define ubimkvol_full_usage "\n\n" -//usage: "Create UBI Volume\n" +//usage: "Create UBI volume\n" //usage: "\nOptions:" //usage: "\n -a ALIGNMENT Volume alignment (default 1)" //usage: "\n -n VOLID Volume ID, if not specified, it" @@ -124,8 +134,8 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) int alignment = 1; char *type = NULL; - opt_complementary = "=1:m+:d+:n+:s+:a+"; - opts = getopt32(argv, "m:d:n:N:s:a:t:", + opt_complementary = "-1:m+:d+:n+:s+:a+"; + opts = getopt32(argv, "m:d:n:N:s:a:t::", &mtd_num, &dev_num, &vol_id, &vol_name, &size_bytes, &alignment, &type ); @@ -166,7 +176,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) memset(&req, 0, sizeof(req)); req.vol_id = vol_id; - if (opts & OPTION_t) { + if ((opts & OPTION_t) && type) { if (type[0] == 's') req.vol_type = UBI_STATIC_VOLUME; else @@ -199,6 +209,57 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv) req.vol_id = vol_id; xioctl(fd, UBI_IOCRSVOL, &req); + } else + if (do_update) { + long long bytes; + + if (opts & OPTION_t) { + // truncate the volume by starting an update for size 0 + bytes = 0; + xioctl(fd, UBI_IOCVOLUP, &bytes); + } + else { + struct stat st; + char buf[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3]; + int input_fd; + unsigned ubinum, volnum; + unsigned leb_size; + ssize_t len; + char *input_data; + + // Make assumption that device not is in normal format. + // Removes need for scanning sysfs tree as full libubi does + if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2) + bb_error_msg_and_die("%s volume node not in correct format", "UBI"); + + sprintf(buf, "/sys/class/ubi/ubi%u_%u/usable_eb_size", ubinum, volnum); + if (open_read_close(buf, buf, sizeof(buf)) <= 0) + bb_error_msg_and_die("%s could not get LEB size", "UBI"); + if (sscanf(buf, "%u", &leb_size) != 1) + bb_error_msg_and_die("%s could not get LEB size", "UBI"); + + if (opts & OPTION_s) { + input_fd = 0; + } else { + if (!argv[optind+1]) + bb_show_usage(); + xstat(argv[optind+1], &st); + size_bytes = st.st_size; + input_fd = xopen(argv[optind+1], O_RDONLY); + } + + bytes = size_bytes; + xioctl(fd, UBI_IOCVOLUP, &bytes); + + input_data = xmalloc(leb_size); + while ((len = full_read(input_fd, input_data, leb_size)) > 0) { + xwrite(fd, input_data, len); + } + if (len < 0) + bb_error_msg_and_die("%s volume update failed", "UBI"); + if (ENABLE_FEATURE_CLEAN_UP) + close(input_fd); + } } if (ENABLE_FEATURE_CLEAN_UP) -- cgit v1.2.3-55-g6feb From 0bfb9c2cf0e8b711634014e06d39b5f47c5a95c2 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 26 Apr 2011 04:31:03 +0200 Subject: add help text Signed-off-by: Denys Vlasenko --- miscutils/ubi_attach_detach.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_attach_detach.c index 3f7f56e04..162ee302d 100644 --- a/miscutils/ubi_attach_detach.c +++ b/miscutils/ubi_attach_detach.c @@ -107,17 +107,26 @@ //usage:#define ubirmvol_trivial_usage //usage: "UBI_DEVICE -n VOLID" //usage:#define ubirmvol_full_usage "\n\n" -//usage: "Remove UBI Volume\n" +//usage: "Remove UBI volume\n" //usage: "\nOptions:" //usage: "\n -n VOLID Volume ID" //usage: //usage:#define ubirsvol_trivial_usage //usage: "UBI_DEVICE -n VOLID -s SIZE" //usage:#define ubirsvol_full_usage "\n\n" -//usage: "Resize UBI Volume\n" +//usage: "Resize UBI volume\n" //usage: "\nOptions:" //usage: "\n -n VOLID Volume ID to resize" //usage: "\n -s SIZE Size in bytes" +//usage: +//usage:#define ubiupdatevol_trivial_usage +//usage: "UBI_DEVICE [IMG_FILE]" +//usage:#define ubiupdatevol_full_usage "\n\n" +//usage: "Update UBI volume\n" +//usage: "\nOptions:" +//usage: "\n -t Truncate UBI volume" +//usage: "\n -s SIZE Bytes in input (if reading stdin)" + int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ubi_tools_main(int argc UNUSED_PARAM, char **argv) -- cgit v1.2.3-55-g6feb From d1993f180cd4ba0992c559e7165ec4c220af4ca0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 26 Apr 2011 04:32:05 +0200 Subject: rename miscutils/ubi_attach_detach.c -> miscutils/ubi_tools.c Signed-off-by: Denys Vlasenko --- miscutils/ubi_attach_detach.c | 278 ------------------------------------------ miscutils/ubi_tools.c | 278 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+), 278 deletions(-) delete mode 100644 miscutils/ubi_attach_detach.c create mode 100644 miscutils/ubi_tools.c diff --git a/miscutils/ubi_attach_detach.c b/miscutils/ubi_attach_detach.c deleted file mode 100644 index 162ee302d..000000000 --- a/miscutils/ubi_attach_detach.c +++ /dev/null @@ -1,278 +0,0 @@ -/* Ported to busybox from mtd-utils. - * - * Licensed under GPLv2, see file LICENSE in this source tree. - */ - -//config:config UBIATTACH -//config: bool "ubiattach" -//config: default y -//config: select PLATFORM_LINUX -//config: help -//config: Attach MTD device to an UBI device. -//config: -//config:config UBIDETACH -//config: bool "ubidetach" -//config: default y -//config: select PLATFORM_LINUX -//config: help -//config: Detach MTD device from an UBI device. -//config: -//config:config UBIMKVOL -//config: bool "ubimkvol" -//config: default y -//config: select PLATFORM_LINUX -//config: help -//config: Create a UBI volume. -//config: -//config:config UBIRMVOL -//config: bool "ubirmvol" -//config: default y -//config: select PLATFORM_LINUX -//config: help -//config: Delete a UBI volume. -//config: -//config:config UBIRSVOL -//config: bool "ubirsvol" -//config: default y -//config: select PLATFORM_LINUX -//config: help -//config: Resize a UBI volume. -//config: -//config:config UBIUPDATEVOL -//config: bool "ubiupdatevol" -//config: default y -//config: select PLATFORM_LINUX -//config: help -//config: Update a UBI volume. - -//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach)) -//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach)) -//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol)) -//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) -//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) -//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol)) - -//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_attach_detach.o -//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_attach_detach.o -//kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_attach_detach.o -//kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_attach_detach.o -//kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_attach_detach.o -//kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_attach_detach.o - -#include "libbb.h" -#include - -#define OPTION_M (1 << 0) -#define OPTION_D (1 << 1) -#define OPTION_n (1 << 2) -#define OPTION_N (1 << 3) -#define OPTION_s (1 << 4) -#define OPTION_a (1 << 5) -#define OPTION_t (1 << 6) - -#define do_attach (ENABLE_UBIATTACH && applet_name[3] == 'a') -#define do_detach (ENABLE_UBIDETACH && applet_name[3] == 'd') -#define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') -#define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') -#define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') -#define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u') - -//usage:#define ubiattach_trivial_usage -//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" -//usage:#define ubiattach_full_usage "\n\n" -//usage: "Attach MTD device to UBI\n" -//usage: "\nOptions:" -//usage: "\n -m MTD_NUM MTD device number to attach" -//usage: "\n -d UBI_NUM UBI device number to assign" -//usage: -//usage:#define ubidetach_trivial_usage -//usage: "-d UBI_NUM UBI_CTRL_DEV" -//usage:#define ubidetach_full_usage "\n\n" -//usage: "Detach MTD device from UBI\n" -//usage: "\nOptions:" -//usage: "\n -d UBI_NUM UBI device number" -//usage: -//usage:#define ubimkvol_trivial_usage -//usage: "UBI_DEVICE -N NAME -s SIZE" -//usage:#define ubimkvol_full_usage "\n\n" -//usage: "Create UBI volume\n" -//usage: "\nOptions:" -//usage: "\n -a ALIGNMENT Volume alignment (default 1)" -//usage: "\n -n VOLID Volume ID, if not specified, it" -//usage: "\n will be assigned automatically" -//usage: "\n -N NAME Volume name" -//usage: "\n -s SIZE Size in bytes" -//usage: "\n -t TYPE Volume type (static|dynamic)" -//usage: -//usage:#define ubirmvol_trivial_usage -//usage: "UBI_DEVICE -n VOLID" -//usage:#define ubirmvol_full_usage "\n\n" -//usage: "Remove UBI volume\n" -//usage: "\nOptions:" -//usage: "\n -n VOLID Volume ID" -//usage: -//usage:#define ubirsvol_trivial_usage -//usage: "UBI_DEVICE -n VOLID -s SIZE" -//usage:#define ubirsvol_full_usage "\n\n" -//usage: "Resize UBI volume\n" -//usage: "\nOptions:" -//usage: "\n -n VOLID Volume ID to resize" -//usage: "\n -s SIZE Size in bytes" -//usage: -//usage:#define ubiupdatevol_trivial_usage -//usage: "UBI_DEVICE [IMG_FILE]" -//usage:#define ubiupdatevol_full_usage "\n\n" -//usage: "Update UBI volume\n" -//usage: "\nOptions:" -//usage: "\n -t Truncate UBI volume" -//usage: "\n -s SIZE Bytes in input (if reading stdin)" - - -int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int ubi_tools_main(int argc UNUSED_PARAM, char **argv) -{ - unsigned opts; - char *ubi_ctrl; - //struct stat st; - int fd; - int mtd_num; - int dev_num = UBI_DEV_NUM_AUTO; - int vol_id = UBI_VOL_NUM_AUTO; - char *vol_name = NULL; - int size_bytes; - int alignment = 1; - char *type = NULL; - - opt_complementary = "-1:m+:d+:n+:s+:a+"; - opts = getopt32(argv, "m:d:n:N:s:a:t::", - &mtd_num, &dev_num, &vol_id, - &vol_name, &size_bytes, &alignment, &type - ); - ubi_ctrl = argv[optind]; - - fd = xopen(ubi_ctrl, O_RDWR); - //xfstat(fd, &st, ubi_ctrl); - //if (!S_ISCHR(st.st_mode)) - // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); - - if (do_attach) { - struct ubi_attach_req req; - if (!(opts & OPTION_M)) - bb_error_msg_and_die("%s device not specified", "MTD"); - - memset(&req, 0, sizeof(req)); - req.mtd_num = mtd_num; - req.ubi_num = dev_num; - - xioctl(fd, UBI_IOCATT, &req); - } else - if (do_detach) { - if (!(opts & OPTION_D)) - bb_error_msg_and_die("%s device not specified", "UBI"); - - xioctl(fd, UBI_IOCDET, &dev_num); - } else - if (do_mkvol) { - struct ubi_mkvol_req req; - int vol_name_len; - if (!(opts & OPTION_s)) - bb_error_msg_and_die("%s size not specified", "UBI"); - if (!(opts & OPTION_N)) - bb_error_msg_and_die("%s name not specified", "UBI"); - vol_name_len = strlen(vol_name); - if (vol_name_len > UBI_MAX_VOLUME_NAME) - bb_error_msg_and_die("%s volume name too long", "UBI"); - - memset(&req, 0, sizeof(req)); - req.vol_id = vol_id; - if ((opts & OPTION_t) && type) { - if (type[0] == 's') - req.vol_type = UBI_STATIC_VOLUME; - else - req.vol_type = UBI_DYNAMIC_VOLUME; - } else { - req.vol_type = UBI_DYNAMIC_VOLUME; - } - req.alignment = alignment; - req.bytes = size_bytes; - strncpy(req.name, vol_name, UBI_MAX_VOLUME_NAME); - req.name_len = vol_name_len; - - xioctl(fd, UBI_IOCMKVOL, &req); - } else - if (do_rmvol) { - if (!(opts & OPTION_n)) - bb_error_msg_and_die("%s volume id not specified", "UBI"); - - xioctl(fd, UBI_IOCRMVOL, &vol_id); - } else - if (do_rsvol) { - struct ubi_rsvol_req req; - if (!(opts & OPTION_s)) - bb_error_msg_and_die("%s size not specified", "UBI"); - if (!(opts & OPTION_n)) - bb_error_msg_and_die("%s volume id not specified", "UBI"); - - memset(&req, 0, sizeof(req)); - req.bytes = size_bytes; - req.vol_id = vol_id; - - xioctl(fd, UBI_IOCRSVOL, &req); - } else - if (do_update) { - long long bytes; - - if (opts & OPTION_t) { - // truncate the volume by starting an update for size 0 - bytes = 0; - xioctl(fd, UBI_IOCVOLUP, &bytes); - } - else { - struct stat st; - char buf[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3]; - int input_fd; - unsigned ubinum, volnum; - unsigned leb_size; - ssize_t len; - char *input_data; - - // Make assumption that device not is in normal format. - // Removes need for scanning sysfs tree as full libubi does - if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2) - bb_error_msg_and_die("%s volume node not in correct format", "UBI"); - - sprintf(buf, "/sys/class/ubi/ubi%u_%u/usable_eb_size", ubinum, volnum); - if (open_read_close(buf, buf, sizeof(buf)) <= 0) - bb_error_msg_and_die("%s could not get LEB size", "UBI"); - if (sscanf(buf, "%u", &leb_size) != 1) - bb_error_msg_and_die("%s could not get LEB size", "UBI"); - - if (opts & OPTION_s) { - input_fd = 0; - } else { - if (!argv[optind+1]) - bb_show_usage(); - xstat(argv[optind+1], &st); - size_bytes = st.st_size; - input_fd = xopen(argv[optind+1], O_RDONLY); - } - - bytes = size_bytes; - xioctl(fd, UBI_IOCVOLUP, &bytes); - - input_data = xmalloc(leb_size); - while ((len = full_read(input_fd, input_data, leb_size)) > 0) { - xwrite(fd, input_data, len); - } - if (len < 0) - bb_error_msg_and_die("%s volume update failed", "UBI"); - if (ENABLE_FEATURE_CLEAN_UP) - close(input_fd); - } - } - - if (ENABLE_FEATURE_CLEAN_UP) - close(fd); - - return EXIT_SUCCESS; -} diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c new file mode 100644 index 000000000..fc7f38c5d --- /dev/null +++ b/miscutils/ubi_tools.c @@ -0,0 +1,278 @@ +/* Ported to busybox from mtd-utils. + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ + +//config:config UBIATTACH +//config: bool "ubiattach" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Attach MTD device to an UBI device. +//config: +//config:config UBIDETACH +//config: bool "ubidetach" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Detach MTD device from an UBI device. +//config: +//config:config UBIMKVOL +//config: bool "ubimkvol" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Create a UBI volume. +//config: +//config:config UBIRMVOL +//config: bool "ubirmvol" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Delete a UBI volume. +//config: +//config:config UBIRSVOL +//config: bool "ubirsvol" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Resize a UBI volume. +//config: +//config:config UBIUPDATEVOL +//config: bool "ubiupdatevol" +//config: default y +//config: select PLATFORM_LINUX +//config: help +//config: Update a UBI volume. + +//applet:IF_UBIATTACH(APPLET_ODDNAME(ubiattach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiattach)) +//applet:IF_UBIDETACH(APPLET_ODDNAME(ubidetach, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubidetach)) +//applet:IF_UBIMKVOL(APPLET_ODDNAME(ubimkvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubimkvol)) +//applet:IF_UBIRMVOL(APPLET_ODDNAME(ubirmvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirmvol)) +//applet:IF_UBIRSVOL(APPLET_ODDNAME(ubirsvol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubirsvol)) +//applet:IF_UBIUPDATEVOL(APPLET_ODDNAME(ubiupdatevol, ubi_tools, BB_DIR_USR_SBIN, BB_SUID_DROP, ubiupdatevol)) + +//kbuild:lib-$(CONFIG_UBIATTACH) += ubi_tools.o +//kbuild:lib-$(CONFIG_UBIDETACH) += ubi_tools.o +//kbuild:lib-$(CONFIG_UBIMKVOL) += ubi_tools.o +//kbuild:lib-$(CONFIG_UBIRMVOL) += ubi_tools.o +//kbuild:lib-$(CONFIG_UBIRSVOL) += ubi_tools.o +//kbuild:lib-$(CONFIG_UBIUPDATEVOL) += ubi_tools.o + +#include "libbb.h" +#include + +#define OPTION_M (1 << 0) +#define OPTION_D (1 << 1) +#define OPTION_n (1 << 2) +#define OPTION_N (1 << 3) +#define OPTION_s (1 << 4) +#define OPTION_a (1 << 5) +#define OPTION_t (1 << 6) + +#define do_attach (ENABLE_UBIATTACH && applet_name[3] == 'a') +#define do_detach (ENABLE_UBIDETACH && applet_name[3] == 'd') +#define do_mkvol (ENABLE_UBIMKVOL && applet_name[3] == 'm') +#define do_rmvol (ENABLE_UBIRMVOL && applet_name[4] == 'm') +#define do_rsvol (ENABLE_UBIRSVOL && applet_name[4] == 's') +#define do_update (ENABLE_UBIUPDATEVOL && applet_name[3] == 'u') + +//usage:#define ubiattach_trivial_usage +//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV" +//usage:#define ubiattach_full_usage "\n\n" +//usage: "Attach MTD device to UBI\n" +//usage: "\nOptions:" +//usage: "\n -m MTD_NUM MTD device number to attach" +//usage: "\n -d UBI_NUM UBI device number to assign" +//usage: +//usage:#define ubidetach_trivial_usage +//usage: "-d UBI_NUM UBI_CTRL_DEV" +//usage:#define ubidetach_full_usage "\n\n" +//usage: "Detach MTD device from UBI\n" +//usage: "\nOptions:" +//usage: "\n -d UBI_NUM UBI device number" +//usage: +//usage:#define ubimkvol_trivial_usage +//usage: "UBI_DEVICE -N NAME -s SIZE" +//usage:#define ubimkvol_full_usage "\n\n" +//usage: "Create UBI volume\n" +//usage: "\nOptions:" +//usage: "\n -a ALIGNMENT Volume alignment (default 1)" +//usage: "\n -n VOLID Volume ID, if not specified, it" +//usage: "\n will be assigned automatically" +//usage: "\n -N NAME Volume name" +//usage: "\n -s SIZE Size in bytes" +//usage: "\n -t TYPE Volume type (static|dynamic)" +//usage: +//usage:#define ubirmvol_trivial_usage +//usage: "UBI_DEVICE -n VOLID" +//usage:#define ubirmvol_full_usage "\n\n" +//usage: "Remove UBI volume\n" +//usage: "\nOptions:" +//usage: "\n -n VOLID Volume ID" +//usage: +//usage:#define ubirsvol_trivial_usage +//usage: "UBI_DEVICE -n VOLID -s SIZE" +//usage:#define ubirsvol_full_usage "\n\n" +//usage: "Resize UBI volume\n" +//usage: "\nOptions:" +//usage: "\n -n VOLID Volume ID to resize" +//usage: "\n -s SIZE Size in bytes" +//usage: +//usage:#define ubiupdatevol_trivial_usage +//usage: "UBI_DEVICE [IMG_FILE]" +//usage:#define ubiupdatevol_full_usage "\n\n" +//usage: "Update UBI volume\n" +//usage: "\nOptions:" +//usage: "\n -t Truncate UBI volume" +//usage: "\n -s SIZE Bytes in input (if reading stdin)" + + +int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ubi_tools_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned opts; + char *ubi_ctrl; + //struct stat st; + int fd; + int mtd_num; + int dev_num = UBI_DEV_NUM_AUTO; + int vol_id = UBI_VOL_NUM_AUTO; + char *vol_name = NULL; + int size_bytes; + int alignment = 1; + char *type = NULL; + + opt_complementary = "-1:m+:d+:n+:s+:a+"; + opts = getopt32(argv, "m:d:n:N:s:a:t::", + &mtd_num, &dev_num, &vol_id, + &vol_name, &size_bytes, &alignment, &type + ); + ubi_ctrl = argv[optind]; + + fd = xopen(ubi_ctrl, O_RDWR); + //xfstat(fd, &st, ubi_ctrl); + //if (!S_ISCHR(st.st_mode)) + // bb_error_msg_and_die("%s: not a char device", ubi_ctrl); + + if (do_attach) { + struct ubi_attach_req req; + if (!(opts & OPTION_M)) + bb_error_msg_and_die("%s device not specified", "MTD"); + + memset(&req, 0, sizeof(req)); + req.mtd_num = mtd_num; + req.ubi_num = dev_num; + + xioctl(fd, UBI_IOCATT, &req); + } else + if (do_detach) { + if (!(opts & OPTION_D)) + bb_error_msg_and_die("%s device not specified", "UBI"); + + xioctl(fd, UBI_IOCDET, &dev_num); + } else + if (do_mkvol) { + struct ubi_mkvol_req req; + int vol_name_len; + if (!(opts & OPTION_s)) + bb_error_msg_and_die("%s size not specified", "UBI"); + if (!(opts & OPTION_N)) + bb_error_msg_and_die("%s name not specified", "UBI"); + vol_name_len = strlen(vol_name); + if (vol_name_len > UBI_MAX_VOLUME_NAME) + bb_error_msg_and_die("%s volume name too long", "UBI"); + + memset(&req, 0, sizeof(req)); + req.vol_id = vol_id; + if ((opts & OPTION_t) && type) { + if (type[0] == 's') + req.vol_type = UBI_STATIC_VOLUME; + else + req.vol_type = UBI_DYNAMIC_VOLUME; + } else { + req.vol_type = UBI_DYNAMIC_VOLUME; + } + req.alignment = alignment; + req.bytes = size_bytes; + strncpy(req.name, vol_name, UBI_MAX_VOLUME_NAME); + req.name_len = vol_name_len; + + xioctl(fd, UBI_IOCMKVOL, &req); + } else + if (do_rmvol) { + if (!(opts & OPTION_n)) + bb_error_msg_and_die("%s volume id not specified", "UBI"); + + xioctl(fd, UBI_IOCRMVOL, &vol_id); + } else + if (do_rsvol) { + struct ubi_rsvol_req req; + if (!(opts & OPTION_s)) + bb_error_msg_and_die("%s size not specified", "UBI"); + if (!(opts & OPTION_n)) + bb_error_msg_and_die("%s volume id not specified", "UBI"); + + memset(&req, 0, sizeof(req)); + req.bytes = size_bytes; + req.vol_id = vol_id; + + xioctl(fd, UBI_IOCRSVOL, &req); + } else + if (do_update) { + long long bytes; + + if (opts & OPTION_t) { + // truncate the volume by starting an update for size 0 + bytes = 0; + xioctl(fd, UBI_IOCVOLUP, &bytes); + } + else { + struct stat st; + char buf[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3]; + int input_fd; + unsigned ubinum, volnum; + unsigned leb_size; + ssize_t len; + char *input_data; + + // Make assumption that device not is in normal format. + // Removes need for scanning sysfs tree as full libubi does + if (sscanf(ubi_ctrl, "/dev/ubi%u_%u", &ubinum, &volnum) != 2) + bb_error_msg_and_die("%s volume node not in correct format", "UBI"); + + sprintf(buf, "/sys/class/ubi/ubi%u_%u/usable_eb_size", ubinum, volnum); + if (open_read_close(buf, buf, sizeof(buf)) <= 0) + bb_error_msg_and_die("%s could not get LEB size", "UBI"); + if (sscanf(buf, "%u", &leb_size) != 1) + bb_error_msg_and_die("%s could not get LEB size", "UBI"); + + if (opts & OPTION_s) { + input_fd = 0; + } else { + if (!argv[optind+1]) + bb_show_usage(); + xstat(argv[optind+1], &st); + size_bytes = st.st_size; + input_fd = xopen(argv[optind+1], O_RDONLY); + } + + bytes = size_bytes; + xioctl(fd, UBI_IOCVOLUP, &bytes); + + input_data = xmalloc(leb_size); + while ((len = full_read(input_fd, input_data, leb_size)) > 0) { + xwrite(fd, input_data, len); + } + if (len < 0) + bb_error_msg_and_die("%s volume update failed", "UBI"); + if (ENABLE_FEATURE_CLEAN_UP) + close(input_fd); + } + } + + if (ENABLE_FEATURE_CLEAN_UP) + close(fd); + + return EXIT_SUCCESS; +} -- cgit v1.2.3-55-g6feb From 9cfcc4d7a2b1dd9bdcda392aa43009b949c56c92 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Sat, 30 Apr 2011 21:31:05 +0200 Subject: fdisk: backport disk check from util-linux With the digit check devices like mmcblk0 were skipped, but now with 0 allowed we're seeing a ton of loop devices listed (loop0, loop10, loop20...) as well as ramzswap0, all which should not be shown in fdisk -l. function old new delta list_devs_in_proc_partititons 157 238 +81 Signed-off-by: Lauri Kasanen Signed-off-by: Denys Vlasenko --- util-linux/fdisk.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c index da03e683e..f4fd4d31d 100644 --- a/util-linux/fdisk.c +++ b/util-linux/fdisk.c @@ -2846,13 +2846,37 @@ open_list_and_close(const char *device, int user_specified) close_dev_fd(); } +/* Is it a whole disk? The digit check is still useful + for Xen devices for example. */ +static int is_whole_disk(const char *disk) +{ + unsigned len; + int fd = open(disk, O_RDONLY); + + if (fd != -1) { + struct hd_geometry geometry; + int err = ioctl(fd, HDIO_GETGEO, &geometry); + close(fd); + if (!err) + return (geometry.start == 0); + } + + /* Treat "nameN" as a partition name, not whole disk */ + /* note: mmcblk0 should work from the geometry check above */ + len = strlen(disk); + if (len != 0 && isdigit(disk[len - 1])) + return 0; + + return 1; +} + /* for fdisk -l: try all things in /proc/partitions that look like a partition name (do not end in a digit) */ static void list_devs_in_proc_partititons(void) { FILE *procpt; - char line[100], ptname[100], devname[120], *s; + char line[100], ptname[100], devname[120]; int ma, mi, sz; procpt = fopen_or_warn("/proc/partitions", "r"); @@ -2861,13 +2885,10 @@ list_devs_in_proc_partititons(void) if (sscanf(line, " %u %u %u %[^\n ]", &ma, &mi, &sz, ptname) != 4) continue; - for (s = ptname; *s; s++) - continue; - /* note: excluding '0': e.g. mmcblk0 is not a partition name! */ - if (s[-1] >= '1' && s[-1] <= '9') - continue; + sprintf(devname, "/dev/%s", ptname); - open_list_and_close(devname, 0); + if (is_whole_disk(devname)) + open_list_and_close(devname, 0); } #if ENABLE_FEATURE_CLEAN_UP fclose(procpt); -- cgit v1.2.3-55-g6feb From 2a6d5988b4aabc9d5cbb466ab875db20104a5056 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Sat, 30 Apr 2011 22:09:01 +0200 Subject: mpstat.c: remove unused line Signed-off-by: Marek Polacek Signed-off-by: Denys Vlasenko --- procps/mpstat.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/procps/mpstat.c b/procps/mpstat.c index d643c999f..da8f34dab 100644 --- a/procps/mpstat.c +++ b/procps/mpstat.c @@ -36,11 +36,10 @@ * We are printing headers in the " IRQNAME/s" form, experimentally * anything smaller than 10 chars looks ugly for /proc/softirqs stats. */ -#define INTRATE_SCRWIDTH 10 +#define INTRATE_SCRWIDTH 10 #define INTRATE_SCRWIDTH_STR "10" /* System files */ -#define SYSFS_DEVCPU "/sys/devices/system/cpu" #define PROCFS_STAT "/proc/stat" #define PROCFS_INTERRUPTS "/proc/interrupts" #define PROCFS_SOFTIRQS "/proc/softirqs" -- cgit v1.2.3-55-g6feb From 2cfb57647b072900b5e66977c500c6ee0978dd0e Mon Sep 17 00:00:00 2001 From: Marek Bečka Date: Sun, 1 May 2011 03:09:14 +0200 Subject: setserial: new applet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit text data bss dec hex filename 873605 493 7584 881682 d7412 busybox_old 876354 493 7584 884431 d7ecf busybox_unstripped Signed-off-by: Marek Bečka Signed-off-by: Denys Vlasenko --- miscutils/setserial.c | 763 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 763 insertions(+) create mode 100644 miscutils/setserial.c diff --git a/miscutils/setserial.c b/miscutils/setserial.c new file mode 100644 index 000000000..501beaacc --- /dev/null +++ b/miscutils/setserial.c @@ -0,0 +1,763 @@ +/* vi: set sw=4 ts=4: */ +/* + * setserial implementation for busybox + * + * + * Copyright (C) 2011 Marek Bečka + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ + +//config:config SETSERIAL +//config: bool "setserial" +//config: default y +//config: depends on PLATFORM_LINUX +//config: help +//config: Retrieve or set Linux serial port. + +//applet:IF_SETSERIAL(APPLET(setserial, BB_DIR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_SETSERIAL) += setserial.o + +#include "libbb.h" +#include + +#ifndef PORT_UNKNOWN +# define PORT_UNKNOWN 0 +#endif +#ifndef PORT_8250 +# define PORT_8250 1 +#endif +#ifndef PORT_16450 +# define PORT_16450 2 +#endif +#ifndef PORT_16550 +# define PORT_16550 3 +#endif +#ifndef PORT_16550A +# define PORT_16550A 4 +#endif +#ifndef PORT_CIRRUS +# define PORT_CIRRUS 5 +#endif +#ifndef PORT_16650 +# define PORT_16650 6 +#endif +#ifndef PORT_16650V2 +# define PORT_16650V2 7 +#endif +#ifndef PORT_16750 +# define PORT_16750 8 +#endif +#ifndef PORT_STARTECH +# define PORT_STARTECH 9 +#endif +#ifndef PORT_16C950 +# define PORT_16C950 10 +#endif +#ifndef PORT_16654 +# define PORT_16654 11 +#endif +#ifndef PORT_16850 +# define PORT_16850 12 +#endif +#ifndef PORT_RSA +# define PORT_RSA 13 +#endif +#ifndef PORT_NS16550A +# define PORT_NS16550A 14 +#endif +#ifndef PORT_XSCALE +# define PORT_XSCALE 15 +#endif +#ifndef PORT_RM9000 +# define PORT_RM9000 16 +#endif +#ifndef PORT_OCTEON +# define PORT_OCTEON 17 +#endif +#ifndef PORT_AR7 +# define PORT_AR7 18 +#endif +#ifndef PORT_U6_16550A +# define PORT_U6_16550A 19 +#endif + +#ifndef ASYNCB_HUP_NOTIFY +# define ASYNCB_HUP_NOTIFY 0 +#endif +#ifndef ASYNCB_FOURPORT +# define ASYNCB_FOURPORT 1 +#endif +#ifndef ASYNCB_SAK +# define ASYNCB_SAK 2 +#endif +#ifndef ASYNCB_SPLIT_TERMIOS +# define ASYNCB_SPLIT_TERMIOS 3 +#endif +#ifndef ASYNCB_SPD_HI +# define ASYNCB_SPD_HI 4 +#endif +#ifndef ASYNCB_SPD_VHI +# define ASYNCB_SPD_VHI 5 +#endif +#ifndef ASYNCB_SKIP_TEST +# define ASYNCB_SKIP_TEST 6 +#endif +#ifndef ASYNCB_AUTO_IRQ +# define ASYNCB_AUTO_IRQ 7 +#endif +#ifndef ASYNCB_SESSION_LOCKOUT +# define ASYNCB_SESSION_LOCKOUT 8 +#endif +#ifndef ASYNCB_PGRP_LOCKOUT +# define ASYNCB_PGRP_LOCKOUT 9 +#endif +#ifndef ASYNCB_CALLOUT_NOHUP +# define ASYNCB_CALLOUT_NOHUP 10 +#endif +#ifndef ASYNCB_SPD_SHI +# define ASYNCB_SPD_SHI 12 +#endif +#ifndef ASYNCB_LOW_LATENCY +# define ASYNCB_LOW_LATENCY 13 +#endif +#ifndef ASYNCB_BUGGY_UART +# define ASYNCB_BUGGY_UART 14 +#endif + +#ifndef ASYNC_HUP_NOTIFY +# define ASYNC_HUP_NOTIFY (1U << ASYNCB_HUP_NOTIFY) +#endif +#ifndef ASYNC_FOURPORT +# define ASYNC_FOURPORT (1U << ASYNCB_FOURPORT) +#endif +#ifndef ASYNC_SAK +# define ASYNC_SAK (1U << ASYNCB_SAK) +#endif +#ifndef ASYNC_SPLIT_TERMIOS +# define ASYNC_SPLIT_TERMIOS (1U << ASYNCB_SPLIT_TERMIOS) +#endif +#ifndef ASYNC_SPD_HI +# define ASYNC_SPD_HI (1U << ASYNCB_SPD_HI) +#endif +#ifndef ASYNC_SPD_VHI +# define ASYNC_SPD_VHI (1U << ASYNCB_SPD_VHI) +#endif +#ifndef ASYNC_SKIP_TEST +# define ASYNC_SKIP_TEST (1U << ASYNCB_SKIP_TEST) +#endif +#ifndef ASYNC_AUTO_IRQ +# define ASYNC_AUTO_IRQ (1U << ASYNCB_AUTO_IRQ) +#endif +#ifndef ASYNC_SESSION_LOCKOUT +# define ASYNC_SESSION_LOCKOUT (1U << ASYNCB_SESSION_LOCKOUT) +#endif +#ifndef ASYNC_PGRP_LOCKOUT +# define ASYNC_PGRP_LOCKOUT (1U << ASYNCB_PGRP_LOCKOUT) +#endif +#ifndef ASYNC_CALLOUT_NOHUP +# define ASYNC_CALLOUT_NOHUP (1U << ASYNCB_CALLOUT_NOHUP) +#endif +#ifndef ASYNC_SPD_SHI +# define ASYNC_SPD_SHI (1U << ASYNCB_SPD_SHI) +#endif +#ifndef ASYNC_LOW_LATENCY +# define ASYNC_LOW_LATENCY (1U << ASYNCB_LOW_LATENCY) +#endif +#ifndef ASYNC_BUGGY_UART +# define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART) +#endif + +#ifndef ASYNC_SPD_CUST +# define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI) +#endif +#ifndef ASYNC_SPD_WARP +# define ASYNC_SPD_WARP (ASYNC_SPD_HI|ASYNC_SPD_SHI) +#endif +#ifndef ASYNC_SPD_MASK +# define ASYNC_SPD_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI|ASYNC_SPD_SHI) +#endif + +#ifndef ASYNC_CLOSING_WAIT_INF +# define ASYNC_CLOSING_WAIT_INF 0 +#endif +#ifndef ASYNC_CLOSING_WAIT_NONE +# define ASYNC_CLOSING_WAIT_NONE 65535 +#endif + +#ifndef _LINUX_SERIAL_H +struct serial_struct { + int type; + int line; + unsigned int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char io_type; + char reserved_char[1]; + int hub6; + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + unsigned char *iomem_base; + unsigned short iomem_reg_shift; + unsigned int port_high; + unsigned long iomap_base; /* cookie passed into ioremap */ +}; +#endif + +//usage:#define setserial_trivial_usage +//usage: "[-gabGvzV] DEVICE [PARAMETER [ARG]]..." +//usage:#define setserial_full_usage "\n\n" +//usage: "Request or set Linux serial port information\n\n" +//usage: "Options:\n" +//usage: " -g Interpret parameters as list of devices for reporting" +//usage: " -a Print all available information\n" +//usage: " -b Print summary information\n" +//usage: " -G Print in form which can be fed back\n" +//usage: " to setserial as command line parameters\n" +//usage: " -z Zero out serial flags before setting\n" +//usage: " -v Verbose\n" +//usage: "\n" +//usage: "Parameters: (* = takes an argument, ^ = can be turned off by preceding ^)\n" +//usage: " *port, *irq, *divisor, *uart, *baund_base, *close_delay, *closing_wait,\n" +//usage: " ^fourport, ^auto_irq, ^skip_test, ^sak, ^session_lockout, ^pgrp_lockout,\n" +//usage: " ^callout_nohup, ^split_termios, ^hup_notify, ^low_latency, autoconfig,\n" +//usage: " spd_normal, spd_hi, spd_vhi, spd_shi, spd_warp, spd_cust\n" +//usage: "\n" +//usage: "UART types:\n" +//usage: " unknown, 8250, 16450, 16550, 16550A, Cirrus, 16650, 16650V2, 16750,\n" +//usage: " 16950, 16954, 16654, 16850, RSA, NS16550A, XSCALE, RM9000, OCTEON, AR7,\n" +//usage: " U6_16550A" + +#define OPT_PRINT_SUMMARY (1 << 0) +#define OPT_PRINT_FEDBACK (1 << 1) +#define OPT_PRINT_ALL (1 << 2) +#define OPT_VERBOSE (1 << 3) +#define OPT_ZERO (1 << 4) +#define OPT_GET (1 << 5) + +#define OPT_MODE_MASK \ + (OPT_PRINT_ALL | OPT_PRINT_SUMMARY | OPT_PRINT_FEDBACK) + +enum print_mode +{ + PRINT_NORMAL = 0, + PRINT_SUMMARY = (1 << 0), + PRINT_FEDBACK = (1 << 1), + PRINT_ALL = (1 << 2), +}; + +#define CTL_SET (1 << 0) +#define CTL_CONFIG (1 << 1) +#define CTL_GET (1 << 2) +#define CTL_CLOSE (1 << 3) +#define CTL_NODIE (1 << 4) + +static const char serial_types[] = + "unknown\0" /* 0 */ + "8250\0" /* 1 */ + "16450\0" /* 2 */ + "16550\0" /* 3 */ + "16550A\0" /* 4 */ + "Cirrus\0" /* 5 */ + "16650\0" /* 6 */ + "16650V2\0" /* 7 */ + "16750\0" /* 8 */ + "16950\0" /* 9 UNIMPLEMENTED: also know as "16950/954" */ + "16954\0" /* 10 */ + "16654\0" /* 11 */ + "16850\0" /* 12 */ + "RSA\0" /* 13 */ +#ifndef SETSERIAL_BASE + "NS16550A\0" /* 14 */ + "XSCALE\0" /* 15 */ + "RM9000\0" /* 16 */ + "OCTEON\0" /* 17 */ + "AR7\0" /* 18 */ + "U6_16550A\0" /* 19 */ +#endif +; + +#ifndef SETSERIAL_BASE +# define MAX_SERIAL_TYPE 19 +#else +# define MAX_SERIAL_TYPE 13 +#endif + +static const char commands[] = + "spd_normal\0" + "spd_hi\0" + "spd_vhi\0" + "spd_shi\0" + "spd_warp\0" + "spd_cust\0" + + "sak\0" + "fourport\0" + "hup_notify\0" + "skip_test\0" + "auto_irq\0" + "split_termios\0" + "session_lockout\0" + "pgrp_lockout\0" + "callout_nohup\0" + "low_latency\0" + + "port\0" + "irq\0" + "divisor\0" + "uart\0" + "baund_base\0" + "close_delay\0" + "closing_wait\0" + + "autoconfig\0" +; + +enum +{ + CMD_SPD_NORMAL = 0, + CMD_SPD_HI, + CMD_SPD_VHI, + CMD_SPD_SHI, + CMD_SPD_WARP, + CMD_SPD_CUST, + + CMD_FLAG_SAK, + CMD_FLAG_FOURPORT, + CMD_FLAG_NUP_NOTIFY, + CMD_FLAG_SKIP_TEST, + CMD_FLAG_AUTO_IRQ, + CMD_FLAG_SPLIT_TERMIOS, + CMD_FLAG_SESSION_LOCKOUT, + CMD_FLAG_PGRP_LOCKOUT, + CMD_FLAG_CALLOUT_NOHUP, + CMD_FLAG_LOW_LATENCY, + + CMD_PORT, + CMD_IRQ, + CMD_DIVISOR, + CMD_UART, + CMD_BASE, + CMD_DELAY, + CMD_WAIT, + + CMD_AUTOCONFIG, + + CMD_FLAG_FIRST = CMD_FLAG_SAK, + CMD_FLAG_LAST = CMD_FLAG_LOW_LATENCY, +}; + +static bool cmd_noprint(int cmd) +{ + return (cmd >= CMD_FLAG_SKIP_TEST && cmd <= CMD_FLAG_CALLOUT_NOHUP); +} + +static bool cmd_is_flag(int cmd) +{ + return (cmd >= CMD_FLAG_FIRST && cmd <= CMD_FLAG_LAST); +} + +static bool cmd_need_arg(int cmd) +{ + return (cmd >= CMD_PORT && cmd <= CMD_WAIT); +} + +#define ALL_SPD ( \ + ASYNC_SPD_HI | ASYNC_SPD_VHI | ASYNC_SPD_SHI | \ + ASYNC_SPD_WARP | ASYNC_SPD_CUST \ + ) + +#define ALL_FLAGS ( \ + ASYNC_SAK | ASYNC_FOURPORT | ASYNC_HUP_NOTIFY | \ + ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SESSION_LOCKOUT | ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP | \ + ASYNC_LOW_LATENCY \ + ) + +#if (ALL_SPD | ALL_FLAGS) > 0xffff +# error "Unexpected flags size" +#endif + +static const uint16_t setbits[CMD_FLAG_LAST + 1] = +{ + 0, + ASYNC_SPD_HI, + ASYNC_SPD_VHI, + ASYNC_SPD_SHI, + ASYNC_SPD_WARP, + ASYNC_SPD_CUST, + + ASYNC_SAK, + ASYNC_FOURPORT, + ASYNC_HUP_NOTIFY, + ASYNC_SKIP_TEST, + ASYNC_AUTO_IRQ, + ASYNC_SPLIT_TERMIOS, + ASYNC_SESSION_LOCKOUT, + ASYNC_PGRP_LOCKOUT, + ASYNC_CALLOUT_NOHUP, + ASYNC_LOW_LATENCY +}; + +static const char STR_INFINITE[] = "infinite"; +static const char STR_NONE[] = "none"; + +static const char *uart_type(int type) +{ + if (type > MAX_SERIAL_TYPE) + return "undefined"; + + return nth_string(serial_types, type); +} + +/* libbb candidate */ +static int index_in_strings_case_insensitive(const char *strings, const char *key) +{ + int idx = 0; + + while (*strings) { + if (strcasecmp(strings, key) == 0) { + return idx; + } + strings += strlen(strings) + 1; /* skip NUL */ + idx++; + } + return -1; +} + +static int uart_id(const char *name) +{ + return index_in_strings_case_insensitive(serial_types, name); +} + +static const char *get_spd(int flags, enum print_mode mode) +{ + int idx; + + switch (flags & ASYNC_SPD_MASK) { + case ASYNC_SPD_HI: + idx = CMD_SPD_HI; + break; + case ASYNC_SPD_VHI: + idx = CMD_SPD_VHI; + break; + case ASYNC_SPD_SHI: + idx = CMD_SPD_SHI; + break; + case ASYNC_SPD_WARP: + idx = CMD_SPD_WARP; + break; + case ASYNC_SPD_CUST: + idx = CMD_SPD_CUST; + break; + default: + if (mode < PRINT_FEDBACK) + return NULL; + idx = CMD_SPD_NORMAL; + } + + return nth_string(commands, idx); +} + +static int get_numeric(const char *arg) +{ + return bb_strtol(arg, NULL, 0); +} + +static int get_wait(const char *arg) +{ + if (strcasecmp(arg, STR_NONE) == 0) + return ASYNC_CLOSING_WAIT_NONE; + + if (strcasecmp(arg, STR_INFINITE) == 0) + return ASYNC_CLOSING_WAIT_INF; + + return get_numeric(arg); +} + +static int get_uart(const char *arg) +{ + int uart = uart_id(arg); + + if (uart < 0) + bb_error_msg_and_die("illegal UART type: %s", arg); + + return uart; +} + +static int serial_open(const char *dev, bool quiet) +{ + int fd; + + fd = device_open(dev, O_RDWR | O_NONBLOCK); + if (fd < 0 && !quiet) + bb_simple_perror_msg(dev); + + return fd; +} + +static int serial_ctl(int fd, int ops, struct serial_struct *serinfo) +{ + int ret = 0; + const char *err; + + if (ops & CTL_SET) { + ret = ioctl(fd, TIOCSSERIAL, serinfo); + if (ret < 0) { + err = "can't set serial info"; + goto fail; + } + } + + if (ops & CTL_CONFIG) { + ret = ioctl(fd, TIOCSERCONFIG); + if (ret < 0) { + err = "can't autoconfigure port"; + goto fail; + } + } + + if (ops & CTL_GET) { + ret = ioctl(fd, TIOCGSERIAL, serinfo); + if (ret < 0) { + err = "can't get serial info"; + goto fail; + } + } + nodie: + if (ops & CTL_CLOSE) + close(fd); + + return ret; + fail: + bb_simple_perror_msg(err); + if (ops & CTL_NODIE) + goto nodie; + exit(EXIT_FAILURE); +} + +static void print_flag(const char **prefix, const char *flag) +{ + printf("%s%s", *prefix, flag); + *prefix = " "; +} + +static void print_serial_flags(int serial_flags, enum print_mode mode, + const char *prefix, const char *postfix) +{ + int i; + const char *spd, *pr; + + pr = prefix; + + spd = get_spd(serial_flags, mode); + if (spd) + print_flag(&pr, spd); + + for (i = CMD_FLAG_FIRST; i <= CMD_FLAG_LAST; i++) { + if ((serial_flags & setbits[i]) + && (mode > PRINT_SUMMARY || !cmd_noprint(i)) + ) { + print_flag(&pr, nth_string(commands, i)); + } + } + + puts(pr == prefix ? "" : postfix); +} + +static void print_closing_wait(unsigned int closing_wait) +{ + switch (closing_wait) { + case ASYNC_CLOSING_WAIT_NONE: + puts(STR_NONE); + break; + case ASYNC_CLOSING_WAIT_INF: + puts(STR_INFINITE); + break; + default: + printf("%u\n", closing_wait); + } +} + +static void serial_get(const char *device, enum print_mode mode) +{ + int fd, ret; + const char *uart, *prefix, *postfix; + struct serial_struct serinfo; + + fd = serial_open(device, /*quiet:*/ mode == PRINT_SUMMARY); + if (fd < 0) + return; + + ret = serial_ctl(fd, CTL_GET | CTL_CLOSE | CTL_NODIE, &serinfo); + if (ret < 0) + return; + + uart = uart_type(serinfo.type); + prefix = ", Flags: "; + postfix = ""; + + switch (mode) { + case PRINT_NORMAL: + printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d", + device, uart, serinfo.port, serinfo.irq); + break; + case PRINT_SUMMARY: + if (!serinfo.type) + return; + printf("%s at 0x%.4x (irq = %d) is a %s", + device, serinfo.port, serinfo.irq, uart); + prefix = " ("; + postfix = ")"; + break; + case PRINT_FEDBACK: + printf("%s uart %s port 0x%.4x irq %d baud_base %d", device, + uart, serinfo.port, serinfo.irq, serinfo.baud_base); + prefix = " "; + break; + case PRINT_ALL: + printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n", + device, serinfo.line, uart, serinfo.port, serinfo.irq); + printf("\tBaud_base: %d, close_delay: %u, divisor: %d\n", + serinfo.baud_base, serinfo.close_delay, + serinfo.custom_divisor); + printf("\tclosing_wait: "); + print_closing_wait(serinfo.closing_wait); + prefix = "\tFlags: "; + postfix = "\n"; + break; + default: + assert(0); + } + + print_serial_flags(serinfo.flags, mode, prefix, postfix); +} + +static int find_cmd(const char *cmd) +{ + int idx; + + idx = index_in_strings_case_insensitive(commands, cmd); + if (idx < 0) + bb_error_msg_and_die("invalid flag: %s", cmd); + + return idx; +} + +static void serial_set(char **arg, int opts) +{ + struct serial_struct serinfo; + int cmd; + const char *word; + int fd; + + fd = serial_open(*arg++, /*quiet:*/ false); + if (fd < 0) + exit(201); + + serial_ctl(fd, CTL_GET, &serinfo); + + if (opts & OPT_ZERO) + serinfo.flags = 0; + + while (*arg) { + int invert; + + word = *arg++; + invert = (*word == '^'); + word += invert; + + cmd = find_cmd(word); + + if (*arg == NULL && cmd_need_arg(cmd)) + bb_error_msg_and_die(bb_msg_requires_arg, word); + + if (invert && !cmd_is_flag(cmd)) + bb_error_msg_and_die("can't invert %s", word); + + switch (cmd) { + case CMD_SPD_NORMAL: + case CMD_SPD_HI: + case CMD_SPD_VHI: + case CMD_SPD_SHI: + case CMD_SPD_WARP: + case CMD_SPD_CUST: + serinfo.flags &= ASYNC_SPD_MASK; + /* fallthrough */ + case CMD_FLAG_SAK: + case CMD_FLAG_FOURPORT: + case CMD_FLAG_NUP_NOTIFY: + case CMD_FLAG_SKIP_TEST: + case CMD_FLAG_AUTO_IRQ: + case CMD_FLAG_SPLIT_TERMIOS: + case CMD_FLAG_SESSION_LOCKOUT: + case CMD_FLAG_PGRP_LOCKOUT: + case CMD_FLAG_CALLOUT_NOHUP: + case CMD_FLAG_LOW_LATENCY: + if (invert) + serinfo.flags &= ~setbits[cmd]; + else + serinfo.flags |= setbits[cmd]; + break; + case CMD_PORT: + serinfo.port = get_numeric(*arg++); + break; + case CMD_IRQ: + serinfo.irq = get_numeric(*arg++); + break; + case CMD_DIVISOR: + serinfo.custom_divisor = get_numeric(*arg++); + break; + case CMD_UART: + serinfo.type = get_uart(*arg++); + break; + case CMD_BASE: + serinfo.baud_base = get_numeric(*arg++); + break; + case CMD_DELAY: + serinfo.close_delay = get_numeric(*arg++); + break; + case CMD_WAIT: + serinfo.closing_wait = get_wait(*arg++); + break; + case CMD_AUTOCONFIG: + serial_ctl(fd, CTL_SET | CTL_CONFIG | CTL_GET, &serinfo); + break; + default: + assert(0); + } + } + + serial_ctl(fd, CTL_SET | CTL_CLOSE, &serinfo); +} + +int setserial_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int setserial_main(int argc UNUSED_PARAM, char **argv) +{ + int opts; + + opt_complementary = "-1:b-aG:G-ab:a-bG"; + opts = getopt32(argv, "bGavzg"); + argv += optind; + + if (!argv[1]) /* one arg only? */ + opts |= OPT_GET; + + if (!(opts & OPT_GET)) { + serial_set(argv, opts); + argv[1] = NULL; + } + + if (opts & (OPT_VERBOSE | OPT_GET)) { + do { + serial_get(*argv++, opts & OPT_MODE_MASK); + } while (*argv); + } + + return EXIT_SUCCESS; +} -- cgit v1.2.3-55-g6feb From fcbc641fe36a2ceff334362cc6ba62b000c842a5 Mon Sep 17 00:00:00 2001 From: Cristian Ionescu-Idbohrn Date: Sun, 1 May 2011 14:43:53 +0200 Subject: get rid of several "variable 'x' set but not used" warnings Signed-off-by: Cristian Ionescu-Idbohrn Signed-off-by: Denys Vlasenko --- .gitignore | 1 + scripts/kconfig/conf.c | 11 ++--------- scripts/kconfig/lxdialog/textbox.c | 8 +++----- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 7d2cca67c..0a0c65bc3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ Config.in # Normal output # /busybox +/busybox_old /busybox_unstripped* # diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 6e097889f..ea2446a89 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -173,7 +173,7 @@ static void conf_askvalue(struct symbol *sym, const char *def) int conf_string(struct menu *menu) { struct symbol *sym = menu->sym; - const char *def, *help; + const char *def; while (1) { printf("%*s%s ", indent - 1, "", menu->prompt->text); @@ -188,10 +188,7 @@ int conf_string(struct menu *menu) case '?': /* print help */ if (line[1] == '\n') { - help = nohelp_text; - if (menu->sym->help) - help = menu->sym->help; - printf("\n%s\n", menu->sym->help); + printf("\n%s\n", menu->sym->help ? menu->sym->help : nohelp_text); def = NULL; break; } @@ -207,7 +204,6 @@ int conf_string(struct menu *menu) static int conf_sym(struct menu *menu) { struct symbol *sym = menu->sym; - int type; tristate oldval, newval; const char *help; @@ -215,7 +211,6 @@ static int conf_sym(struct menu *menu) printf("%*s%s ", indent - 1, "", menu->prompt->text); if (sym->name) printf("(%s) ", sym->name); - type = sym_get_type(sym); putchar('['); oldval = sym_get_tristate_value(sym); switch (oldval) { @@ -282,11 +277,9 @@ static int conf_choice(struct menu *menu) { struct symbol *sym, *def_sym; struct menu *child; - int type; bool is_new; sym = menu->sym; - type = sym_get_type(sym); is_new = !sym_has_value(sym); if (sym_is_changable(sym)) { conf_sym(menu); diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c index 77848bb8e..de4ae41d7 100644 --- a/scripts/kconfig/lxdialog/textbox.c +++ b/scripts/kconfig/lxdialog/textbox.c @@ -38,11 +38,8 @@ int dialog_textbox(const char *title, const char *file, int height, int width) { int i, x, y, cur_x, cur_y, fpos, key = 0; int passed_end; - char search_term[MAX_LEN + 1]; WINDOW *dialog, *text; - search_term[0] = '\0'; /* no search term entered yet */ - /* Open input file for reading */ if ((fd = open(file, O_RDONLY)) == -1) { endwin(); @@ -437,7 +434,6 @@ static void print_page(WINDOW * win, int height, int width) */ static void print_line(WINDOW * win, int row, int width) { - int y, x; char *line; line = get_line(); @@ -446,11 +442,13 @@ static void print_line(WINDOW * win, int row, int width) waddch(win, ' '); waddnstr(win, line, MIN(strlen(line), width - 2)); - getyx(win, y, x); /* Clear 'residue' of previous line */ #if OLD_NCURSES { int i; + int y, x; + + getyx(win, y, x); for (i = 0; i < width - x; i++) waddch(win, ' '); } -- cgit v1.2.3-55-g6feb From 90ea1e34548e36e800b1c9699885167f9b3e9ff0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 2 May 2011 02:47:25 +0200 Subject: setserial: small fix Signed-off-by: Denys Vlasenko --- miscutils/setserial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miscutils/setserial.c b/miscutils/setserial.c index 501beaacc..2951b987d 100644 --- a/miscutils/setserial.c +++ b/miscutils/setserial.c @@ -687,7 +687,7 @@ static void serial_set(char **arg, int opts) case CMD_SPD_SHI: case CMD_SPD_WARP: case CMD_SPD_CUST: - serinfo.flags &= ASYNC_SPD_MASK; + serinfo.flags &= ~ASYNC_SPD_MASK; /* fallthrough */ case CMD_FLAG_SAK: case CMD_FLAG_FOURPORT: -- cgit v1.2.3-55-g6feb From 91b77c3953d6b88770e3102fc2ce428e8f9a8f13 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Sun, 1 May 2011 14:00:27 +0200 Subject: Compile options: default to -O2 when -Os is not available Recent versions of GCC for PowerPC systems suffer from some compiler bugs which prevent the use of "--enable-target-optspace" in their configuration, which makes the compiler option "-Os" unavailable; for reference see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43810 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48278 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45053 In the result, BusyBox will be compiled without any optimization options, which causes a huge binary (1.8 MiB for default configuration on PPC, built with gcc 54.51 from the Yocto / Poky / OpenEmbedded tool chains). This commit changes behaviour so "-O2" gets used as fallback when "-Os" is not available. This reduces the image size in above test to 1.3 MiB. This is still 10...15% more then what we get with "-Os", but much better than using no optimization at all. Signed-off-by: Wolfgang Denk Signed-off-by: Denys Vlasenko --- Makefile.flags | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.flags b/Makefile.flags index 363300b52..b3e13713d 100644 --- a/Makefile.flags +++ b/Makefile.flags @@ -58,14 +58,14 @@ CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1 #CFLAGS += $(call cc-option,-Wconversion,) ifneq ($(CONFIG_DEBUG),y) -CFLAGS += $(call cc-option,-Os,) +CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) else CFLAGS += $(call cc-option,-g,) #CFLAGS += "-D_FORTIFY_SOURCE=2" ifeq ($(CONFIG_DEBUG_PESSIMIZE),y) CFLAGS += $(call cc-option,-O0,) else -CFLAGS += $(call cc-option,-Os,) +CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,)) endif endif -- cgit v1.2.3-55-g6feb From 52d83708364f85463fbc3756420b4068df13aab7 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 May 2011 00:51:43 +0200 Subject: sed: code shrink text data bss dec hex filename 876354 493 7584 884431 d7ecf busybox_old 876323 493 7584 884400 d7eb0 busybox_unstripped Signed-off-by: Denys Vlasenko --- editors/sed.c | 146 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 54 deletions(-) diff --git a/editors/sed.c b/editors/sed.c index 9ab758bd7..9e27e3e18 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -14,49 +14,47 @@ */ /* Code overview. + * + * Files are laid out to avoid unnecessary function declarations. So for + * example, every function add_cmd calls occurs before add_cmd in this file. + * + * add_cmd() is called on each line of sed command text (from a file or from + * the command line). It calls get_address() and parse_cmd_args(). The + * resulting sed_cmd_t structures are appended to a linked list + * (G.sed_cmd_head/G.sed_cmd_tail). + * + * add_input_file() adds a FILE* to the list of input files. We need to + * know all input sources ahead of time to find the last line for the $ match. + * + * process_files() does actual sedding, reading data lines from each input FILE * + * (which could be stdin) and applying the sed command list (sed_cmd_head) to + * each of the resulting lines. + * + * sed_main() is where external code calls into this, with a command line. + */ - Files are laid out to avoid unnecessary function declarations. So for - example, every function add_cmd calls occurs before add_cmd in this file. - - add_cmd() is called on each line of sed command text (from a file or from - the command line). It calls get_address() and parse_cmd_args(). The - resulting sed_cmd_t structures are appended to a linked list - (G.sed_cmd_head/G.sed_cmd_tail). - - add_input_file() adds a FILE* to the list of input files. We need to - know all input sources ahead of time to find the last line for the $ match. - - process_files() does actual sedding, reading data lines from each input FILE * - (which could be stdin) and applying the sed command list (sed_cmd_head) to - each of the resulting lines. - - sed_main() is where external code calls into this, with a command line. -*/ - - -/* - Supported features and commands in this version of sed: - - - comments ('#') - - address matching: num|/matchstr/[,num|/matchstr/|$]command - - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) - - edit commands: (a)ppend, (i)nsert, (c)hange - - file commands: (r)ead - - backreferences in substitution expressions (\0, \1, \2...\9) - - grouped commands: {cmd1;cmd2} - - transliteration (y/source-chars/dest-chars/) - - pattern space hold space storing / swapping (g, h, x) - - labels / branching (: label, b, t, T) - - (Note: Specifying an address (range) to match is *optional*; commands - default to the whole pattern space if no specific address match was - requested.) - - Todo: - - Create a wrapper around regex to make libc's regex conform with sed - - Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html -*/ +/* Supported features and commands in this version of sed: + * + * - comments ('#') + * - address matching: num|/matchstr/[,num|/matchstr/|$]command + * - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags) + * - edit commands: (a)ppend, (i)nsert, (c)hange + * - file commands: (r)ead + * - backreferences in substitution expressions (\0, \1, \2...\9) + * - grouped commands: {cmd1;cmd2} + * - transliteration (y/source-chars/dest-chars/) + * - pattern space hold space storing / swapping (g, h, x) + * - labels / branching (: label, b, t, T) + * + * (Note: Specifying an address (range) to match is *optional*; commands + * default to the whole pattern space if no specific address match was + * requested.) + * + * Todo: + * - Create a wrapper around regex to make libc's regex conform with sed + * + * Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html + */ //usage:#define sed_trivial_usage //usage: "[-efinr] SED_CMD [FILE]..." @@ -244,11 +242,13 @@ static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str) delimiter = -delimiter; } - for (; (ch = str[idx]); idx++) { + for (; (ch = str[idx]) != '\0'; idx++) { if (bracket >= 0) { - if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2 - && str[idx - 1] == '^'))) + if (ch == ']' + && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^')) + ) { bracket = -1; + } } else if (escaped) escaped = 0; else if (ch == '\\') @@ -434,11 +434,47 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr) */ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) { + static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}"; + enum { + IDX_s = 0, + IDX_a, + IDX_i, + IDX_c, + IDX_r, + IDX_w, + IDX_colon, + IDX_b, + IDX_t, + IDX_T, + IDX_y, + IDX_d, + IDX_D, + IDX_g, + IDX_G, + IDX_h, + IDX_H, + IDX_l, + IDX_n, + IDX_N, + IDX_p, + IDX_P, + IDX_q, + IDX_x, + IDX_equal, + IDX_lbrace, + IDX_rbrace, + IDX_nul + }; + struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; }; + + unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters; + /* handle (s)ubstitution command */ - if (sed_cmd->cmd == 's') + if (idx == IDX_s) { cmdstr += parse_subst_cmd(sed_cmd, cmdstr); + } /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ - else if (strchr("aic", sed_cmd->cmd)) { + else if (idx <= IDX_c) { /* a,i,c */ if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') bb_error_msg_and_die("only a beginning address can be specified for edit commands"); for (;;) { @@ -454,8 +490,9 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) /* "\anychar" -> "anychar" */ parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0'); cmdstr += strlen(cmdstr); + } /* handle file cmds: (r)ead */ - } else if (strchr("rw", sed_cmd->cmd)) { + else if (idx <= IDX_w) { /* r,w */ if (sed_cmd->end_line || sed_cmd->end_match) bb_error_msg_and_die("command only uses one address"); cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); @@ -463,8 +500,9 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); sed_cmd->sw_last_char = '\n'; } + } /* handle branch commands */ - } else if (strchr(":btT", sed_cmd->cmd)) { + else if (idx <= IDX_T) { /* :,b,t,T */ int length; cmdstr = skip_whitespace(cmdstr); @@ -475,7 +513,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) } } /* translation command */ - else if (sed_cmd->cmd == 'y') { + else if (idx == IDX_y) { char *match, *replace; int i = cmdstr[0]; @@ -495,7 +533,7 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr) /* if it wasnt a single-letter command that takes no arguments * then it must be an invalid command. */ - else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) { + else if (idx >= IDX_nul) { /* not d,D,g,G,h,H,l,n,N,p,P,q,x,=,{,} */ bb_error_msg_and_die("unsupported command %c", sed_cmd->cmd); } @@ -966,9 +1004,9 @@ static void process_files(void) } sed_cmd->in_match = !( /* has the ending line come, or is this a single address command? */ - (sed_cmd->end_line ? - sed_cmd->end_line == -1 ? - !next_line + (sed_cmd->end_line + ? sed_cmd->end_line == -1 + ? !next_line : (sed_cmd->end_line <= linenum) : !sed_cmd->end_match ) -- cgit v1.2.3-55-g6feb From 6a0abcc02f0773f32f2d31b3532eec1b11e1f67d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 May 2011 00:52:22 +0200 Subject: sed: support \r in s command function old new delta copy_parsing_escapes 65 91 +26 Signed-off-by: Denys Vlasenko --- editors/sed.c | 3 ++- testsuite/sed.tests | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/editors/sed.c b/editors/sed.c index 9e27e3e18..11c476321 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -218,8 +218,9 @@ static char *copy_parsing_escapes(const char *string, int len) char *dest = xmalloc(len + 1); parse_escapes(dest, string, len, 'n', '\n'); - /* GNU sed also recognizes \t */ + /* GNU sed also recognizes \t and \r */ parse_escapes(dest, dest, strlen(dest), 't', '\t'); + parse_escapes(dest, dest, strlen(dest), 'r', '\r'); return dest; } diff --git a/testsuite/sed.tests b/testsuite/sed.tests index 395372ae6..e9d0ed601 100755 --- a/testsuite/sed.tests +++ b/testsuite/sed.tests @@ -287,6 +287,9 @@ testing "sed -i with address modifies all files, not only first" \ "cp input input2; sed -i -e '1s/foo/bar/' input input2 && cat input input2; rm input2" \ "bar\nbar\n" "foo\n" "" +testing "sed understands \r" \ + "sed 's/r/\r/'" \ + "\rrr\n" "" "rrr\n" # testing "description" "arguments" "result" "infile" "stdin" -- cgit v1.2.3-55-g6feb From 7945808c871f1cf659f7cbd74da18daad052fa40 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 3 May 2011 16:46:47 +0200 Subject: udhcp: fix some default values from "" to NULL Signed-off-by: Denys Vlasenko --- networking/udhcp/files.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c index 49bcafb9c..6840f3c25 100644 --- a/networking/udhcp/files.c +++ b/networking/udhcp/files.c @@ -80,9 +80,9 @@ static const struct config_keyword keywords[] = { /* keywords with no defaults must be last! */ {"option" , udhcp_str2optset, &server_config.options , ""}, {"opt" , udhcp_str2optset, &server_config.options , ""}, - {"notify_file" , read_str , &server_config.notify_file , ""}, - {"sname" , read_str , &server_config.sname , ""}, - {"boot_file" , read_str , &server_config.boot_file , ""}, + {"notify_file" , read_str , &server_config.notify_file , NULL}, + {"sname" , read_str , &server_config.sname , NULL}, + {"boot_file" , read_str , &server_config.boot_file , NULL}, {"static_lease" , read_staticlease, &server_config.static_leases, ""}, }; enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; -- cgit v1.2.3-55-g6feb From 882062915d52581fb754e5721addca72aa789ab7 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Wed, 4 May 2011 19:03:30 +0200 Subject: mount: for NFS use tcp per default rather than udp nfs-utils's mount.nfs uses TCP per default unless explicitly asked for otherwise. This is also the expected behaviour for NFSv4, so ok there, too. Signed-off-by: Bernhard Reutner-Fischer --- util-linux/mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util-linux/mount.c b/util-linux/mount.c index 3e2ba1fab..6a154e2b2 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -1195,7 +1195,7 @@ static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) noac = 0; nordirplus = 0; retry = 10000; /* 10000 minutes ~ 1 week */ - tcp = 0; + tcp = 1; /* nfs-utils uses tcp per default */ mountprog = MOUNTPROG; mountvers = 0; -- cgit v1.2.3-55-g6feb From 9180c6045e8b2465b3e18a22eb08ee8438ae8544 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 4 May 2011 21:14:12 +0200 Subject: tar: store negative mtime as 0; pack very large files using base-256 encoding function old new delta writeTarHeader 841 979 +138 Signed-off-by: Denys Vlasenko --- archival/tar.c | 36 ++++++++++++++++++++++++++++++++---- include/archive.h | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/archival/tar.c b/archival/tar.c index 01b83d5e2..ebaa183b4 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -264,7 +264,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, PUT_OCTAL(header.uid, statbuf->st_uid); PUT_OCTAL(header.gid, statbuf->st_gid); memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ - PUT_OCTAL(header.mtime, statbuf->st_mtime); + /* users report that files with negative st_mtime cause trouble, so: */ + PUT_OCTAL(header.mtime, statbuf->st_mtime >= 0 ? statbuf->st_mtime : 0); /* Enter the user and group names */ safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); @@ -316,15 +317,42 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, } else if (S_ISFIFO(statbuf->st_mode)) { header.typeflag = FIFOTYPE; } else if (S_ISREG(statbuf->st_mode)) { - if (sizeof(statbuf->st_size) > 4 - && statbuf->st_size > (off_t)0777777777777LL + /* header.size field is 12 bytes long */ + /* Does octal-encoded size fit? */ + uoff_t filesize = statbuf->st_size; + if (sizeof(filesize) <= 4 + || filesize <= (uoff_t)0777777777777LL ) { + PUT_OCTAL(header.size, filesize); + } + /* Does base256-encoded size fit? + * It always does unless off_t is wider than 64 bits. + */ + else if (ENABLE_FEATURE_TAR_GNU_EXTENSIONS +#if ULLONG_MAX > 0xffffffffffffffffLL /* 2^64-1 */ + && (filesize <= 0x3fffffffffffffffffffffffLL) +#endif + ) { + /* GNU tar uses "base-256 encoding" for very large numbers. + * Encoding is binary, with highest bit always set as a marker + * and sign in next-highest bit: + * 80 00 .. 00 - zero + * bf ff .. ff - largest positive number + * ff ff .. ff - minus 1 + * c0 00 .. 00 - smallest negative number + */ + char *p8 = header.size + sizeof(header.size); + do { + *--p8 = (uint8_t)filesize; + filesize >>= 8; + } while (p8 != header.size); + *p8 |= 0x80; + } else { bb_error_msg_and_die("can't store file '%s' " "of size %"OFF_FMT"u, aborting", fileName, statbuf->st_size); } header.typeflag = REGTYPE; - PUT_OCTAL(header.size, statbuf->st_size); } else { bb_error_msg("%s: unknown file type", fileName); return FALSE; diff --git a/include/archive.h b/include/archive.h index b139dc5be..9e176d335 100644 --- a/include/archive.h +++ b/include/archive.h @@ -125,7 +125,7 @@ typedef struct archive_handle_t { #define TAR_BLOCK_SIZE 512 #define NAME_SIZE 100 #define NAME_SIZE_STR "100" -typedef struct tar_header_t { /* byte offset */ +typedef struct tar_header_t { /* byte offset */ char name[NAME_SIZE]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ -- cgit v1.2.3-55-g6feb From 14b162f9ab2c34cf5ffc9f5c8e9f90d58e8445eb Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 4 May 2011 21:17:23 +0200 Subject: tweak examples/udhcp/udhcpd.conf Signed-off-by: Denys Vlasenko --- examples/udhcp/udhcpd.conf | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/udhcp/udhcpd.conf b/examples/udhcp/udhcpd.conf index 43b49df89..cd2957ccc 100644 --- a/examples/udhcp/udhcpd.conf +++ b/examples/udhcp/udhcpd.conf @@ -14,11 +14,6 @@ interface eth0 # smaller than lease block. #max_leases 254 -# The time period at which udhcpd will write out a dhcpd.leases -# file. If this is 0, udhcpd will never automatically write a -# lease file. Specified in seconds. -#auto_time 7200 - # The amount of time that an IP will be reserved (leased to nobody) # if a DHCP decline message is received (seconds) #decline_time 3600 @@ -34,11 +29,16 @@ interface eth0 # to this value (seconds) #min_lease 60 +# The location of the pid file +#pidfile /var/run/udhcpd.pid + # The location of the leases file #lease_file /var/lib/misc/udhcpd.leases -# The location of the pid file -#pidfile /var/run/udhcpd.pid +# The time period at which udhcpd will write out leases file. +# If this is 0, udhcpd will never automatically write leases file. +# Specified in seconds. +#auto_time 7200 # Every time udhcpd writes a leases file, the below script will be called #notify_file # default: no script -- cgit v1.2.3-55-g6feb From d3e4be3ccb1506cfabecc4c2f0baeb07fdd06723 Mon Sep 17 00:00:00 2001 From: Pascal Bellard Date: Thu, 5 May 2011 00:26:37 +0200 Subject: sed: shrink by 17 bytes Signed-off-by: Pascal Bellard Signed-off-by: Denys Vlasenko --- editors/sed.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/editors/sed.c b/editors/sed.c index 11c476321..99e56ff52 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -215,12 +215,16 @@ static void parse_escapes(char *dest, const char *string, int len, char from, ch static char *copy_parsing_escapes(const char *string, int len) { + const char *s; char *dest = xmalloc(len + 1); - parse_escapes(dest, string, len, 'n', '\n'); + /* sed recognizes \n */ /* GNU sed also recognizes \t and \r */ - parse_escapes(dest, dest, strlen(dest), 't', '\t'); - parse_escapes(dest, dest, strlen(dest), 'r', '\r'); + for (s = "\nn\tt\rr"; *s; s += 2) { + parse_escapes(dest, string, len, s[1], s[0]); + string = dest; + len = strlen(dest); + } return dest; } -- cgit v1.2.3-55-g6feb From a348b4557d3d0af411135c23448a2c5a7cd82982 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 5 May 2011 02:31:30 +0200 Subject: traceroute: properly reduce poll timeout This removes the problem where during the time we wait to declare a target as unresponsive we receive an unrelated ICMP packet. If there is enough traffic, this can make traceroute hang as it never declares the target as unresponsive. function old new delta common_traceroute_main 4196 4261 +65 Signed-off-by: Denys Vlasenko --- networking/traceroute.c | 61 ++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/networking/traceroute.c b/networking/traceroute.c index 96f9d3472..85181ab8d 100644 --- a/networking/traceroute.c +++ b/networking/traceroute.c @@ -398,18 +398,23 @@ static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa) static int -wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to) +wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms) { struct pollfd pfd[1]; int read_len = 0; pfd[0].fd = rcvsock; pfd[0].events = POLLIN; - if (safe_poll(pfd, 1, waittime * 1000) > 0) { + if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) { + unsigned t; + read_len = recv_from_to(rcvsock, recv_pkt, sizeof(recv_pkt), - /*flags:*/ 0, + /*flags:*/ MSG_DONTWAIT, &from_lsa->u.sa, to, from_lsa->len); + t = monotonic_us(); + *left_ms -= (t - *timestamp_us) / 1000; + *timestamp_us = t; } return read_len; @@ -730,7 +735,7 @@ packet_ok(int read_len, len_and_sockaddr *from_lsa, type, pr_type(type), icp->icmp6_code); read_len -= sizeof(struct icmp6_hdr); - for (i = 0; i < read_len ; i++) { + for (i = 0; i < read_len; i++) { if (i % 16 == 0) printf("%04x:", i); if (i % 4 == 0) @@ -819,7 +824,6 @@ print_delta_ms(unsigned t1p, unsigned t2p) static int common_traceroute_main(int op, char **argv) { - int i; int minpacket; int tos = 0; int max_ttl = 30; @@ -973,6 +977,7 @@ common_traceroute_main(int op, char **argv) #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS if (lsrr > 0) { unsigned char optlist[MAX_IPOPTLEN]; + unsigned size; /* final hop */ gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr; @@ -982,14 +987,14 @@ common_traceroute_main(int op, char **argv) optlist[0] = IPOPT_NOP; /* loose source route option */ optlist[1] = IPOPT_LSRR; - i = lsrr * sizeof(gwlist[0]); - optlist[2] = i + 3; + size = lsrr * sizeof(gwlist[0]); + optlist[2] = size + 3; /* pointer to LSRR addresses */ optlist[3] = IPOPT_MINOFF; - memcpy(optlist + 4, gwlist, i); + memcpy(optlist + 4, gwlist, size); if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS, - (char *)optlist, i + sizeof(gwlist[0])) < 0) { + (char *)optlist, size + sizeof(gwlist[0])) < 0) { bb_perror_msg_and_die("IP_OPTIONS"); } } @@ -1103,28 +1108,34 @@ common_traceroute_main(int op, char **argv) int unreachable = 0; /* counter */ int gotlastaddr = 0; /* flags */ int got_there = 0; - int first = 1; printf("%2d", ttl); for (probe = 0; probe < nprobes; ++probe) { int read_len; unsigned t1; unsigned t2; + int left_ms; struct ip *ip; - if (!first && pausemsecs > 0) - usleep(pausemsecs * 1000); fflush_all(); + if (probe != 0 && pausemsecs > 0) + usleep(pausemsecs * 1000); - t1 = monotonic_us(); send_probe(++seq, ttl); + t2 = t1 = monotonic_us(); + + left_ms = waittime * 1000; + while ((read_len = wait_for_reply(from_lsa, to, &t2, &left_ms)) != 0) { + int icmp_code; + + /* Recv'ed a packet, or read error */ + /* t2 = monotonic_us() - set by wait_for_reply */ - first = 0; - while ((read_len = wait_for_reply(from_lsa, to)) != 0) { - t2 = monotonic_us(); - i = packet_ok(read_len, from_lsa, to, seq); + if (read_len < 0) + continue; + icmp_code = packet_ok(read_len, from_lsa, to, seq); /* Skip short packet */ - if (i == 0) + if (icmp_code == 0) continue; if (!gotlastaddr @@ -1143,10 +1154,10 @@ common_traceroute_main(int op, char **argv) printf(" (%d)", ip->ip_ttl); /* time exceeded in transit */ - if (i == -1) + if (icmp_code == -1) break; - i--; - switch (i) { + icmp_code--; + switch (icmp_code) { #if ENABLE_TRACEROUTE6 case ICMP6_DST_UNREACH_NOPORT << 8: got_there = 1; @@ -1219,16 +1230,18 @@ common_traceroute_main(int op, char **argv) ++unreachable; break; default: - printf(" !<%d>", i); + printf(" !<%d>", icmp_code); ++unreachable; break; } break; - } + } /* while (wait and read a packet) */ + /* there was no packet at all? */ if (read_len == 0) printf(" *"); - } + } /* for (nprobes) */ + bb_putchar('\n'); if (got_there || (unreachable > 0 && unreachable >= nprobes - 1) -- cgit v1.2.3-55-g6feb From 7d9a1d25e53544c922d5e362180b2703a86d32df Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 6 May 2011 20:34:04 +0200 Subject: top: make it possible to feed commands via pipe function old new delta handle_input - 492 +492 mult_lvl_cmp 38 49 +11 packed_usage 28247 28257 +10 top_main 1345 928 -417 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/1 up/down: 513/-417) Total: 96 bytes Signed-off-by: Denys Vlasenko --- procps/top.c | 265 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 155 insertions(+), 110 deletions(-) diff --git a/procps/top.c b/procps/top.c index ee6555188..ed8b56173 100644 --- a/procps/top.c +++ b/procps/top.c @@ -92,9 +92,9 @@ enum { SORT_DEPTH = 3 }; struct globals { top_status_t *top; int ntop; + smallint inverted; #if ENABLE_FEATURE_TOPMEM smallint sort_field; - smallint inverted; #endif #if ENABLE_FEATURE_TOP_SMP_CPU smallint smp_cpu_info; /* one/many cpu info lines? */ @@ -194,9 +194,9 @@ static int mult_lvl_cmp(void* a, void* b) for (i = 0; i < SORT_DEPTH; i++) { cmp_val = (*sort_function[i])(a, b); if (cmp_val != 0) - return cmp_val; + break; } - return 0; + return inverted ? -cmp_val : cmp_val; } static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) @@ -850,8 +850,105 @@ enum { | PSSCAN_PID | PSSCAN_SMAPS | PSSCAN_COMM, + EXIT_MASK = (unsigned)-1, }; +#if ENABLE_FEATURE_USE_TERMIOS +static unsigned handle_input(unsigned scan_mask, unsigned interval) +{ + unsigned char c, *p, buf[64]; + int len; + struct pollfd pfd[1]; + + pfd[0].fd = 0; + pfd[0].events = POLLIN; + if (safe_poll(pfd, 1, interval * 1000) <= 0) + return scan_mask; + + len = safe_read(STDIN_FILENO, &buf, sizeof(buf)-1); + if (len <= 0) { /* error/EOF? */ + option_mask32 |= OPT_EOF; + return scan_mask; + } + + buf[len] = 0; + p = buf; + while ((c = *p++) != 0) { + if (c == initial_settings.c_cc[VINTR]) + return EXIT_MASK; + if (c == initial_settings.c_cc[VEOF]) + return EXIT_MASK; + c |= 0x20; /* lowercase */ + if (c == 'q') + return EXIT_MASK; + if (c == 'n') { + IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) + sort_function[0] = pid_sort; + } + if (c == 'm') { + IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) + sort_function[0] = mem_sort; +# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + sort_function[1] = pcpu_sort; + sort_function[2] = time_sort; +# endif + } +# if ENABLE_FEATURE_SHOW_THREADS + if (c == 'h' + IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) + ) { + scan_mask ^= PSSCAN_TASKS; + } +# endif +# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE + if (c == 'p') { + IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) + sort_function[0] = pcpu_sort; + sort_function[1] = mem_sort; + sort_function[2] = time_sort; + } + if (c == 't') { + IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) + sort_function[0] = time_sort; + sort_function[1] = mem_sort; + sort_function[2] = pcpu_sort; + } +# if ENABLE_FEATURE_TOPMEM + if (c == 's') { + scan_mask = TOPMEM_MASK; + free(prev_hist); + prev_hist = NULL; + prev_hist_count = 0; + sort_field = (sort_field + 1) % NUM_SORT_FIELD; + } +# endif + if (c == 'r') + inverted ^= 1; +# if ENABLE_FEATURE_TOP_SMP_CPU + /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ + if (c == 'c' || c == '1') { + /* User wants to toggle per cpu <> aggregate */ + if (smp_cpu_info) { + free(cpu_prev_jif); + free(cpu_jif); + cpu_jif = &cur_jif; + cpu_prev_jif = &prev_jif; + } else { + /* Prepare for xrealloc() */ + cpu_jif = cpu_prev_jif = NULL; + } + num_cpus = 0; + smp_cpu_info = !smp_cpu_info; + get_jiffy_counts(); + } +# endif +# endif + } + + return scan_mask; +} +#endif + //usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU //usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__ //usage:#else @@ -871,8 +968,9 @@ enum { //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu") //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time") //usage: IF_FEATURE_TOPMEM( -//usage: "\n"" S: show memory, R: reverse memory sort" +//usage: "\n"" S: show memory" //usage: ) +//usage: "\n"" R: reverse sort" //usage: IF_SHOW_THREADS_OR_TOP_SMP( //usage: "\n"" " //usage: IF_FEATURE_SHOW_THREADS("H: toggle threads") @@ -880,23 +978,34 @@ enum { //usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP") //usage: ) //usage: "\n"" Q,^C: exit" +//usage: "\n" +//usage: "\n""Options:" +//usage: "\n"" -b Batch mode" +//usage: "\n"" -n N Exit after N iterations" +//usage: "\n"" -d N Delay between updates" +//usage: IF_FEATURE_TOPMEM( +//usage: "\n"" -m Same as 's' key" +//usage: ) + +/* Interactive testing: + * echo sss | ./busybox top + * - shows memory screen + * echo sss | ./busybox top -bn1 >mem + * - saves memory screen - the *whole* list, not first NROWS porcesses! + * + * TODO: -i STRING param as a better alternative? + */ int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int top_main(int argc UNUSED_PARAM, char **argv) { int iterations; unsigned lines, col; - int lines_rem; unsigned interval; char *str_interval, *str_iterations; unsigned scan_mask = TOP_MASK; #if ENABLE_FEATURE_USE_TERMIOS struct termios new_settings; - struct pollfd pfd[1]; - unsigned char c; - - pfd[0].fd = 0; - pfd[0].events = POLLIN; #endif INIT_G(); @@ -933,15 +1042,6 @@ int top_main(int argc UNUSED_PARAM, char **argv) /* change to /proc */ xchdir("/proc"); -#if ENABLE_FEATURE_USE_TERMIOS - tcgetattr(0, (void *) &initial_settings); - memcpy(&new_settings, &initial_settings, sizeof(new_settings)); - /* unbuffered input, turn off echo */ - new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); - - bb_signals(BB_FATAL_SIGS, sig_catcher); - tcsetattr_stdin_TCSANOW(&new_settings); -#endif #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE sort_function[0] = pcpu_sort; @@ -951,21 +1051,41 @@ int top_main(int argc UNUSED_PARAM, char **argv) sort_function[0] = mem_sort; #endif - while (1) { +#if ENABLE_FEATURE_USE_TERMIOS + tcgetattr(0, (void *) &initial_settings); + memcpy(&new_settings, &initial_settings, sizeof(new_settings)); + if (!OPT_BATCH_MODE) { + /* unbuffered input, turn off echo */ + new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); + tcsetattr_stdin_TCSANOW(&new_settings); + } + + bb_signals(BB_FATAL_SIGS, sig_catcher); + + /* Eat initial input, if any */ + scan_mask = handle_input(scan_mask, 0); +#endif + + while (scan_mask != EXIT_MASK) { procps_status_t *p = NULL; - lines = 24; /* default */ - col = 79; + if (OPT_BATCH_MODE) { + lines = INT_MAX; + col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ + } else { + lines = 24; /* default */ + col = 79; #if ENABLE_FEATURE_USE_TERMIOS - /* We output to stdout, we need size of stdout (not stdin)! */ - get_terminal_width_height(STDOUT_FILENO, &col, &lines); - if (lines < 5 || col < 10) { - sleep(interval); - continue; - } + /* We output to stdout, we need size of stdout (not stdin)! */ + get_terminal_width_height(STDOUT_FILENO, &col, &lines); + if (lines < 5 || col < 10) { + sleep(interval); + continue; + } #endif - if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ - col = LINE_BUF_SIZE-2; + if (col > LINE_BUF_SIZE - 2) + col = LINE_BUF_SIZE - 2; + } /* read process IDs & status for all the processes */ while ((p = procps_scan(p, scan_mask)) != NULL) { @@ -1033,15 +1153,11 @@ int top_main(int argc UNUSED_PARAM, char **argv) qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); } #endif - lines_rem = lines; - if (OPT_BATCH_MODE) { - lines_rem = INT_MAX; - } if (scan_mask != TOPMEM_MASK) - display_process_list(lines_rem, col); + display_process_list(lines, col); #if ENABLE_FEATURE_TOPMEM else - display_topmem_process_list(lines_rem, col); + display_topmem_process_list(lines, col); #endif clearmems(); if (iterations >= 0 && !--iterations) @@ -1052,81 +1168,10 @@ int top_main(int argc UNUSED_PARAM, char **argv) if (option_mask32 & (OPT_b|OPT_EOF)) /* batch mode, or EOF on stdin ("top 0) { - if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ - option_mask32 |= OPT_EOF; - continue; - } - if (c == initial_settings.c_cc[VINTR]) - break; - c |= 0x20; /* lowercase */ - if (c == 'q') - break; - if (c == 'n') { - IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) - sort_function[0] = pid_sort; - } - if (c == 'm') { - IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) - sort_function[0] = mem_sort; -# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE - sort_function[1] = pcpu_sort; - sort_function[2] = time_sort; -# endif - } -# if ENABLE_FEATURE_SHOW_THREADS - if (c == 'h' - IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) - ) { - scan_mask ^= PSSCAN_TASKS; - } -# endif -# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE - if (c == 'p') { - IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) - sort_function[0] = pcpu_sort; - sort_function[1] = mem_sort; - sort_function[2] = time_sort; - } - if (c == 't') { - IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) - sort_function[0] = time_sort; - sort_function[1] = mem_sort; - sort_function[2] = pcpu_sort; - } -# if ENABLE_FEATURE_TOPMEM - if (c == 's') { - scan_mask = TOPMEM_MASK; - free(prev_hist); - prev_hist = NULL; - prev_hist_count = 0; - sort_field = (sort_field + 1) % NUM_SORT_FIELD; - } - if (c == 'r') - inverted ^= 1; -# endif -# if ENABLE_FEATURE_TOP_SMP_CPU - /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ - if (c == 'c' || c == '1') { - /* User wants to toggle per cpu <> aggregate */ - if (smp_cpu_info) { - free(cpu_prev_jif); - free(cpu_jif); - cpu_jif = &cur_jif; - cpu_prev_jif = &prev_jif; - } else { - /* Prepare for xrealloc() */ - cpu_jif = cpu_prev_jif = NULL; - } - num_cpus = 0; - smp_cpu_info = !smp_cpu_info; - get_jiffy_counts(); - } -# endif -# endif - } + else + scan_mask = handle_input(scan_mask, interval); #endif /* FEATURE_USE_TERMIOS */ - } /* end of "while (1)" */ + } /* end of "while (not Q)" */ bb_putchar('\n'); #if ENABLE_FEATURE_USE_TERMIOS -- cgit v1.2.3-55-g6feb From 4d6059eedc665b46c274f40067af20e3aad46894 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 6 May 2011 20:47:54 +0200 Subject: top: another scripting improvement function old new delta handle_input 492 493 +1 Signed-off-by: Denys Vlasenko --- procps/top.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/procps/top.c b/procps/top.c index ed8b56173..c46797e78 100644 --- a/procps/top.c +++ b/procps/top.c @@ -856,24 +856,22 @@ enum { #if ENABLE_FEATURE_USE_TERMIOS static unsigned handle_input(unsigned scan_mask, unsigned interval) { - unsigned char c, *p, buf[64]; - int len; + unsigned char c; struct pollfd pfd[1]; pfd[0].fd = 0; pfd[0].events = POLLIN; - if (safe_poll(pfd, 1, interval * 1000) <= 0) - return scan_mask; - len = safe_read(STDIN_FILENO, &buf, sizeof(buf)-1); - if (len <= 0) { /* error/EOF? */ - option_mask32 |= OPT_EOF; - return scan_mask; - } + while (1) { + if (safe_poll(pfd, 1, interval * 1000) <= 0) + return scan_mask; + interval = 0; + + if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ + option_mask32 |= OPT_EOF; + return scan_mask; + } - buf[len] = 0; - p = buf; - while ((c = *p++) != 0) { if (c == initial_settings.c_cc[VINTR]) return EXIT_MASK; if (c == initial_settings.c_cc[VEOF]) @@ -881,9 +879,11 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) c |= 0x20; /* lowercase */ if (c == 'q') return EXIT_MASK; + if (c == 'n') { IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) sort_function[0] = pid_sort; + continue; } if (c == 'm') { IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) @@ -892,12 +892,14 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) sort_function[1] = pcpu_sort; sort_function[2] = time_sort; # endif + continue; } # if ENABLE_FEATURE_SHOW_THREADS if (c == 'h' IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) ) { scan_mask ^= PSSCAN_TASKS; + continue; } # endif # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE @@ -906,12 +908,14 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) sort_function[0] = pcpu_sort; sort_function[1] = mem_sort; sort_function[2] = time_sort; + continue; } if (c == 't') { IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) sort_function[0] = time_sort; sort_function[1] = mem_sort; sort_function[2] = pcpu_sort; + continue; } # if ENABLE_FEATURE_TOPMEM if (c == 's') { @@ -920,10 +924,13 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) prev_hist = NULL; prev_hist_count = 0; sort_field = (sort_field + 1) % NUM_SORT_FIELD; + continue; } # endif - if (c == 'r') + if (c == 'r') { inverted ^= 1; + continue; + } # if ENABLE_FEATURE_TOP_SMP_CPU /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ if (c == 'c' || c == '1') { @@ -940,9 +947,11 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) num_cpus = 0; smp_cpu_info = !smp_cpu_info; get_jiffy_counts(); + continue; } # endif # endif + break; /* unknown key -> force refresh */ } return scan_mask; @@ -991,7 +1000,9 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval) * echo sss | ./busybox top * - shows memory screen * echo sss | ./busybox top -bn1 >mem - * - saves memory screen - the *whole* list, not first NROWS porcesses! + * - saves memory screen - the *whole* list, not first NROWS processes! + * echo .m.s.s.s.s.s.s.q | ./busybox top -b >z + * - saves several different screens, and exits * * TODO: -i STRING param as a better alternative? */ @@ -1165,8 +1176,8 @@ int top_main(int argc UNUSED_PARAM, char **argv) #if !ENABLE_FEATURE_USE_TERMIOS sleep(interval); #else - if (option_mask32 & (OPT_b|OPT_EOF)) - /* batch mode, or EOF on stdin ("top Date: Sat, 7 May 2011 04:36:46 +0200 Subject: preparatory patch Signed-off-by: Denys Vlasenko --- procps/iostat.c | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/procps/iostat.c b/procps/iostat.c index 06a33eb1d..28bf4dec2 100644 --- a/procps/iostat.c +++ b/procps/iostat.c @@ -128,6 +128,23 @@ static void print_timestamp(void) printf("%s\n", buf); } +static cputime_t get_smp_uptime(void) +{ + FILE *fp; + char buf[sizeof(long)*3 * 2 + 4]; + unsigned long sec, dec; + + fp = xfopen_for_read("/proc/uptime"); + + if (fgets(buf, sizeof(buf), fp)) + if (sscanf(buf, "%lu.%lu", &sec, &dec) != 2) + bb_error_msg_and_die("can't read /proc/uptime"); + + fclose(fp); + + return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100; +} + /* Fetch CPU statistics from /proc/stat */ static void get_cpu_statistics(struct stats_cpu *sc) { @@ -153,23 +170,6 @@ static void get_cpu_statistics(struct stats_cpu *sc) fclose(fp); } -static cputime_t get_smp_uptime(void) -{ - FILE *fp; - char buf[sizeof(long)*3 * 2 + 4]; - unsigned long sec, dec; - - fp = xfopen_for_read("/proc/uptime"); - - if (fgets(buf, sizeof(buf), fp)) - if (sscanf(buf, "%lu.%lu", &sec, &dec) != 2) - bb_error_msg_and_die("can't read /proc/uptime"); - - fclose(fp); - - return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100; -} - /* * Obtain current uptime in jiffies. * Uptime is sum of individual CPUs' uptimes. @@ -507,11 +507,12 @@ int iostat_main(int argc, char **argv) /* Store device names into device list */ while (*argv && !isdigit(*argv[0])) { - if (strcmp(*argv, "ALL") != 0) + if (strcmp(*argv, "ALL") != 0) { /* If not ALL, save device name */ save_to_devlist(*argv); - else + } else { G.show_all = 1; + } argv++; } -- cgit v1.2.3-55-g6feb From 29c54aa9f9e8eedf2d227550a790e76661ce63ad Mon Sep 17 00:00:00 2001 From: Maksym Kryzhanovskyy Date: Sat, 7 May 2011 04:37:22 +0200 Subject: iostat: code shrink ~0.5k Signed-off-by: Maksym Kryzhanovskyy Signed-off-by: Denys Vlasenko --- e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c | 6 +- e2fsprogs/old_e2fsprogs/ext2fs/getsize.c | 6 +- procps/iostat.c | 306 +++++++++++--------------- 3 files changed, 131 insertions(+), 187 deletions(-) diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c index 941efa42c..e1f6ba6d2 100644 --- a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c +++ b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c @@ -96,9 +96,9 @@ blkid_loff_t blkid_get_dev_size(int fd) #ifdef BLKGETSIZE64 #ifdef __linux__ - if ((uname(&ut) == 0) && - ((ut.release[0] == '2') && (ut.release[1] == '.') && - (ut.release[2] < '6') && (ut.release[3] == '.'))) + uname(&ut); + if ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] < '6') && (ut.release[3] == '.')) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c index ff11fe98c..ee4bbb7b0 100644 --- a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c +++ b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c @@ -174,9 +174,9 @@ errcode_t ext2fs_get_device_size(const char *file, int blocksize, #ifdef BLKGETSIZE64 #ifdef __linux__ - if ((uname(&ut) == 0) && - ((ut.release[0] == '2') && (ut.release[1] == '.') && - (ut.release[2] < '6') && (ut.release[3] == '.'))) + uname(&ut); + if ((ut.release[0] == '2') && (ut.release[1] == '.') && + (ut.release[2] < '6') && (ut.release[3] == '.')) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && diff --git a/procps/iostat.c b/procps/iostat.c index 28bf4dec2..cd233c72f 100644 --- a/procps/iostat.c +++ b/procps/iostat.c @@ -24,8 +24,6 @@ #define debug(fmt, ...) ((void)0) #define MAX_DEVICE_NAME 12 -#define CURRENT 0 -#define LAST 1 #if 1 typedef unsigned long long cputime_t; @@ -39,18 +37,33 @@ typedef long icputime_t; # define CPUTIME_MAX (~0UL) #endif -struct stats_cpu { - cputime_t cpu_user; - cputime_t cpu_nice; - cputime_t cpu_system; - cputime_t cpu_idle; - cputime_t cpu_iowait; - cputime_t cpu_steal; - cputime_t cpu_irq; - cputime_t cpu_softirq; - cputime_t cpu_guest; +enum { + STATS_CPU_USER, + STATS_CPU_NICE, + STATS_CPU_SYSTEM, + STATS_CPU_IDLE, + STATS_CPU_IOWAIT, + STATS_CPU_IRQ, + STATS_CPU_SOFTIRQ, + STATS_CPU_STEAL, + STATS_CPU_GUEST, + + GLOBAL_UPTIME, + SMP_UPTIME, + + N_STATS_CPU, }; +typedef struct { + cputime_t vector[N_STATS_CPU]; +} stats_cpu_t; + +typedef struct { + stats_cpu_t *prev; + stats_cpu_t *curr; + cputime_t itv; +} stats_cpu_pair_t; + struct stats_dev { char dname[MAX_DEVICE_NAME]; unsigned long long rd_sectors; @@ -59,24 +72,24 @@ struct stats_dev { unsigned long wr_ops; }; -/* List of devices entered on the command line */ -struct device_list { - char dname[MAX_DEVICE_NAME]; -}; - /* Globals. Sort by size and access frequency. */ struct globals { smallint show_all; - unsigned devlist_i; /* Index to the list of devices */ unsigned total_cpus; /* Number of CPUs */ unsigned clk_tck; /* Number of clock ticks per second */ - struct device_list *dlist; + llist_t *dev_list; /* List of devices entered on the command line */ struct stats_dev *saved_stats_dev; struct tm tmtime; + struct { + const char *str; + unsigned div; + } unit; }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + G.unit.str = "Blk"; \ + G.unit.div = 1; \ } while (0) /* Must match option string! */ @@ -104,12 +117,12 @@ static void print_header(void) char buf[16]; struct utsname uts; - if (uname(&uts) < 0) - bb_perror_msg_and_die("uname"); + uname(&uts); /* never fails */ + /* Date representation for the current locale */ strftime(buf, sizeof(buf), "%x", &G.tmtime); - printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n\n", + printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n", uts.sysname, uts.release, uts.nodename, buf, uts.machine, G.total_cpus); } @@ -124,6 +137,8 @@ static void get_localtime(struct tm *ptm) static void print_timestamp(void) { char buf[20]; + /* %x: date representation for the current locale */ + /* %X: time representation for the current locale */ strftime(buf, sizeof(buf), "%x %X", &G.tmtime); printf("%s\n", buf); } @@ -131,14 +146,12 @@ static void print_timestamp(void) static cputime_t get_smp_uptime(void) { FILE *fp; - char buf[sizeof(long)*3 * 2 + 4]; unsigned long sec, dec; fp = xfopen_for_read("/proc/uptime"); - if (fgets(buf, sizeof(buf), fp)) - if (sscanf(buf, "%lu.%lu", &sec, &dec) != 2) - bb_error_msg_and_die("can't read /proc/uptime"); + if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2) + bb_error_msg_and_die("can't read '%s'", "/proc/uptime"); fclose(fp); @@ -146,10 +159,10 @@ static cputime_t get_smp_uptime(void) } /* Fetch CPU statistics from /proc/stat */ -static void get_cpu_statistics(struct stats_cpu *sc) +static void get_cpu_statistics(stats_cpu_t *sc) { FILE *fp; - char buf[1024]; + char buf[1024], *ibuf = buf + 4; fp = xfopen_for_read("/proc/stat"); @@ -157,28 +170,25 @@ static void get_cpu_statistics(struct stats_cpu *sc) while (fgets(buf, sizeof(buf), fp)) { /* Does the line starts with "cpu "? */ - if (starts_with_cpu(buf) && buf[3] == ' ') { - sscanf(buf + 4 + 1, - "%"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %" - FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u", - &sc->cpu_user, &sc->cpu_nice, &sc->cpu_system, - &sc->cpu_idle, &sc->cpu_iowait, &sc->cpu_irq, - &sc->cpu_softirq, &sc->cpu_steal, &sc->cpu_guest); + if (!starts_with_cpu(buf) || buf[3] != ' ') { + continue; } + for (int i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) { + ibuf = skip_whitespace(ibuf); + sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]); + if (i != STATS_CPU_GUEST) { + sc->vector[GLOBAL_UPTIME] += sc->vector[i]; + } + ibuf = skip_non_whitespace(ibuf); + } + break; } - fclose(fp); -} + if (this_is_smp()) { + sc->vector[SMP_UPTIME] = get_smp_uptime(); + } -/* - * Obtain current uptime in jiffies. - * Uptime is sum of individual CPUs' uptimes. - */ -static cputime_t get_uptime(const struct stats_cpu *sc) -{ - /* NB: Don't include cpu_guest, it is already in cpu_user */ - return sc->cpu_user + sc->cpu_nice + sc->cpu_system + sc->cpu_idle + - + sc->cpu_iowait + sc->cpu_irq + sc->cpu_steal + sc->cpu_softirq; + fclose(fp); } static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new) @@ -219,65 +229,51 @@ static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv) return ((double)overflow_safe_sub(prev, curr)) / itv * 100; } -static void print_stats_cpu_struct(const struct stats_cpu *p, - const struct stats_cpu *c, cputime_t itv) +static void print_stats_cpu_struct(stats_cpu_pair_t *stats) { + cputime_t *p = stats->prev->vector; + cputime_t *c = stats->curr->vector; printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n", - percent_value(p->cpu_user , c->cpu_user , itv), - percent_value(p->cpu_nice , c->cpu_nice , itv), - percent_value(p->cpu_system + p->cpu_softirq + p->cpu_irq, - c->cpu_system + c->cpu_softirq + c->cpu_irq, itv), - percent_value(p->cpu_iowait , c->cpu_iowait , itv), - percent_value(p->cpu_steal , c->cpu_steal , itv), - percent_value(p->cpu_idle , c->cpu_idle , itv) + percent_value(p[STATS_CPU_USER] , c[STATS_CPU_USER] , stats->itv), + percent_value(p[STATS_CPU_NICE] , c[STATS_CPU_NICE] , stats->itv), + percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ], + c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv), + percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv), + percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv), + percent_value(p[STATS_CPU_IDLE] , c[STATS_CPU_IDLE] , stats->itv) ); } static void print_stats_dev_struct(const struct stats_dev *p, const struct stats_dev *c, cputime_t itv) { - int unit = 1; - - if (option_mask32 & OPT_k) - unit = 2; - else if (option_mask32 & OPT_m) - unit = 2048; - if (option_mask32 & OPT_z) if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops) return; - printf("%-13s", c->dname); - printf(" %8.2f %12.2f %12.2f %10llu %10llu \n", + printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu \n", c->dname, percent_value(p->rd_ops + p->wr_ops , /**/ c->rd_ops + c->wr_ops , itv), - percent_value(p->rd_sectors, c->rd_sectors, itv) / unit, - percent_value(p->wr_sectors, c->wr_sectors, itv) / unit, - (c->rd_sectors - p->rd_sectors) / unit, - (c->wr_sectors - p->wr_sectors) / unit); + percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div, + percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div, + (c->rd_sectors - p->rd_sectors) / G.unit.div, + (c->wr_sectors - p->wr_sectors) / G.unit.div); } -static void cpu_report(const struct stats_cpu *last, - const struct stats_cpu *cur, - cputime_t itv) +static void cpu_report(stats_cpu_pair_t *stats) { /* Always print a header */ puts("avg-cpu: %user %nice %system %iowait %steal %idle"); /* Print current statistics */ - print_stats_cpu_struct(last, cur, itv); + print_stats_cpu_struct(stats); } static void print_devstat_header(void) { - printf("Device: tps"); - - if (option_mask32 & OPT_m) - puts(" MB_read/s MB_wrtn/s MB_read MB_wrtn"); - else if (option_mask32 & OPT_k) - puts(" kB_read/s kB_wrtn/s kB_read kB_wrtn"); - else - puts(" Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn"); + printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n", "tps", + G.unit.str, "_read", G.unit.str, "_wrtn", + G.unit.str, "_read", G.unit.str, "_wrtn"); } /* @@ -289,37 +285,6 @@ static int is_partition(const char *dev) return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]); } -/* - * Return number of numbers on cmdline. - * Reasonable values are only 0 (no interval/count specified), - * 1 (interval specified) and 2 (both interval and count specified) - */ -static int numbers_on_cmdline(int argc, char *argv[]) -{ - int sum = 0; - - if (isdigit(argv[argc-1][0])) - sum++; - if (argc > 2 && isdigit(argv[argc-2][0])) - sum++; - - return sum; -} - -static int is_dev_in_dlist(const char *dev) -{ - int i; - - /* Go through the device list */ - for (i = 0; i < G.devlist_i; i++) - if (strcmp(G.dlist[i].dname, dev) == 0) - /* Found a match */ - return 1; - - /* No match found */ - return 0; -} - static void do_disk_statistics(cputime_t itv) { FILE *fp; @@ -356,7 +321,7 @@ static void do_disk_statistics(cputime_t itv) break; } - if (!G.devlist_i && !is_partition(sd.dname)) { + if (!G.dev_list && !is_partition(sd.dname)) { /* User didn't specify device */ if (!G.show_all && !sd.rd_ops && !sd.wr_ops) { /* Don't print unused device */ @@ -367,7 +332,7 @@ static void do_disk_statistics(cputime_t itv) i++; } else { /* Is device in device list? */ - if (is_dev_in_dlist(sd.dname)) { + if (llist_find_str(G.dev_list, sd.dname)) { /* Print current statistics */ print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv); G.saved_stats_dev[i] = sd; @@ -389,28 +354,6 @@ static void dev_report(cputime_t itv) do_disk_statistics(itv); } -static void save_to_devlist(const char *dname) -{ - int i; - struct device_list *tmp = G.dlist; - - if (strncmp(dname, "/dev/", 5) == 0) - /* We'll ignore prefix '/dev/' */ - dname += 5; - - /* Go through the list */ - for (i = 0; i < G.devlist_i; i++, tmp++) - if (strcmp(tmp->dname, dname) == 0) - /* Already in the list */ - return; - - /* Add device name to the list */ - strncpy(tmp->dname, dname, MAX_DEVICE_NAME - 1); - - /* Update device list index */ - G.devlist_i++; -} - static unsigned get_number_of_devices(void) { FILE *fp; @@ -440,18 +383,6 @@ static unsigned get_number_of_devices(void) return n; } -static int number_of_ALL_on_cmdline(char **argv) -{ - int alls = 0; - - /* Iterate over cmd line arguments, count "ALL" */ - while (*argv) - if (strcmp(*argv++, "ALL") == 0) - alls++; - - return alls; -} - //usage:#define iostat_trivial_usage //usage: "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]" //usage:#define iostat_full_usage "\n\n" @@ -465,19 +396,17 @@ static int number_of_ALL_on_cmdline(char **argv) //usage: "\n -m Use Mb/s" int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int iostat_main(int argc, char **argv) +int iostat_main(int argc UNUSED_PARAM, char **argv) { int opt, dev_num; - unsigned interval = 0; + unsigned interval; int count; - cputime_t global_uptime[2] = { 0 }; - cputime_t smp_uptime[2] = { 0 }; - cputime_t itv; - struct stats_cpu stats_cur, stats_last; + stats_cpu_t stats_data[2]; + smallint current_stats; INIT_G(); - memset(&stats_last, 0, sizeof(stats_last)); + memset(&stats_data, 0, sizeof(stats_data)); /* Get number of clock ticks per sec */ G.clk_tck = get_user_hz(); @@ -496,26 +425,24 @@ int iostat_main(int argc, char **argv) opt |= OPT_c + OPT_d; argv += optind; - argc -= optind; - - dev_num = argc - numbers_on_cmdline(argc, argv); - /* We don't want to allocate space for 'ALL' */ - dev_num -= number_of_ALL_on_cmdline(argv); - if (dev_num > 0) - /* Make space for device list */ - G.dlist = xzalloc(sizeof(G.dlist[0]) * dev_num); /* Store device names into device list */ + dev_num = 0; while (*argv && !isdigit(*argv[0])) { if (strcmp(*argv, "ALL") != 0) { /* If not ALL, save device name */ - save_to_devlist(*argv); + char *dev_name = skip_dev_pfx(*argv); + if (!llist_find_str(G.dev_list, dev_name)) { + llist_add_to(&G.dev_list, dev_name); + dev_num++; + } } else { G.show_all = 1; } argv++; } + interval = 0; count = 1; if (*argv) { /* Get interval */ @@ -534,28 +461,46 @@ int iostat_main(int argc, char **argv) ); } + if (opt & OPT_m) { + G.unit.str = " MB"; + G.unit.div = 2048; + } + + if (opt & OPT_k) { + G.unit.str = " kB"; + G.unit.div = 2; + } + + get_localtime(&G.tmtime); + /* Display header */ print_header(); + current_stats = 0; /* Main loop */ for (;;) { + stats_cpu_pair_t stats; + + stats.prev = &stats_data[current_stats ^ 1]; + stats.curr = &stats_data[current_stats]; + /* Fill the time structure */ get_localtime(&G.tmtime); /* Fetch current CPU statistics */ - get_cpu_statistics(&stats_cur); - - /* Fetch current uptime */ - global_uptime[CURRENT] = get_uptime(&stats_cur); + get_cpu_statistics(stats.curr); /* Get interval */ - itv = get_interval(global_uptime[LAST], global_uptime[CURRENT]); + stats.itv = get_interval( + stats.prev->vector[GLOBAL_UPTIME], + stats.curr->vector[GLOBAL_UPTIME] + ); if (opt & OPT_t) print_timestamp(); if (opt & OPT_c) { - cpu_report(&stats_last, &stats_cur, itv); + cpu_report(&stats); if (opt & OPT_d) /* Separate outputs by a newline */ bb_putchar('\n'); @@ -563,32 +508,31 @@ int iostat_main(int argc, char **argv) if (opt & OPT_d) { if (this_is_smp()) { - smp_uptime[CURRENT] = get_smp_uptime(); - itv = get_interval(smp_uptime[LAST], smp_uptime[CURRENT]); - smp_uptime[LAST] = smp_uptime[CURRENT]; + stats.itv = get_interval( + stats.prev->vector[SMP_UPTIME], + stats.curr->vector[SMP_UPTIME] + ); } - dev_report(itv); + dev_report(stats.itv); } + bb_putchar('\n'); + if (count > 0) { if (--count == 0) break; } - /* Backup current stats */ - global_uptime[LAST] = global_uptime[CURRENT]; - stats_last = stats_cur; + /* Swap stats */ + current_stats ^= 1; - bb_putchar('\n'); sleep(interval); } - bb_putchar('\n'); - if (ENABLE_FEATURE_CLEAN_UP) { - free(&G); - free(G.dlist); + llist_free(G.dev_list, NULL); free(G.saved_stats_dev); + free(&G); } return EXIT_SUCCESS; -- cgit v1.2.3-55-g6feb From b8709032a3fb57b3ec536bdf9b92b526ed63b995 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 8 May 2011 21:20:01 +0200 Subject: hush: fix incorrect PS2 dispaly and trap handling while reading command The fix affects only !ENABLE_FEATURE_EDITING configuration Signed-off-by: Denys Vlasenko --- shell/hush.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index d3e957c2f..bcd458427 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1319,6 +1319,8 @@ static void restore_G_args(save_arg_t *sv, char **argv) * "echo $$; sleep 5 & wait; ls -l" + "kill -INT " * Example 3: this does not wait 5 sec, but executes ls: * "sleep 5; ls -l" + press ^C + * Example 4: this does not wait and does not execute ls: + * "sleep 5 & wait; ls -l" + press ^C * * (What happens to signals which are IGN on shell start?) * (What happens with signal mask on shell start?) @@ -1471,13 +1473,13 @@ static int check_and_run_traps(int sig) int last_sig = 0; if (sig) - goto jump_in; + goto got_sig; + while (1) { sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); if (sig <= 0) break; - jump_in: - last_sig = sig; + got_sig: if (G.traps && G.traps[sig]) { if (G.traps[sig][0]) { /* We have user-defined handler */ @@ -1488,6 +1490,7 @@ static int check_and_run_traps(int sig) save_rcode = G.last_exitcode; builtin_eval(argv); G.last_exitcode = save_rcode; + last_sig = sig; } /* else: "" trap, ignoring signal */ continue; } @@ -1503,6 +1506,7 @@ static int check_and_run_traps(int sig) /* Builtin was ^C'ed, make it look prettier: */ bb_putchar('\n'); G.flag_SIGINT = 1; + last_sig = sig; break; #if ENABLE_HUSH_JOB case SIGHUP: { @@ -1521,6 +1525,11 @@ static int check_and_run_traps(int sig) #endif default: /* ignored: */ /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ + /* note: + * we dont do 'last_sig = sig' here -> NOT returning this sig. + * example: wait is not interrupted by TERM + * in interactive shell, because TERM is ignored. + */ break; } } @@ -1921,11 +1930,18 @@ static void get_user_input(struct in_str *i) # else do { G.flag_SIGINT = 0; - fputs(prompt_str, stdout); + if (i->last_char == '\0' || i->last_char == '\n') { + /* Why check_and_run_traps here? Try this interactively: + * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) & + * $ <[enter], repeatedly...> + * Without check_and_run_traps, handler never runs. + */ + check_and_run_traps(0); + fputs(prompt_str, stdout); + } fflush_all(); G.user_input_buf[0] = r = fgetc(i->file); /*G.user_input_buf[1] = '\0'; - already is and never changed */ -//do we need check_and_run_traps(0)? (maybe only if stdin) } while (G.flag_SIGINT); i->eof_flag = (r == EOF); # endif @@ -3322,6 +3338,7 @@ static char *fetch_till_str(o_string *as_string, int ch; goto jump_in; + while (1) { ch = i_getch(input); if (ch != EOF) -- cgit v1.2.3-55-g6feb From 80c5b6893d4708b3683ad9a51c990a326a8f1dff Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 8 May 2011 21:21:10 +0200 Subject: libbb: nonblock_safe_read->nonblock_immune_read, remove unused param of xmalloc_reads Signed-off-by: Denys Vlasenko --- editors/patch.c | 2 +- include/libbb.h | 4 ++-- libbb/read_printf.c | 12 +++++++----- mailutils/mail.c | 4 ++-- printutils/lpd.c | 2 +- shell/ash.c | 8 ++++---- shell/shell_common.c | 2 +- util-linux/acpid.c | 2 +- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/editors/patch.c b/editors/patch.c index a90252a03..6d3f319b0 100644 --- a/editors/patch.c +++ b/editors/patch.c @@ -239,7 +239,7 @@ static int apply_one_hunk(void) plist = TT.current_hunk; buf = NULL; if (reverse ? TT.oldlen : TT.newlen) for (;;) { - char *data = xmalloc_reads(TT.filein, NULL, NULL); + char *data = xmalloc_reads(TT.filein, NULL); TT.linenum++; diff --git a/include/libbb.h b/include/libbb.h index 34f7f6a8b..4ea94e77f 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -672,7 +672,7 @@ void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) F extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; -extern ssize_t nonblock_safe_read(int fd, void *buf, size_t count) FAST_FUNC; +extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count) FAST_FUNC; // NB: will return short read on error, not -1, // if some data was read before error occurred extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; @@ -683,7 +683,7 @@ extern ssize_t open_read_close(const char *filename, void *buf, size_t maxsz) FA // Reads one line a-la fgets (but doesn't save terminating '\n'). // Reads byte-by-byte. Useful when it is important to not read ahead. // Bytes are appended to pfx (which must be malloced, or NULL). -extern char *xmalloc_reads(int fd, char *pfx, size_t *maxsz_p) FAST_FUNC; +extern char *xmalloc_reads(int fd, size_t *maxsz_p) FAST_FUNC; /* Reads block up to *maxsz_p (default: INT_MAX - 4095) */ extern void *xmalloc_read(int fd, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC; /* Returns NULL if file can't be opened (default max size: INT_MAX - 4095) */ diff --git a/libbb/read_printf.c b/libbb/read_printf.c index 8664bc625..0e6fbf662 100644 --- a/libbb/read_printf.c +++ b/libbb/read_printf.c @@ -55,7 +55,7 @@ * which detects EAGAIN and uses poll() to wait on the fd. * Thankfully, poll() doesn't care about O_NONBLOCK flag. */ -ssize_t FAST_FUNC nonblock_safe_read(int fd, void *buf, size_t count) +ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count) { struct pollfd pfd[1]; ssize_t n; @@ -74,13 +74,15 @@ ssize_t FAST_FUNC nonblock_safe_read(int fd, void *buf, size_t count) // Reads one line a-la fgets (but doesn't save terminating '\n'). // Reads byte-by-byte. Useful when it is important to not read ahead. // Bytes are appended to pfx (which must be malloced, or NULL). -char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p) +char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p) { char *p; - size_t sz = buf ? strlen(buf) : 0; + char *buf = NULL; + size_t sz = 0; size_t maxsz = maxsz_p ? *maxsz_p : (INT_MAX - 4095); goto jump_in; + while (sz < maxsz) { if ((size_t)(p - buf) == sz) { jump_in: @@ -88,8 +90,8 @@ char* FAST_FUNC xmalloc_reads(int fd, char *buf, size_t *maxsz_p) p = buf + sz; sz += 128; } - /* nonblock_safe_read() because we are used by e.g. shells */ - if (nonblock_safe_read(fd, p, 1) != 1) { /* EOF/error */ + if (nonblock_immune_read(fd, p, 1) != 1) { + /* EOF/error */ if (p == buf) { /* we read nothing */ free(buf); return NULL; diff --git a/mailutils/mail.c b/mailutils/mail.c index 44957016f..66c79471f 100644 --- a/mailutils/mail.c +++ b/mailutils/mail.c @@ -172,8 +172,8 @@ void FAST_FUNC get_cred_or_die(int fd) G.user = xstrdup(bb_ask(fd, /* timeout: */ 0, "User: ")); G.pass = xstrdup(bb_ask(fd, /* timeout: */ 0, "Password: ")); } else { - G.user = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL); - G.pass = xmalloc_reads(fd, /* pfx: */ NULL, /* maxsize: */ NULL); + G.user = xmalloc_reads(fd, /* maxsize: */ NULL); + G.pass = xmalloc_reads(fd, /* maxsize: */ NULL); } if (!G.user || !*G.user || !G.pass) bb_error_msg_and_die("no username or password"); diff --git a/printutils/lpd.c b/printutils/lpd.c index 115552e0b..642e8a89e 100644 --- a/printutils/lpd.c +++ b/printutils/lpd.c @@ -102,7 +102,7 @@ static char *xmalloc_read_stdin(void) { // SECURITY: size_t max = 4 * 1024; // more than enough for commands! - return xmalloc_reads(STDIN_FILENO, NULL, &max); + return xmalloc_reads(STDIN_FILENO, &max); } int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; diff --git a/shell/ash.c b/shell/ash.c index b50e0952e..b1b11bd1b 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5918,7 +5918,7 @@ expbackq(union node *cmd, int quoted, int quotes) read: if (in.fd < 0) break; - i = nonblock_safe_read(in.fd, buf, sizeof(buf)); + i = nonblock_immune_read(in.fd, buf, sizeof(buf)); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; @@ -9617,7 +9617,7 @@ preadfd(void) #if ENABLE_FEATURE_EDITING retry: if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) - nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); + nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); else { int timeout = -1; # if ENABLE_ASH_IDLE_TIMEOUT @@ -9663,10 +9663,10 @@ preadfd(void) } } #else - nr = nonblock_safe_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); + nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); #endif -#if 0 /* disabled: nonblock_safe_read() handles this problem */ +#if 0 /* disabled: nonblock_immune_read() handles this problem */ if (nr < 0) { if (parsefile->fd == 0 && errno == EWOULDBLOCK) { int flags = fcntl(0, F_GETFL); diff --git a/shell/shell_common.c b/shell/shell_common.c index 68659abd3..86a6493ed 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -170,7 +170,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), if ((bufpos & 0xff) == 0) buffer = xrealloc(buffer, bufpos + 0x100); - if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) { + if (nonblock_immune_read(fd, &buffer[bufpos], 1) != 1) { retval = (const char *)(uintptr_t)1; break; } diff --git a/util-linux/acpid.c b/util-linux/acpid.c index c9eed2a7f..4b7e5cacb 100644 --- a/util-linux/acpid.c +++ b/util-linux/acpid.c @@ -283,7 +283,7 @@ int acpid_main(int argc UNUSED_PARAM, char **argv) char *buf; int len; - buf = xmalloc_reads(pfd[i].fd, NULL, NULL); + buf = xmalloc_reads(pfd[i].fd, NULL); /* buf = "button/power PWRB 00000080 00000000" */ len = strlen(buf) - 9; if (len >= 0) -- cgit v1.2.3-55-g6feb From 80542bad2f1df9d99b579c9eeb3c2675c14c72c0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 8 May 2011 21:23:43 +0200 Subject: hush: make read builtin interruptible. function old new delta builtin_read 185 471 +286 check_and_run_traps 200 262 +62 nonblock_immune_read 73 119 +46 sigismember - 44 +44 record_signal - 21 +21 sigisemptyset - 16 +16 ... ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 7/5 up/down: 483/-46) Total: 437 bytes Signed-off-by: Denys Vlasenko --- include/libbb.h | 2 +- libbb/read_printf.c | 9 ++--- shell/ash.c | 6 ++-- shell/hush.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++-- shell/shell_common.c | 17 +++++++-- 5 files changed, 120 insertions(+), 12 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 4ea94e77f..56dfa61b7 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -672,7 +672,7 @@ void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) F extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC; -extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count) FAST_FUNC; +extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC; // NB: will return short read on error, not -1, // if some data was read before error occurred extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC; diff --git a/libbb/read_printf.c b/libbb/read_printf.c index 0e6fbf662..192f83d6e 100644 --- a/libbb/read_printf.c +++ b/libbb/read_printf.c @@ -55,19 +55,20 @@ * which detects EAGAIN and uses poll() to wait on the fd. * Thankfully, poll() doesn't care about O_NONBLOCK flag. */ -ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count) +ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) { struct pollfd pfd[1]; ssize_t n; while (1) { - n = safe_read(fd, buf, count); + n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count); if (n >= 0 || errno != EAGAIN) return n; /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ pfd[0].fd = fd; pfd[0].events = POLLIN; - safe_poll(pfd, 1, -1); /* note: this pulls in printf */ + /* note: safe_poll pulls in printf */ + loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1); } } @@ -90,7 +91,7 @@ char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p) p = buf + sz; sz += 128; } - if (nonblock_immune_read(fd, p, 1) != 1) { + if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) { /* EOF/error */ if (p == buf) { /* we read nothing */ free(buf); diff --git a/shell/ash.c b/shell/ash.c index b1b11bd1b..d48cd016f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5918,7 +5918,7 @@ expbackq(union node *cmd, int quoted, int quotes) read: if (in.fd < 0) break; - i = nonblock_immune_read(in.fd, buf, sizeof(buf)); + i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; @@ -9617,7 +9617,7 @@ preadfd(void) #if ENABLE_FEATURE_EDITING retry: if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) - nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); + nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); else { int timeout = -1; # if ENABLE_ASH_IDLE_TIMEOUT @@ -9663,7 +9663,7 @@ preadfd(void) } } #else - nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); + nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1); #endif #if 0 /* disabled: nonblock_immune_read() handles this problem */ diff --git a/shell/hush.c b/shell/hush.c index bcd458427..0b17b222d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -795,8 +795,15 @@ struct globals { /* which signals have non-DFL handler (even with no traps set)? */ unsigned non_DFL_mask; char **traps; /* char *traps[NSIG] */ - sigset_t blocked_set; + /* Signal mask on the entry to the (top-level) shell. Never modified. */ sigset_t inherited_set; + /* Starts equal to inherited_set, + * but shell-special signals are added and SIGCHLD is removed. + * When a trap is set/cleared, signal is added to/removed from it: + */ + sigset_t blocked_set; + /* Used by read() */ + sigset_t detected_set; #if HUSH_DEBUG unsigned long memleak_value; int debug_indent; @@ -1476,6 +1483,17 @@ static int check_and_run_traps(int sig) goto got_sig; while (1) { + if (!sigisemptyset(&G.detected_set)) { + sig = 0; + do { + sig++; + if (sigismember(&G.detected_set, sig)) { + sigdelset(&G.detected_set, sig); + goto got_sig; + } + } while (sig < NSIG); + } + sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); if (sig <= 0) break; @@ -8484,6 +8502,32 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM) return EXIT_SUCCESS; } +/* Interruptibility of read builtin in bash + * (tested on bash-4.2.8 by sending signals (not by ^C)): + * + * Empty trap makes read ignore corresponding signal, for any signal. + * + * SIGINT: + * - terminates non-interactive shell; + * - interrupts read in interactive shell; + * if it has non-empty trap: + * - executes trap and returns to command prompt in interactive shell; + * - executes trap and returns to read in non-interactive shell; + * SIGTERM: + * - is ignored (does not interrupt) read in interactive shell; + * - terminates non-interactive shell; + * if it has non-empty trap: + * - executes trap and returns to read; + * SIGHUP: + * - terminates shell (regardless of interactivity); + * if it has non-empty trap: + * - executes trap and returns to read; + */ +/* helper */ +static void record_signal(int sig) +{ + sigaddset(&G.detected_set, sig); +} static int FAST_FUNC builtin_read(char **argv) { const char *r; @@ -8491,7 +8535,9 @@ static int FAST_FUNC builtin_read(char **argv) char *opt_p = NULL; char *opt_t = NULL; char *opt_u = NULL; + const char *ifs; int read_flags; + sigset_t saved_blkd_set; /* "!": do not abort on errors. * Option string must start with "sr" to match BUILTIN_READ_xxx @@ -8500,10 +8546,47 @@ static int FAST_FUNC builtin_read(char **argv) if (read_flags == (uint32_t)-1) return EXIT_FAILURE; argv += optind; + ifs = get_local_var_value("IFS"); /* can be NULL */ + + again: + /* We need to temporarily unblock and record signals around read */ + + saved_blkd_set = G.blocked_set; + { + unsigned sig; + struct sigaction sa, old_sa; + + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + /*sa.sa_flags = 0;*/ + sa.sa_handler = record_signal; + + sig = 0; + do { + sig++; + if (sigismember(&G.blocked_set, sig)) { + char *sig_trap = (G.traps && G.traps[sig]) ? G.traps[sig] : NULL; + /* If has a nonempty trap... */ + if ((sig_trap && sig_trap[0]) + /* ...or has no trap and is SIGINT or SIGHUP */ + || (!sig_trap && (sig == SIGINT || sig == SIGHUP)) + ) { + sigaction(sig, &sa, &old_sa); + if (old_sa.sa_handler == SIG_IGN) /* oops... restore back to IGN! */ + sigaction_set(sig, &old_sa); + else + sigdelset(&G.blocked_set, sig); + } + } + } while (sig < NSIG-1); + } + + if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0) + sigprocmask_set(&G.blocked_set); r = shell_builtin_read(set_local_var_from_halves, argv, - get_local_var_value("IFS"), /* can be NULL */ + ifs, read_flags, opt_n, opt_p, @@ -8511,6 +8594,17 @@ static int FAST_FUNC builtin_read(char **argv) opt_u ); + if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0) { + G.blocked_set = saved_blkd_set; + sigprocmask_set(&G.blocked_set); + } + + if ((uintptr_t)r == 1 && errno == EINTR) { + unsigned sig = check_and_run_traps(0); + if (sig && sig != SIGINT) + goto again; + } + if ((uintptr_t)r > 1) { bb_error_msg("%s", r); r = (char*)(uintptr_t)1; diff --git a/shell/shell_common.c b/shell/shell_common.c index 86a6493ed..a5c455c8e 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -36,6 +36,10 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator) /* read builtin */ +/* Needs to be interruptible: shell mush handle traps and shell-special signals + * while inside read. To implement this, be sure to not loop on EINTR + * and return errno == EINTR reliably. + */ //TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL" //string. hush naturally has it, and ash has setvareq(). //Here we can simply store "VAR=" at buffer start and store read data directly @@ -51,6 +55,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), const char *opt_u ) { + unsigned err; unsigned end_ms; /* -t TIMEOUT */ int fd; /* -u FD */ int nchars; /* -n NUM */ @@ -62,6 +67,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), int startword; smallint backslash; + errno = err = 0; + pp = argv; while (*pp) { if (!is_well_formed_var_name(*pp, '\0')) { @@ -153,6 +160,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), do { char c; + errno = 0; + if (end_ms) { int timeout; struct pollfd pfd[1]; @@ -161,8 +170,9 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), pfd[0].events = POLLIN; timeout = end_ms - (unsigned)monotonic_ms(); if (timeout <= 0 /* already late? */ - || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ + || poll(pfd, 1, timeout) != 1 /* no? wait... */ ) { /* timed out! */ + err = errno; retval = (const char *)(uintptr_t)1; goto ret; } @@ -170,7 +180,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), if ((bufpos & 0xff) == 0) buffer = xrealloc(buffer, bufpos + 0x100); - if (nonblock_immune_read(fd, &buffer[bufpos], 1) != 1) { + if (nonblock_immune_read(fd, &buffer[bufpos], 1, /*loop_on_EINTR:*/ 0) != 1) { + err = errno; retval = (const char *)(uintptr_t)1; break; } @@ -240,6 +251,8 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), free(buffer); if (read_flags & BUILTIN_READ_SILENT) tcsetattr(fd, TCSANOW, &old_tty); + + errno = err; return retval; } -- cgit v1.2.3-55-g6feb From 54e9e1217c67563d70d6e2b14021293f707d7927 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 9 May 2011 00:52:15 +0200 Subject: hush: code shrink Signed-off-by: Denys Vlasenko --- shell/hush.c | 125 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 59 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 0b17b222d..71972f751 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1379,6 +1379,11 @@ enum { #endif }; +static void sigprocmask_set(sigset_t *set) +{ + sigprocmask(SIG_SETMASK, set, NULL); +} + #if ENABLE_HUSH_FAST static void SIGCHLD_handler(int sig UNUSED_PARAM) { @@ -5378,18 +5383,15 @@ static void reset_traps_to_defaults(void) * Stupid. It can be done with *single* &= op, but we can't use * the fact that G.blocked_set is implemented as a bitmask * in libc... */ - mask = (SPECIAL_INTERACTIVE_SIGS >> 1); - sig = 1; - while (1) { + mask = SPECIAL_INTERACTIVE_SIGS; + sig = 0; + while ((mask >>= 1) != 0) { + sig++; if (mask & 1) { /* Careful. Only if no trap or trap is not "" */ if (!G.traps || !G.traps[sig] || G.traps[sig][0]) sigdelset(&G.blocked_set, sig); } - mask >>= 1; - if (!mask) - break; - sig++; } /* Our homegrown sig mask is saner to work with :) */ G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; @@ -5411,7 +5413,7 @@ static void reset_traps_to_defaults(void) continue; sigdelset(&G.blocked_set, sig); } - sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); + sigprocmask_set(&G.blocked_set); } #else /* !BB_MMU */ @@ -5541,7 +5543,7 @@ static void re_execute_shell(char ***to_free, const char *s, do_exec: debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); - sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); + sigprocmask_set(&G.inherited_set); execve(bb_busybox_exec_path, argv, pp); /* Fallback. Useful for init=/bin/hush usage etc */ if (argv[0][0] == '/') @@ -6195,7 +6197,7 @@ static void execvp_or_die(char **argv) NORETURN; static void execvp_or_die(char **argv) { debug_printf_exec("execing '%s'\n", argv[0]); - sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); + sigprocmask_set(&G.inherited_set); execvp(argv[0], argv); bb_perror_msg("can't execute '%s'", argv[0]); _exit(127); /* bash compat */ @@ -6327,7 +6329,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, # endif /* Re-exec ourselves */ debug_printf_exec("re-execing applet '%s'\n", argv[0]); - sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); + sigprocmask_set(&G.inherited_set); execv(bb_busybox_exec_path, argv); /* If they called chroot or otherwise made the binary no longer * executable, fall through */ @@ -7435,84 +7437,89 @@ static void init_sigmasks(void) { unsigned sig; unsigned mask; - sigset_t old_blocked_set; + /* POSIX allows shell to re-enable SIGCHLD + * even if it was SIG_IGN on entry */ +#if ENABLE_HUSH_FAST + G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ +#endif if (!G.inherited_set_is_saved) { +#if ENABLE_HUSH_FAST + signal(SIGCHLD, SIGCHLD_handler); +#else + signal(SIGCHLD, SIG_DFL); +#endif sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); G.inherited_set = G.blocked_set; } - old_blocked_set = G.blocked_set; + /* Which signals are shell-special? */ mask = (1 << SIGQUIT); if (G_interactive_fd) { - mask = (1 << SIGQUIT) | SPECIAL_INTERACTIVE_SIGS; + mask |= SPECIAL_INTERACTIVE_SIGS; if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ mask |= SPECIAL_JOB_SIGS; } G.non_DFL_mask = mask; + /* Block them. And unblock SIGCHLD */ sig = 0; - while (mask) { + while ((mask >>= 1) != 0) { + sig++; if (mask & 1) sigaddset(&G.blocked_set, sig); - mask >>= 1; - sig++; } sigdelset(&G.blocked_set, SIGCHLD); - if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0) - sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); - - /* POSIX allows shell to re-enable SIGCHLD - * even if it was SIG_IGN on entry */ -#if ENABLE_HUSH_FAST - G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ - if (!G.inherited_set_is_saved) - signal(SIGCHLD, SIGCHLD_handler); -#else - if (!G.inherited_set_is_saved) - signal(SIGCHLD, SIG_DFL); -#endif + if (memcmp(&G.inherited_set, &G.blocked_set, sizeof(G.inherited_set)) != 0) + sigprocmask_set(&G.blocked_set); G.inherited_set_is_saved = 1; } #if ENABLE_HUSH_JOB /* helper */ -static void maybe_set_to_sigexit(int sig) +/* Set handlers to restore tty pgrp and exit */ +static void set_fatal_handlers_to_sigexit(void) { void (*handler)(int); + unsigned fatal_sigs, sig; + + /* We will restore tty pgrp on these signals */ + fatal_sigs = 0 + + (1 << SIGILL ) * HUSH_DEBUG + + (1 << SIGFPE ) * HUSH_DEBUG + + (1 << SIGBUS ) * HUSH_DEBUG + + (1 << SIGSEGV) * HUSH_DEBUG + + (1 << SIGTRAP) * HUSH_DEBUG + + (1 << SIGABRT) + /* bash 3.2 seems to handle these just like 'fatal' ones */ + + (1 << SIGPIPE) + + (1 << SIGALRM) + /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked. + * if we aren't interactive... but in this case + * we never want to restore pgrp on exit, and this fn is not called */ + /*+ (1 << SIGHUP )*/ + /*+ (1 << SIGTERM)*/ + /*+ (1 << SIGINT )*/ + ; + /* non_DFL_mask'ed signals are, well, masked, * no need to set handler for them. */ - if (!((G.non_DFL_mask >> sig) & 1)) { + fatal_sigs &= ~G.non_DFL_mask; + + /* For each sig in fatal_sigs... */ + sig = 0; + while ((fatal_sigs >>= 1) != 0) { + sig++; + if (!(fatal_sigs & 1)) + continue; handler = signal(sig, sigexit); if (handler == SIG_IGN) /* oops... restore back to IGN! */ signal(sig, handler); } } -/* Set handlers to restore tty pgrp and exit */ -static void set_fatal_handlers(void) -{ - /* We _must_ restore tty pgrp on fatal signals */ - if (HUSH_DEBUG) { - maybe_set_to_sigexit(SIGILL ); - maybe_set_to_sigexit(SIGFPE ); - maybe_set_to_sigexit(SIGBUS ); - maybe_set_to_sigexit(SIGSEGV); - maybe_set_to_sigexit(SIGTRAP); - } /* else: hush is perfect. what SEGV? */ - maybe_set_to_sigexit(SIGABRT); - /* bash 3.2 seems to handle these just like 'fatal' ones */ - maybe_set_to_sigexit(SIGPIPE); - maybe_set_to_sigexit(SIGALRM); - /* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked. - * if we aren't interactive... but in this case - * we never want to restore pgrp on exit, and this fn is not called */ - /*maybe_set_to_sigexit(SIGHUP );*/ - /*maybe_set_to_sigexit(SIGTERM);*/ - /*maybe_set_to_sigexit(SIGINT );*/ -} #endif static int set_mode(int state, char mode, const char *o_opt) @@ -7769,7 +7776,7 @@ int hush_main(int argc, char **argv) sigaddset(&G.blocked_set, sig); } } - sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); + sigprocmask_set(&G.blocked_set); } # if ENABLE_HUSH_LOOPS optarg++; @@ -7910,7 +7917,7 @@ int hush_main(int argc, char **argv) if (G_saved_tty_pgrp) { /* Set other signals to restore saved_tty_pgrp */ - set_fatal_handlers(); + set_fatal_handlers_to_sigexit(); /* Put ourselves in our own process group * (bash, too, does this only if ctty is available) */ bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ @@ -8301,7 +8308,7 @@ static int FAST_FUNC builtin_trap(char **argv) sigdelset(&G.blocked_set, sig); } } - sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); + sigprocmask_set(&G.blocked_set); return ret; } @@ -8858,7 +8865,7 @@ static int FAST_FUNC builtin_wait(char **argv) * $ */ sigaddset(&G.blocked_set, SIGCHLD); - sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); + sigprocmask_set(&G.blocked_set); while (1) { checkjobs(NULL); if (errno == ECHILD) @@ -8875,7 +8882,7 @@ static int FAST_FUNC builtin_wait(char **argv) } } sigdelset(&G.blocked_set, SIGCHLD); - sigprocmask(SIG_SETMASK, &G.blocked_set, NULL); + sigprocmask_set(&G.blocked_set); return ret; } -- cgit v1.2.3-55-g6feb From bcf1fa80f31468a2299b681dd096d41a0b5ed7f4 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 9 May 2011 01:05:33 +0200 Subject: hush: add tests for interrupting read Signed-off-by: Denys Vlasenko --- shell/hush_test/hush-trap/signal_read1.right | 1 + shell/hush_test/hush-trap/signal_read1.tests | 5 +++++ shell/hush_test/hush-trap/signal_read2.right | 2 ++ shell/hush_test/hush-trap/signal_read2.tests | 7 +++++++ shell/hush_test/run-all | 10 ++++++---- 5 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 shell/hush_test/hush-trap/signal_read1.right create mode 100755 shell/hush_test/hush-trap/signal_read1.tests create mode 100644 shell/hush_test/hush-trap/signal_read2.right create mode 100755 shell/hush_test/hush-trap/signal_read2.tests diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-trap/signal_read1.right new file mode 100644 index 000000000..2870a8e70 --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read1.right @@ -0,0 +1 @@ +Got HUP:0 diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-trap/signal_read1.tests new file mode 100755 index 000000000..1105479a3 --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read1.tests @@ -0,0 +1,5 @@ +(sleep 1; kill -HUP $$) & +trap 'echo "Got HUP:$?"; exit' HUP +while true; do + read ignored +done diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-trap/signal_read2.right new file mode 100644 index 000000000..71a6bc16d --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read2.right @@ -0,0 +1,2 @@ +HUP +Done:129 diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-trap/signal_read2.tests new file mode 100755 index 000000000..eab5b9b5b --- /dev/null +++ b/shell/hush_test/hush-trap/signal_read2.tests @@ -0,0 +1,7 @@ +$THIS_SH -c ' +(sleep 1; kill -HUP $$) & +while true; do + read ignored +done +' +echo "Done:$?" diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 256f152dc..64a7abc47 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all @@ -48,8 +48,9 @@ do_test() *.orig|*~) ;; #*) echo $x ; sh $x ;; *) + echo -n "$1/$x:" sh "$x" >"../$1-$x.fail" 2>&1 && \ - { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail"; + { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; } ;; esac done @@ -60,6 +61,7 @@ do_test() name="${x%%.tests}" test -f "$name.right" || continue # echo Running test: "$x" + echo -n "$1/$x:" ( "$THIS_SH" "./$x" >"$name.xx" 2>&1 # filter C library differences @@ -70,9 +72,9 @@ do_test() diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" ) case $? in - 0) echo "$1/$x: ok";; - 77) echo "$1/$x: skip (feature disabled)";; - *) echo "$1/$x: fail"; tret=1;; + 0) echo " ok";; + 77) echo " skip (feature disabled)";; + *) echo " fail"; tret=1;; esac done exit ${tret} -- cgit v1.2.3-55-g6feb From ed607a87e042bd0470c21244db814d599e2bce4f Mon Sep 17 00:00:00 2001 From: Steve Iribarne Date: Mon, 9 May 2011 01:42:12 +0200 Subject: pam link error with SHARED_BUSYBOX and LIBBUSYBOX enabled Signed-off-by: Steve Iribarne Signed-off-by: Denys Vlasenko --- scripts/trylink | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/trylink b/scripts/trylink index 5994a757b..a8b0b2e03 100755 --- a/scripts/trylink +++ b/scripts/trylink @@ -255,6 +255,7 @@ if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then $GC_SECTIONS \ $START_GROUP $O_FILES $END_GROUP \ -L"$sharedlib_dir" -lbusybox \ + $l_list \ $INFO_OPTS \ || { echo "Linking $EXE failed" -- cgit v1.2.3-55-g6feb From 12bc152b31420c3e3d441c87a995fe7b65dd23fe Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 9 May 2011 03:57:27 +0200 Subject: fbset: abort on unknown options. closes 3121 Signed-off-by: Denys Vlasenko --- util-linux/fbset.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/util-linux/fbset.c b/util-linux/fbset.c index 75d41b882..3be342481 100644 --- a/util-linux/fbset.c +++ b/util-linux/fbset.c @@ -402,7 +402,14 @@ int fbset_main(int argc, char **argv) argv++; argc--; for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) { - if (thisarg[0] == '-') for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) { + if (thisarg[0] != '-') { + if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1) + bb_show_usage(); + mode = thisarg; + options |= OPT_READMODE; + goto contin; + } + for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) { if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0) continue; if (argc <= g_cmdoptions[i].param_count) @@ -471,10 +478,7 @@ int fbset_main(int argc, char **argv) argv += g_cmdoptions[i].param_count; goto contin; } - if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1) - bb_show_usage(); - mode = *argv; - options |= OPT_READMODE; + bb_show_usage(); contin: ; } -- cgit v1.2.3-55-g6feb From 10c0131a8a1b3db7fd6b23b72ebd7b33afc7b018 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 11 May 2011 11:49:21 +0200 Subject: hush: use SA_RESTARTed signal handlers across read. Signed-off-by: Denys Vlasenko --- shell/hush.c | 37 +++++++++++++++++++++---------------- shell/shell_common.c | 34 +++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 71972f751..509bd415b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -792,8 +792,13 @@ struct globals { unsigned handled_SIGCHLD; smallint we_have_children; #endif - /* which signals have non-DFL handler (even with no traps set)? */ - unsigned non_DFL_mask; + /* Which signals have non-DFL handler (even with no traps set)? + * Set at the start to: + * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOB_SIGS) + * SPECIAL_INTERACTIVE_SIGS are cleared after fork. + * Other than these two times, never modified. + */ + unsigned special_sig_mask; char **traps; /* char *traps[NSIG] */ /* Signal mask on the entry to the (top-level) shell. Never modified. */ sigset_t inherited_set; @@ -1341,11 +1346,11 @@ static void restore_G_args(save_arg_t *sv, char **argv) * After each pipe execution, we extract any pending signals via sigtimedwait() * and act on them. * - * unsigned non_DFL_mask: a mask of such "special" signals + * unsigned special_sig_mask: a mask of such "special" signals * sigset_t blocked_set: current blocked signal set * * "trap - SIGxxx": - * clear bit in blocked_set unless it is also in non_DFL_mask + * clear bit in blocked_set unless it is also in special_sig_mask * "trap 'cmd' SIGxxx": * set bit in blocked_set (even if 'cmd' is '') * after [v]fork, if we plan to be a shell: @@ -5376,7 +5381,7 @@ static void reset_traps_to_defaults(void) * Testcase: (while :; do :; done) + ^Z should background. * Same goes for SIGTERM, SIGHUP, SIGINT. */ - if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) + if (!G.traps && !(G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS)) return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ /* Switching off SPECIAL_INTERACTIVE_SIGS. @@ -5394,10 +5399,10 @@ static void reset_traps_to_defaults(void) } } /* Our homegrown sig mask is saner to work with :) */ - G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; + G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; /* Resetting all traps to default except empty ones */ - mask = G.non_DFL_mask; + mask = G.special_sig_mask; if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { if (!G.traps[sig] || !G.traps[sig][0]) continue; @@ -7440,9 +7445,6 @@ static void init_sigmasks(void) /* POSIX allows shell to re-enable SIGCHLD * even if it was SIG_IGN on entry */ -#if ENABLE_HUSH_FAST - G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ -#endif if (!G.inherited_set_is_saved) { #if ENABLE_HUSH_FAST signal(SIGCHLD, SIGCHLD_handler); @@ -7460,7 +7462,7 @@ static void init_sigmasks(void) if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ mask |= SPECIAL_JOB_SIGS; } - G.non_DFL_mask = mask; + G.special_sig_mask = mask; /* Block them. And unblock SIGCHLD */ sig = 0; @@ -7504,10 +7506,10 @@ static void set_fatal_handlers_to_sigexit(void) /*+ (1 << SIGINT )*/ ; - /* non_DFL_mask'ed signals are, well, masked, + /* special_sig_mask'ed signals are, well, masked, * no need to set handler for them. */ - fatal_sigs &= ~G.non_DFL_mask; + fatal_sigs &= ~G.special_sig_mask; /* For each sig in fatal_sigs... */ sig = 0; @@ -7571,8 +7573,11 @@ int hush_main(int argc, char **argv) struct variable *shell_ver; INIT_G(); - if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ + if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */ G.last_exitcode = EXIT_SUCCESS; +#if ENABLE_HUSH_FAST + G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ +#endif #if !BB_MMU G.argv0_for_re_execing = argv[0]; #endif @@ -8303,7 +8308,7 @@ static int FAST_FUNC builtin_trap(char **argv) /* There was a trap handler, we are removing it * (if sig has non-DFL handling, * we don't need to do anything) */ - if (sig < 32 && (G.non_DFL_mask & (1 << sig))) + if (sig < sizeof(G.special_sig_mask)*8 && (G.special_sig_mask & (1 << sig))) continue; sigdelset(&G.blocked_set, sig); } @@ -8565,7 +8570,7 @@ static int FAST_FUNC builtin_read(char **argv) memset(&sa, 0, sizeof(sa)); sigfillset(&sa.sa_mask); - /*sa.sa_flags = 0;*/ + sa.sa_flags = SA_RESTART; sa.sa_handler = record_signal; sig = 0; diff --git a/shell/shell_common.c b/shell/shell_common.c index a5c455c8e..bbc22ed34 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -159,32 +159,40 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), bufpos = 0; do { char c; + struct pollfd pfd[1]; + int timeout; - errno = 0; + if ((bufpos & 0xff) == 0) + buffer = xrealloc(buffer, bufpos + 0x100); + timeout = -1; if (end_ms) { - int timeout; - struct pollfd pfd[1]; - - pfd[0].fd = fd; - pfd[0].events = POLLIN; timeout = end_ms - (unsigned)monotonic_ms(); - if (timeout <= 0 /* already late? */ - || poll(pfd, 1, timeout) != 1 /* no? wait... */ - ) { /* timed out! */ - err = errno; + if (timeout <= 0) { /* already late? */ retval = (const char *)(uintptr_t)1; goto ret; } } - if ((bufpos & 0xff) == 0) - buffer = xrealloc(buffer, bufpos + 0x100); - if (nonblock_immune_read(fd, &buffer[bufpos], 1, /*loop_on_EINTR:*/ 0) != 1) { + /* We must poll even if timeout is -1: + * we want to be interrupted if signal arrives, + * regardless of SA_RESTART-ness of that signal! + */ + errno = 0; + pfd[0].fd = fd; + pfd[0].events = POLLIN; + if (poll(pfd, 1, timeout) != 1) { + /* timed out, or EINTR */ + err = errno; + retval = (const char *)(uintptr_t)1; + goto ret; + } + if (read(fd, &buffer[bufpos], 1) != 1) { err = errno; retval = (const char *)(uintptr_t)1; break; } + c = buffer[bufpos]; if (c == '\0') continue; -- cgit v1.2.3-55-g6feb From 9d6cbafe728c9100a35e29ba7a3729c5303e73ea Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 11 May 2011 23:56:11 +0200 Subject: hush: replace signal handling machinery With new version of signal handling, read builtin should be less buggy wrt signals. function old new delta install_sighandlers - 121 +121 switch_off_special_sigs - 84 +84 pick_sighandler - 58 +58 install_special_sighandlers - 47 +47 builtin_wait 284 319 +35 record_pending_signo - 21 +21 execvp_or_die 43 48 +5 file_get 290 288 -2 run_list 1004 998 -6 static.zero_timespec 8 - -8 sigprocmask_set 14 - -14 sigwaitinfo 23 - -23 record_signal 23 - -23 __GI_sigwaitinfo 23 - -23 sigtimedwait 25 - -25 builtin_trap 417 392 -25 __GI_sigtimedwait 25 - -25 hush_main 1003 965 -38 check_and_run_traps 263 217 -46 __rt_sigtimedwait 52 - -52 reset_traps_to_defaults 213 126 -87 init_sigmasks 198 - -198 builtin_read 536 197 -339 ------------------------------------------------------------------------------ (add/remove: 5/10 grow/shrink: 2/7 up/down: 371/-934) Total: -563 bytes text data bss dec hex filename 903075 936 17736 921747 e1093 busybox_old 902547 936 17736 921219 e0e83 busybox_unstripped Signed-off-by: Denys Vlasenko --- shell/hush.c | 510 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 278 insertions(+), 232 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 509bd415b..b2c3a752e 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -106,6 +106,10 @@ # define PIPE_BUF 4096 /* amount of buffering in a pipe */ #endif +/* Not every libc has sighandler_t. Fix it */ +typedef void (*hush_sighandler_t)(int); +#define sighandler_t hush_sighandler_t + //config:config HUSH //config: bool "hush" //config: default y @@ -764,7 +768,6 @@ struct globals { smalluint last_exitcode; /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ smalluint global_args_malloced; - smalluint inherited_set_is_saved; /* how many non-NULL argv's we have. NB: $# + 1 */ int global_argc; char **global_argv; @@ -794,21 +797,20 @@ struct globals { #endif /* Which signals have non-DFL handler (even with no traps set)? * Set at the start to: - * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOB_SIGS) + * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) * SPECIAL_INTERACTIVE_SIGS are cleared after fork. + * The rest is cleared right before execv syscalls. * Other than these two times, never modified. */ unsigned special_sig_mask; +#if ENABLE_HUSH_JOB + unsigned fatal_sig_mask; +#define G_fatal_sig_mask G.fatal_sig_mask +#else +#define G_fatal_sig_mask 0 +#endif char **traps; /* char *traps[NSIG] */ - /* Signal mask on the entry to the (top-level) shell. Never modified. */ - sigset_t inherited_set; - /* Starts equal to inherited_set, - * but shell-special signals are added and SIGCHLD is removed. - * When a trap is set/cleared, signal is added to/removed from it: - */ - sigset_t blocked_set; - /* Used by read() */ - sigset_t detected_set; + sigset_t pending_set; #if HUSH_DEBUG unsigned long memleak_value; int debug_indent; @@ -1337,8 +1339,8 @@ static void restore_G_args(save_arg_t *sv, char **argv) * (What happens to signals which are IGN on shell start?) * (What happens with signal mask on shell start?) * - * Implementation in hush - * ====================== + * Old implementation + * ================== * We use in-kernel pending signal mask to determine which signals were sent. * We block all signals which we don't want to take action immediately, * i.e. we block all signals which need to have special handling as described @@ -1369,6 +1371,49 @@ static void restore_G_args(save_arg_t *sv, char **argv) * Standard says "When a subshell is entered, traps that are not being ignored * are set to the default actions". bash interprets it so that traps which * are set to '' (ignore) are NOT reset to defaults. We do the same. + * + * Problem: the above approach makes it unwieldy to catch signals while + * we are in read builtin, of while we read commands from stdin: + * masked signals are not visible! + * + * New implementation + * ================== + * We record each signal we are interested in by installing signal handler + * for them - a bit like emulating kernel pending signal mask in userspace. + * We are interested in: signals which need to have special handling + * as described above, and all signals which have traps set. + * Signals are rocorded in pending_set. + * After each pipe execution, we extract any pending signals + * and act on them. + * + * unsigned special_sig_mask: a mask of shell-special signals. + * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp. + * char *traps[sig] if trap for sig is set (even if it's ''). + * sigset_t pending_set: set of sigs we received. + * + * "trap - SIGxxx": + * if sig is in special_sig_mask, set handler back to: + * record_pending_signo, or to IGN if it's a tty stop signal + * if sig is in fatal_sig_mask, set handler back to sigexit. + * else: set handler back to SIG_DFL + * "trap 'cmd' SIGxxx": + * set handler to record_pending_signo. + * "trap '' SIGxxx": + * set handler to SIG_IGN. + * after [v]fork, if we plan to be a shell: + * set signals with special interactive handling to SIG_DFL + * (because child shell is not interactive), + * unset all traps except '' (note: regardless of child shell's type - {}, (), etc) + * after [v]fork, if we plan to exec: + * POSIX says fork clears pending signal mask in child - no need to clear it. + * + * To make wait builtin interruptible, we handle SIGCHLD as special signal, + * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it. + * + * Note (compat): + * Standard says "When a subshell is entered, traps that are not being ignored + * are set to the default actions". bash interprets it so that traps which + * are set to '' (ignore) are NOT reset to defaults. We do the same. */ enum { SPECIAL_INTERACTIVE_SIGS = 0 @@ -1376,26 +1421,25 @@ enum { | (1 << SIGINT) | (1 << SIGHUP) , - SPECIAL_JOB_SIGS = 0 + SPECIAL_JOBSTOP_SIGS = 0 #if ENABLE_HUSH_JOB | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) #endif + , }; -static void sigprocmask_set(sigset_t *set) +static void record_pending_signo(int sig) { - sigprocmask(SIG_SETMASK, set, NULL); -} - + sigaddset(&G.pending_set, sig); #if ENABLE_HUSH_FAST -static void SIGCHLD_handler(int sig UNUSED_PARAM) -{ - G.count_SIGCHLD++; + if (sig == SIGCHLD) { + G.count_SIGCHLD++; //bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); -} + } #endif +} #if ENABLE_HUSH_JOB @@ -1433,6 +1477,31 @@ static void sigexit(int sig) #endif +static sighandler_t pick_sighandler(unsigned sig) +{ + sighandler_t handler = SIG_DFL; + if (sig < sizeof(unsigned)*8) { + unsigned sigmask = (1 << sig); + +#if ENABLE_HUSH_JOB + /* sig is fatal? */ + if (G_fatal_sig_mask & sigmask) + handler = sigexit; +#endif + /* sig has special handling? */ + else if (G.special_sig_mask & sigmask) + handler = record_pending_signo; + /* TTIN/TTOU/TSTS can't be set to record_pending_signo + * in order to ignore them: they will be raised + * in an endless loop then when we try to do some + * terminal ioctls! We do nave to _ignore_ these. + */ + if (SPECIAL_JOBSTOP_SIGS & sigmask) + handler = SIG_IGN; + } + return handler; +} + /* Restores tty foreground process group, and exits. */ static void hush_exit(int exitcode) NORETURN; static void hush_exit(int exitcode) @@ -1478,39 +1547,30 @@ static void hush_exit(int exitcode) } -static int check_and_run_traps(int sig) +//TODO: return a mask of ALL handled sigs? +static int check_and_run_traps(void) { - /* I want it in rodata, not in bss. - * gcc 4.2.1 puts it in rodata only if it has { 0, 0 } - * initializer. But other compilers may still use bss. - * TODO: find more portable solution. - */ - static const struct timespec zero_timespec = { 0, 0 }; - smalluint save_rcode; int last_sig = 0; - if (sig) - goto got_sig; - while (1) { - if (!sigisemptyset(&G.detected_set)) { - sig = 0; - do { - sig++; - if (sigismember(&G.detected_set, sig)) { - sigdelset(&G.detected_set, sig); - goto got_sig; - } - } while (sig < NSIG); - } + int sig; - sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec); - if (sig <= 0) + if (sigisemptyset(&G.pending_set)) break; + sig = 0; + do { + sig++; + if (sigismember(&G.pending_set, sig)) { + sigdelset(&G.pending_set, sig); + goto got_sig; + } + } while (sig < NSIG); + break; got_sig: if (G.traps && G.traps[sig]) { if (G.traps[sig][0]) { /* We have user-defined handler */ + smalluint save_rcode; char *argv[3]; /* argv[0] is unused */ argv[1] = G.traps[sig]; @@ -1524,12 +1584,6 @@ static int check_and_run_traps(int sig) } /* not a trap: special action */ switch (sig) { -#if ENABLE_HUSH_FAST - case SIGCHLD: - G.count_SIGCHLD++; -//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); - break; -#endif case SIGINT: /* Builtin was ^C'ed, make it look prettier: */ bb_putchar('\n'); @@ -1550,12 +1604,22 @@ static int check_and_run_traps(int sig) } sigexit(SIGHUP); } +#endif +#if ENABLE_HUSH_FAST + case SIGCHLD: + G.count_SIGCHLD++; +//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); + /* Note: + * We dont do 'last_sig = sig' here -> NOT returning this sig. + * This simplifies wait builtin a bit. + */ + break; #endif default: /* ignored: */ /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */ - /* note: - * we dont do 'last_sig = sig' here -> NOT returning this sig. - * example: wait is not interrupted by TERM + /* Note: + * We dont do 'last_sig = sig' here -> NOT returning this sig. + * Example: wait is not interrupted by TERM * in interactive shell, because TERM is ignored. */ break; @@ -1948,7 +2012,7 @@ static void get_user_input(struct in_str *i) * only after . (^C will work) */ r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1); /* catch *SIGINT* etc (^C is handled by read_line_input) */ - check_and_run_traps(0); + check_and_run_traps(); } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ i->eof_flag = (r < 0); if (i->eof_flag) { /* EOF/error detected */ @@ -1964,7 +2028,7 @@ static void get_user_input(struct in_str *i) * $ <[enter], repeatedly...> * Without check_and_run_traps, handler never runs. */ - check_and_run_traps(0); + check_and_run_traps(); fputs(prompt_str, stdout); } fflush_all(); @@ -5368,6 +5432,25 @@ void re_execute_shell(char ***to_free, const char *s, char *g_argv0, char **g_argv, char **builtin_argv) NORETURN; +static void switch_off_special_sigs(unsigned mask) +{ + unsigned sig = 0; + while ((mask >>= 1) != 0) { + sig++; + if (!(mask & 1)) + continue; + if (G.traps) { + if (G.traps[sig] && !G.traps[sig][0]) + /* trap is '', has to remain SIG_IGN */ + continue; + free(G.traps[sig]); + G.traps[sig] = NULL; + } + /* We are here only if no trap or trap was not '' */ + signal(sig, SIG_DFL); + } +} + static void reset_traps_to_defaults(void) { /* This function is always called in a child shell @@ -5381,44 +5464,35 @@ static void reset_traps_to_defaults(void) * Testcase: (while :; do :; done) + ^Z should background. * Same goes for SIGTERM, SIGHUP, SIGINT. */ - if (!G.traps && !(G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS)) - return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ - - /* Switching off SPECIAL_INTERACTIVE_SIGS. - * Stupid. It can be done with *single* &= op, but we can't use - * the fact that G.blocked_set is implemented as a bitmask - * in libc... */ - mask = SPECIAL_INTERACTIVE_SIGS; - sig = 0; - while ((mask >>= 1) != 0) { - sig++; - if (mask & 1) { - /* Careful. Only if no trap or trap is not "" */ - if (!G.traps || !G.traps[sig] || G.traps[sig][0]) - sigdelset(&G.blocked_set, sig); - } - } - /* Our homegrown sig mask is saner to work with :) */ + mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; + if (!G.traps && !mask) + return; /* already no traps and no special sigs */ + + /* Switch off special sigs */ + switch_off_special_sigs(mask); +#if ENABLE_HUSH_JOB + G_fatal_sig_mask = 0; +#endif G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; + /* SIGQUIT and maybe SPECIAL_JOBSTOP_SIGS remain set in G.special_sig_mask */ - /* Resetting all traps to default except empty ones */ - mask = G.special_sig_mask; - if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { - if (!G.traps[sig] || !G.traps[sig][0]) - continue; + if (!G.traps) + return; + + /* Reset all sigs to default except ones with empty traps */ + for (sig = 0; sig < NSIG; sig++) { + if (!G.traps[sig]) + continue; /* no trap: nothing to do */ + if (!G.traps[sig][0]) + continue; /* empty trap: has to remain SIG_IGN */ + /* sig has non-empty trap, reset it: */ free(G.traps[sig]); G.traps[sig] = NULL; - /* There is no signal for 0 (EXIT) */ + /* There is no signal for trap 0 (EXIT) */ if (sig == 0) continue; - /* There was a trap handler, we just removed it. - * But if sig still has non-DFL handling, - * we should not unblock the sig. */ - if (mask & 1) - continue; - sigdelset(&G.blocked_set, sig); + signal(sig, pick_sighandler(sig)); } - sigprocmask_set(&G.blocked_set); } #else /* !BB_MMU */ @@ -5463,6 +5537,7 @@ static void re_execute_shell(char ***to_free, const char *s, for (sig = 1; sig < NSIG; sig++) { if (G.traps[sig] && !G.traps[sig][0]) empty_trap_mask |= 1LL << sig; +///vda: optimize } } @@ -5548,7 +5623,7 @@ static void re_execute_shell(char ***to_free, const char *s, do_exec: debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s); - sigprocmask_set(&G.inherited_set); + switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); execve(bb_busybox_exec_path, argv, pp); /* Fallback. Useful for init=/bin/hush usage etc */ if (argv[0][0] == '/') @@ -6202,7 +6277,7 @@ static void execvp_or_die(char **argv) NORETURN; static void execvp_or_die(char **argv) { debug_printf_exec("execing '%s'\n", argv[0]); - sigprocmask_set(&G.inherited_set); + switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); execvp(argv[0], argv); bb_perror_msg("can't execute '%s'", argv[0]); _exit(127); /* bash compat */ @@ -6334,7 +6409,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, # endif /* Re-exec ourselves */ debug_printf_exec("re-execing applet '%s'\n", argv[0]); - sigprocmask_set(&G.inherited_set); + switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS); execv(bb_busybox_exec_path, argv); /* If they called chroot or otherwise made the binary no longer * executable, fall through */ @@ -7033,9 +7108,6 @@ static NOINLINE int run_pipe(struct pipe *pi) if (setup_redirects(command, NULL)) _exit(1); - /* Restore default handlers just prior to exec */ - /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */ - /* Stores to nommu_save list of env vars putenv'ed * (NOMMU, on MMU we don't need that) */ /* cast away volatility... */ @@ -7313,7 +7385,7 @@ static int run_list(struct pipe *pi) * and we don't need to wait for anything. */ G.last_exitcode = rcode; debug_printf_exec(": builtin/func exitcode %d\n", rcode); - check_and_run_traps(0); + check_and_run_traps(); #if ENABLE_HUSH_LOOPS /* Was it "break" or "continue"? */ if (G.flag_break_continue) { @@ -7345,7 +7417,7 @@ static int run_list(struct pipe *pi) /* even bash 3.2 doesn't do that well with nested bg: * try "{ { sleep 10; echo DEEP; } & echo HERE; } &". * I'm NOT treating inner &'s as jobs */ - check_and_run_traps(0); + check_and_run_traps(); #if ENABLE_HUSH_JOB if (G.run_list_level == 1) insert_bg_job(pi); @@ -7360,13 +7432,13 @@ static int run_list(struct pipe *pi) /* Waits for completion, then fg's main shell */ rcode = checkjobs_and_fg_shell(pi); debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode); - check_and_run_traps(0); + check_and_run_traps(); } else #endif { /* This one just waits for completion */ rcode = checkjobs(pi); debug_printf_exec(": checkjobs exitcode %d\n", rcode); - check_and_run_traps(0); + check_and_run_traps(); } G.last_exitcode = rcode; } @@ -7437,58 +7509,61 @@ static int run_and_free_list(struct pipe *pi) } +static void install_sighandlers(unsigned mask) +{ + sighandler_t old_handler; + unsigned sig = 0; + while ((mask >>= 1) != 0) { + sig++; + if (!(mask & 1)) + continue; + old_handler = signal(sig, pick_sighandler(sig)); + /* POSIX allows shell to re-enable SIGCHLD + * even if it was SIG_IGN on entry. + * Therefore we skip IGN check for it: + */ + if (sig == SIGCHLD) + continue; + if (old_handler == SIG_IGN) { + /* oops... restore back to IGN, and record this fact */ + signal(sig, old_handler); + if (!G.traps) + G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); + free(G.traps[sig]); + G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ + } + } +} + /* Called a few times only (or even once if "sh -c") */ -static void init_sigmasks(void) +static void install_special_sighandlers(void) { - unsigned sig; unsigned mask; - /* POSIX allows shell to re-enable SIGCHLD - * even if it was SIG_IGN on entry */ - if (!G.inherited_set_is_saved) { -#if ENABLE_HUSH_FAST - signal(SIGCHLD, SIGCHLD_handler); -#else - signal(SIGCHLD, SIG_DFL); -#endif - sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); - G.inherited_set = G.blocked_set; - } + if (G.special_sig_mask != 0) + return; /* Which signals are shell-special? */ - mask = (1 << SIGQUIT); + mask = (1 << SIGQUIT) | (1 << SIGCHLD); if (G_interactive_fd) { mask |= SPECIAL_INTERACTIVE_SIGS; if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ - mask |= SPECIAL_JOB_SIGS; + mask |= SPECIAL_JOBSTOP_SIGS; } G.special_sig_mask = mask; - /* Block them. And unblock SIGCHLD */ - sig = 0; - while ((mask >>= 1) != 0) { - sig++; - if (mask & 1) - sigaddset(&G.blocked_set, sig); - } - sigdelset(&G.blocked_set, SIGCHLD); - - if (memcmp(&G.inherited_set, &G.blocked_set, sizeof(G.inherited_set)) != 0) - sigprocmask_set(&G.blocked_set); - - G.inherited_set_is_saved = 1; + install_sighandlers(mask); } #if ENABLE_HUSH_JOB /* helper */ /* Set handlers to restore tty pgrp and exit */ -static void set_fatal_handlers_to_sigexit(void) +static void install_fatal_sighandlers(void) { - void (*handler)(int); - unsigned fatal_sigs, sig; + unsigned mask; /* We will restore tty pgrp on these signals */ - fatal_sigs = 0 + mask = 0 + (1 << SIGILL ) * HUSH_DEBUG + (1 << SIGFPE ) * HUSH_DEBUG + (1 << SIGBUS ) * HUSH_DEBUG @@ -7505,22 +7580,13 @@ static void set_fatal_handlers_to_sigexit(void) /*+ (1 << SIGTERM)*/ /*+ (1 << SIGINT )*/ ; - - /* special_sig_mask'ed signals are, well, masked, + /* special_sig_mask'ed signals are set to record_pending_signo * no need to set handler for them. */ - fatal_sigs &= ~G.special_sig_mask; + /*mask &= ~G.special_sig_mask; - they never overlap */ + G_fatal_sig_mask = mask; - /* For each sig in fatal_sigs... */ - sig = 0; - while ((fatal_sigs >>= 1) != 0) { - sig++; - if (!(fatal_sigs & 1)) - continue; - handler = signal(sig, sigexit); - if (handler == SIG_IGN) /* oops... restore back to IGN! */ - signal(sig, handler); - } + install_sighandlers(mask); } #endif @@ -7682,10 +7748,11 @@ int hush_main(int argc, char **argv) } /* Shell is non-interactive at first. We need to call - * init_sigmasks() if we are going to execute "sh