From a97a2f12804668435e05cf98ae43e3a81e3da041 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 25 Feb 2024 01:06:30 +0100 Subject: ls: do not truncate username/groupname to 8 chars function old new delta .rodata 105412 105408 -4 Signed-off-by: Denys Vlasenko --- coreutils/ls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coreutils/ls.c b/coreutils/ls.c index b69b80460..cc809b797 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -526,10 +526,10 @@ static NOINLINE unsigned display_single(const struct dnode *dn) #if ENABLE_FEATURE_LS_USERNAME else { if (opt & OPT_g) { - column += printf("%-8.8s ", + column += printf("%-8s ", get_cached_groupname(dn->dn_gid)); } else { - column += printf("%-8.8s %-8.8s ", + column += printf("%-8s %-8s ", get_cached_username(dn->dn_uid), get_cached_groupname(dn->dn_gid)); } -- cgit v1.2.3-55-g6feb From 2639f3bc72ac2f03af7ccc825429ccb2fce99a16 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 10 Feb 2024 18:51:39 +0300 Subject: hush: set G.ifs sooner (prevents segfault) function old new delta set_G_ifs - 151 +151 run_list 1024 1031 +7 run_pipe 1567 1445 -122 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 1/1 up/down: 158/-122) Total: 36 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 62 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index ca01e2b5b..0c91008b9 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -9250,6 +9250,37 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe) * backgrounded: cmd & { list } & * subshell: ( list ) [&] */ +static void set_G_ifs(void) +{ + /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*" + * Result should be 3 lines: q w e, qwe, q w e + */ + if (G.ifs_whitespace != G.ifs) + free(G.ifs_whitespace); + G.ifs = get_local_var_value("IFS"); + if (G.ifs) { + char *p; + G.ifs_whitespace = (char*)G.ifs; + p = skip_whitespace(G.ifs); + if (*p) { + /* Not all $IFS is whitespace */ + char *d; + int len = p - G.ifs; + p = skip_non_whitespace(p); + G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */ + d = mempcpy(G.ifs_whitespace, G.ifs, len); + while (*p) { + if (isspace(*p)) + *d++ = *p; + p++; + } + *d = '\0'; + } + } else { + G.ifs = defifs; + G.ifs_whitespace = (char*)G.ifs; + } +} #if !ENABLE_HUSH_MODE_X #define redirect_and_varexp_helper(command, sqp, argv_expanded) \ redirect_and_varexp_helper(command, sqp) @@ -9286,34 +9317,7 @@ static NOINLINE int run_pipe(struct pipe *pi) debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); debug_enter(); - /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*" - * Result should be 3 lines: q w e, qwe, q w e - */ - if (G.ifs_whitespace != G.ifs) - free(G.ifs_whitespace); - G.ifs = get_local_var_value("IFS"); - if (G.ifs) { - char *p; - G.ifs_whitespace = (char*)G.ifs; - p = skip_whitespace(G.ifs); - if (*p) { - /* Not all $IFS is whitespace */ - char *d; - int len = p - G.ifs; - p = skip_non_whitespace(p); - G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */ - d = mempcpy(G.ifs_whitespace, G.ifs, len); - while (*p) { - if (isspace(*p)) - *d++ = *p; - p++; - } - *d = '\0'; - } - } else { - G.ifs = defifs; - G.ifs_whitespace = (char*)G.ifs; - } + set_G_ifs(); IF_HUSH_JOB(pi->pgrp = -1;) pi->stopped_cmds = 0; @@ -9758,6 +9762,8 @@ static int run_list(struct pipe *pi) debug_printf_exec("run_list lvl %d start\n", G.run_list_level); debug_enter(); + set_G_ifs(); + #if ENABLE_HUSH_LOOPS /* Check syntax for "for" */ { -- cgit v1.2.3-55-g6feb From 758b21402abc7015cfc54eb21a2e7eead1ecf6ba Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 25 Feb 2024 17:50:43 +0100 Subject: hush: detect when terminating "done"/"fi" is missing function old new delta parse_stream 2271 2292 +21 .rodata 105408 105427 +19 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 40/0) Total: 40 bytes Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-parsing/nodone1.right | 1 + shell/ash_test/ash-parsing/nodone1.tests | 1 + shell/ash_test/ash-parsing/nodone2.right | 1 + shell/ash_test/ash-parsing/nodone2.tests | 3 +++ shell/hush.c | 11 ++++++++++- shell/hush_test/hush-parsing/nodone1.right | 1 + shell/hush_test/hush-parsing/nodone1.tests | 1 + shell/hush_test/hush-parsing/nodone2.right | 1 + shell/hush_test/hush-parsing/nodone2.tests | 3 +++ 9 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 shell/ash_test/ash-parsing/nodone1.right create mode 100755 shell/ash_test/ash-parsing/nodone1.tests create mode 100644 shell/ash_test/ash-parsing/nodone2.right create mode 100755 shell/ash_test/ash-parsing/nodone2.tests create mode 100644 shell/hush_test/hush-parsing/nodone1.right create mode 100755 shell/hush_test/hush-parsing/nodone1.tests create mode 100644 shell/hush_test/hush-parsing/nodone2.right create mode 100755 shell/hush_test/hush-parsing/nodone2.tests diff --git a/shell/ash_test/ash-parsing/nodone1.right b/shell/ash_test/ash-parsing/nodone1.right new file mode 100644 index 000000000..0150ccad5 --- /dev/null +++ b/shell/ash_test/ash-parsing/nodone1.right @@ -0,0 +1 @@ +./nodone1.tests: line 2: syntax error: unexpected end of file (expecting "done") diff --git a/shell/ash_test/ash-parsing/nodone1.tests b/shell/ash_test/ash-parsing/nodone1.tests new file mode 100755 index 000000000..de286c5a2 --- /dev/null +++ b/shell/ash_test/ash-parsing/nodone1.tests @@ -0,0 +1 @@ +for i; do : diff --git a/shell/ash_test/ash-parsing/nodone2.right b/shell/ash_test/ash-parsing/nodone2.right new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/shell/ash_test/ash-parsing/nodone2.right @@ -0,0 +1 @@ +1 diff --git a/shell/ash_test/ash-parsing/nodone2.tests b/shell/ash_test/ash-parsing/nodone2.tests new file mode 100755 index 000000000..69537b3b1 --- /dev/null +++ b/shell/ash_test/ash-parsing/nodone2.tests @@ -0,0 +1,3 @@ +for i in 1; do echo $i +# the next line has no EOL. It still must count as "done" keyword: +done \ No newline at end of file diff --git a/shell/hush.c b/shell/hush.c index 0c91008b9..3e6a13b32 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -5497,6 +5497,15 @@ static struct pipe *parse_stream(char **pstring, } o_free_and_set_NULL(&ctx.word); done_pipe(&ctx, PIPE_SEQ); + + /* Do we sit inside of any if's, loops or case's? */ + if (HAS_KEYWORDS + IF_HAS_KEYWORDS(&& (ctx.ctx_res_w != RES_NONE || ctx.old_flag != 0)) + ) { + syntax_error_unterm_str("compound statement"); + goto parse_error_exitcode1; + } + pi = ctx.list_head; /* If we got nothing... */ /* (this makes bare "&" cmd a no-op. @@ -5519,7 +5528,7 @@ static struct pipe *parse_stream(char **pstring, // *heredoc_cnt_ptr = heredoc_cnt; debug_leave(); debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); - debug_printf_parse("parse_stream return %p\n", pi); + debug_printf_parse("parse_stream return %p: EOF\n", pi); return pi; } diff --git a/shell/hush_test/hush-parsing/nodone1.right b/shell/hush_test/hush-parsing/nodone1.right new file mode 100644 index 000000000..3dc1bcfbe --- /dev/null +++ b/shell/hush_test/hush-parsing/nodone1.right @@ -0,0 +1 @@ +hush: syntax error: unterminated compound statement diff --git a/shell/hush_test/hush-parsing/nodone1.tests b/shell/hush_test/hush-parsing/nodone1.tests new file mode 100755 index 000000000..de286c5a2 --- /dev/null +++ b/shell/hush_test/hush-parsing/nodone1.tests @@ -0,0 +1 @@ +for i; do : diff --git a/shell/hush_test/hush-parsing/nodone2.right b/shell/hush_test/hush-parsing/nodone2.right new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/shell/hush_test/hush-parsing/nodone2.right @@ -0,0 +1 @@ +1 diff --git a/shell/hush_test/hush-parsing/nodone2.tests b/shell/hush_test/hush-parsing/nodone2.tests new file mode 100755 index 000000000..69537b3b1 --- /dev/null +++ b/shell/hush_test/hush-parsing/nodone2.tests @@ -0,0 +1,3 @@ +for i in 1; do echo $i +# the next line has no EOL. It still must count as "done" keyword: +done \ No newline at end of file -- cgit v1.2.3-55-g6feb From f873c63085e3f3745040286999779077ccc03987 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Sun, 25 Feb 2024 10:13:33 +0100 Subject: ip link: support for the CAN netlink I developed this application to test the Linux kernel series [1]. As described in it I could not use the iproute2 package since the microcontroller is without MMU. function old new delta do_set_can - 920 +920 packed_usage 34645 34908 +263 get_float_1000 - 164 +164 .rodata 105427 105539 +112 do_iplink 1313 1381 +68 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 3/0 up/down: 1527/0) Total: 1527 bytes cc: Marc Kleine-Budde [1] https://marc.info/?l=linux-netdev&m=167999323611710&w=2 Signed-off-by: Dario Binacchi Signed-off-by: Denys Vlasenko --- configs/TEST_nommu_defconfig | 1 + networking/ip.c | 55 +++++++- networking/libiproute/iplink.c | 288 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 333 insertions(+), 11 deletions(-) diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig index 415f5a802..fa3e96326 100644 --- a/configs/TEST_nommu_defconfig +++ b/configs/TEST_nommu_defconfig @@ -703,6 +703,7 @@ CONFIG_FEATURE_INETD_RPC=y CONFIG_IP=y CONFIG_FEATURE_IP_ADDRESS=y CONFIG_FEATURE_IP_LINK=y +CONFIG_FEATURE_IP_LINK_CAN=y CONFIG_FEATURE_IP_ROUTE=y CONFIG_FEATURE_IP_TUNNEL=y CONFIG_FEATURE_IP_RULE=y diff --git a/networking/ip.c b/networking/ip.c index 36126b747..02f93adbf 100644 --- a/networking/ip.c +++ b/networking/ip.c @@ -74,6 +74,12 @@ //config: help //config: Configure network devices with "ip". //config: +//config:config FEATURE_IP_LINK_CAN +//config: bool "ip link set type can" +//config: default y +//config: help +//config: Configure CAN devices with "ip". +//config: //config:config FEATURE_IP_ROUTE //config: bool "ip route" //config: default y @@ -152,16 +158,61 @@ //usage:#define iplink_trivial_usage //usage: /*Usage:iplink*/"set IFACE [up|down] [arp on|off] [multicast on|off]\n" //usage: " [promisc on|off] [mtu NUM] [name NAME] [qlen NUM] [address MAC]\n" -//usage: " [master IFACE | nomaster] [netns PID]" +//usage: " [master IFACE | nomaster] [netns PID]"IF_FEATURE_IP_LINK_CAN(" [type TYPE ARGS]") // * short help shows only "set" command, long help continues (with just one "\n") // * and shows all other commands: //usage:#define iplink_full_usage "\n" //usage: "iplink add [link IFACE] IFACE [address MAC] type TYPE [ARGS]\n" //usage: "iplink delete IFACE type TYPE [ARGS]\n" -//usage: " TYPE ARGS := vlan VLANARGS | vrf table NUM\n" +//usage: " TYPE ARGS := vlan VLANARGS | vrf table NUM"IF_FEATURE_IP_LINK_CAN(" | can CANARGS")"\n" //usage: " VLANARGS := id VLANID [protocol 802.1q|802.1ad] [reorder_hdr on|off]\n" //usage: " [gvrp on|off] [mvrp on|off] [loose_binding on|off]\n" +//usage: IF_FEATURE_IP_LINK_CAN( +//usage: " CANARGS := [bitrate BITRATE [sample-point SAMPLE-POINT]] |\n" +//usage: " [tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1\n" +//usage: " phase-seg2 PHASE-SEG2 [sjw SJW]]\n" +//usage: " [dbitrate BITRATE [dsample-point SAMPLE-POINT]] |\n" +//usage: " [dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n" +//usage: " dphase-seg2 PHASE-SEG2 [dsjw SJW]]\n" +//usage: " [loopback on|off] [listen-only on|off] [triple-sampling on|off]\n" +//usage: " [one-shot on|off] [berr-reporting on|off]\n" +//usage: " [fd on|off] [fd-non-iso on|off] [presume-ack on|off]\n" +//usage: " [restart-ms TIME-MS] [restart]\n" +//usage: " [termination 0..65535]\n" +//usage: ) //usage: "iplink show [IFACE]" +//upstream man ip-link-can: +//Usage: ip link set DEVICE type can +// [ bitrate BITRATE [ sample-point SAMPLE-POINT] ] | +// [ tq TQ prop-seg PROP_SEG phase-seg1 PHASE-SEG1 +// phase-seg2 PHASE-SEG2 [ sjw SJW ] ] +// +// [ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] | +// [ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1 +// dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ] +// +// [ loopback { on | off } ] +// [ listen-only { on | off } ] +// [ triple-sampling { on | off } ] +// [ one-shot { on | off } ] +// [ berr-reporting { on | off } ] +// [ fd { on | off } ] +// [ fd-non-iso { on | off } ] +// [ presume-ack { on | off } ] +// +// [ restart-ms TIME-MS ] +// [ restart ] +// +// [ termination { 0..65535 } ] +// +// Where: BITRATE := { 1..1000000 } +// SAMPLE-POINT := { 0.000..0.999 } +// TQ := { NUMBER } +// PROP-SEG := { 1..8 } +// PHASE-SEG1 := { 1..8 } +// PHASE-SEG2 := { 1..8 } +// SJW := { 1..4 } +// RESTART-MS := { 0 | NUMBER } //upstream man ip-link: //===================== //ip link add [link DEV] [ name ] NAME diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c index 9eb0b4f5f..37ed114bc 100644 --- a/networking/libiproute/iplink.c +++ b/networking/libiproute/iplink.c @@ -11,6 +11,9 @@ #include #include +#if ENABLE_FEATURE_IP_LINK_CAN +# include +#endif #include "ip_common.h" /* #include "libbb.h" is inside */ #include "rt_names.h" #include "utils.h" @@ -28,6 +31,11 @@ #undef IFLA_VLAN_PROTOCOL #define IFLA_VLAN_PROTOCOL 5 +#ifndef NLMSG_TAIL +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) +#endif + #ifndef IFLA_LINKINFO # define IFLA_LINKINFO 18 # define IFLA_INFO_KIND 1 @@ -55,6 +63,11 @@ struct ifla_vlan_flags { #define str_on_off "on\0""off\0" +enum { + PARM_on = 0, + PARM_off +}; + /* Exits on error */ static int get_ctl_fd(void) { @@ -241,10 +254,257 @@ static void die_must_be_on_off(const char *msg) bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg); } +#if ENABLE_FEATURE_IP_LINK_CAN +static uint32_t get_float_1000(char *arg, const char *errmsg) +{ + uint32_t ret; + double d; + char *ptr; + + errno = 0; +//TODO: needs setlocale(LC_NUMERIC, "C")? + d = strtod(arg, &ptr); + if (errno || ptr == arg || *ptr + || d > (0xFFFFFFFFU / 1000) || d < 0 + ) { + invarg_1_to_2(arg, errmsg); /* does not return */ + } + ret = d * 1000; + + return ret; +} + +static void do_set_can(char *dev, char **argv) +{ + struct can_bittiming bt = {}, dbt = {}; + struct can_ctrlmode cm = {}; + char *keyword; + static const char keywords[] ALIGN1 = + "bitrate\0""sample-point\0""tq\0" + "prop-seg\0""phase-seg1\0""phase-seg2\0""sjw\0" + "dbitrate\0""dsample-point\0""dtq\0" + "dprop-seg\0""dphase-seg1\0""dphase-seg2\0""dsjw\0" + "loopback\0""listen-only\0""triple-sampling\0" + "one-shot\0""berr-reporting\0" + "fd\0""fd-non-iso\0""presume-ack\0" + "cc-len8-dlc\0""restart\0""restart-ms\0" + "termination\0"; + enum { ARG_bitrate = 0, ARG_sample_point, ARG_tq, + ARG_prop_seg, ARG_phase_seg1, ARG_phase_seg2, ARG_sjw, + ARG_dbitrate, ARG_dsample_point, ARG_dtq, + ARG_dprop_seg, ARG_dphase_seg1, ARG_dphase_seg2, ARG_dsjw, + ARG_loopback, ARG_listen_only, ARG_triple_sampling, + ARG_one_shot, ARG_berr_reporting, + ARG_fd, ARG_fd_non_iso, ARG_presume_ack, + ARG_cc_len8_dlc, ARG_restart, ARG_restart_ms, + ARG_termination }; + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req; + size_t dev_len; + struct rtattr *linkinfo, *data; + smalluint key, param; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWLINK; + req.i.ifi_family = preferred_family; + xrtnl_open(&rth); + req.i.ifi_index = xll_name_to_index(dev); + dev_len = strlen(dev); + if (dev_len < 2 || dev_len > IFNAMSIZ) + invarg_1_to_2(dev, "dev"); + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, dev, dev_len); + linkinfo = NLMSG_TAIL(&req.n); + addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); + addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, (void *)"can", + strlen("can")); + data = NLMSG_TAIL(&req.n); + addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); + while (*argv) { + key = index_in_substrings(keywords, *argv); + keyword = *argv; + //printf("%s: key: %d, *argv: %s\n", __func__, key, *argv); + switch (key) { + case ARG_bitrate: + case ARG_tq: + case ARG_prop_seg: + case ARG_phase_seg1: + case ARG_phase_seg2: + case ARG_sjw: { + uint32_t *val; + NEXT_ARG(); + if (key == ARG_bitrate) + val = &bt.bitrate; + else if (key == ARG_tq) + val = &bt.tq; + else if (key == ARG_prop_seg) + val = &bt.prop_seg; + else if (key == ARG_phase_seg1) + val = &bt.phase_seg1; + else if (key == ARG_phase_seg2) + val = &bt.phase_seg2; + else + val = &bt.sjw; + + *val = get_u32(*argv, keyword); + break; + } + case ARG_sample_point: { + NEXT_ARG(); + bt.sample_point = get_float_1000(*argv, keyword); + break; + } + case ARG_dbitrate: + case ARG_dtq: + case ARG_dprop_seg: + case ARG_dphase_seg1: + case ARG_dphase_seg2: + case ARG_dsjw: { + uint32_t *val; + NEXT_ARG(); + if (key == ARG_dbitrate) + val = &dbt.bitrate; + else if (key == ARG_dtq) + val = &dbt.tq; + else if (key == ARG_dprop_seg) + val = &dbt.prop_seg; + else if (key == ARG_dphase_seg1) + val = &dbt.phase_seg1; + else if (key == ARG_dphase_seg2) + val = &dbt.phase_seg2; + else + val = &dbt.sjw; + + *val = get_u32(*argv, keyword); + break; + } + case ARG_dsample_point: { + NEXT_ARG(); + dbt.sample_point = get_float_1000(*argv, keyword); + break; + } + case ARG_loopback: + case ARG_listen_only: + case ARG_triple_sampling: + case ARG_one_shot: + case ARG_berr_reporting: + case ARG_fd: + case ARG_fd_non_iso: + case ARG_presume_ack: + case ARG_cc_len8_dlc: { + uint32_t flag = 0; + + NEXT_ARG(); + param = index_in_strings(str_on_off, *argv); + if (param < 0) + die_must_be_on_off(keyword); + + if (key == ARG_loopback) + flag = CAN_CTRLMODE_LOOPBACK; + else if (key == ARG_listen_only) + flag = CAN_CTRLMODE_LISTENONLY; + else if (key == ARG_triple_sampling) + flag = CAN_CTRLMODE_3_SAMPLES; + else if (key == ARG_one_shot) + flag = CAN_CTRLMODE_ONE_SHOT; + else if (key == ARG_berr_reporting) + flag = CAN_CTRLMODE_BERR_REPORTING; + else if (key == ARG_fd) + flag = CAN_CTRLMODE_FD; + else if (key == ARG_fd_non_iso) + flag = CAN_CTRLMODE_FD_NON_ISO; + else if (key == ARG_presume_ack) + flag = CAN_CTRLMODE_PRESUME_ACK; + else +#if defined(CAN_CTRLMODE_CC_LEN8_DLC) + flag = CAN_CTRLMODE_CC_LEN8_DLC; +#else + die_must_be_on_off(keyword); +#endif + cm.mask |= flag; + if (param == PARM_on) + cm.flags |= flag; + + break; + } + case ARG_restart: { + uint32_t val = 1; + /*NEXT_ARG(); - WRONG? */ + addattr_l(&req.n, sizeof(req), IFLA_CAN_RESTART, &val, sizeof(val)); + break; + } + case ARG_restart_ms: { + uint32_t val; + NEXT_ARG(); + val = get_u32(*argv, keyword); + addattr_l(&req.n, sizeof(req), IFLA_CAN_RESTART_MS, &val, sizeof(val)); + break; + } + case ARG_termination: { + uint16_t val; + NEXT_ARG(); + val = get_u16(*argv, keyword); + addattr_l(&req.n, sizeof(req), IFLA_CAN_TERMINATION, &val, sizeof(val)); + break; + } + default: + break; + } + argv++; + } + + if (bt.bitrate || bt.tq) + addattr_l(&req.n, sizeof(req), IFLA_CAN_BITTIMING, &bt, sizeof(bt)); + + if (cm.mask) + addattr_l(&req.n, sizeof(req), IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); + + data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; + linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo; + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + xfunc_die(); +} + +static void set_type(char *type, char *dev, char **argv) +{ +/* When we have more than just "type can ARGS" supported, maybe: + static const char keywords[] ALIGN1 = "" + IF_FEATURE_IP_LINK_CAN("can\0") + ; + typedef void FAST_FUNC(*ip_type_set_func_ptr_t)(char*, char**); + static const ip_type_set_func_ptr_t funcs[] ALIGN_PTR = { + IF_FEATURE_IP_LINK_CAN(do_set_can,) + }; + ip_type_set_func_ptr_t func; + int key; + + key = index_in_substrings(keywords, type); + if (key < 0) + invarg_1_to_2(type, "type"); + func = funcs[key]; + func(dev, argv); +*/ + if (strcmp(type, "can") != 0) + invarg_1_to_2(type, "type"); + do_set_can(dev, argv); +} +#endif + /* Return value becomes exitcode. It's okay to not return at all */ static int do_set(char **argv) { char *dev = NULL; +#if ENABLE_FEATURE_IP_LINK_CAN + char *type = NULL; +#endif uint32_t mask = 0; uint32_t flags = 0; int qlen = -1; @@ -261,18 +521,24 @@ static int do_set(char **argv) "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" "arp\0""promisc\0""address\0""netns\0" "master\0""nomaster\0" +#if ENABLE_FEATURE_IP_LINK_CAN + "type\0" +#endif "dev\0" /* must be last */; enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast, ARG_arp, ARG_promisc, ARG_addr, ARG_netns, ARG_master, ARG_nomaster, +#if ENABLE_FEATURE_IP_LINK_CAN + ARG_type, +#endif ARG_dev }; - enum { PARM_on = 0, PARM_off }; smalluint key; while (*argv) { /* substring search ensures that e.g. "addr" and "address" * are both accepted */ key = index_in_substrings(keywords, *argv); + //printf("%s: key: %d, *argv: %s\n", __func__, key, *argv); if (key == ARG_up) { mask |= IFF_UP; flags |= IFF_UP; @@ -304,6 +570,13 @@ static int do_set(char **argv) } else if (key == ARG_netns) { NEXT_ARG(); netns = get_unsigned(*argv, "netns"); +#if ENABLE_FEATURE_IP_LINK_CAN + } else if (key == ARG_type) { + NEXT_ARG(); + type = *argv; + argv++; + break; +#endif } else if (key >= ARG_dev) { /* ^^^^^^ ">=" here results in "dev IFACE" treated as default */ if (key == ARG_dev) { @@ -311,6 +584,7 @@ static int do_set(char **argv) } if (dev) duparg2("dev", *argv); + dev = *argv; } else { /* "on|off" options */ @@ -496,6 +770,10 @@ static int do_set(char **argv) } if (mask) do_chflags(dev, flags, mask); +#if ENABLE_FEATURE_IP_LINK_CAN + if (type) + set_type(type, dev, argv); +#endif return 0; } @@ -531,10 +809,6 @@ static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size) PROTO_8021Q = 0, PROTO_8021AD, }; - enum { - PARM_on = 0, - PARM_off - }; int arg; uint16_t id, proto; struct ifla_vlan_flags flags = {}; @@ -610,10 +884,6 @@ static void vrf_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size) addattr_l(n, size, IFLA_VRF_TABLE, &table, sizeof(table)); } -#ifndef NLMSG_TAIL -#define NLMSG_TAIL(nmsg) \ - ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) -#endif /* Return value becomes exitcode. It's okay to not return at all */ static int do_add_or_delete(char **argv, const unsigned rtm) { -- cgit v1.2.3-55-g6feb From c5a1be25ba6dd705382ce6c25d96a32f79974c04 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 26 Feb 2024 16:26:15 +0100 Subject: ash: fix handling of single-quoted strings in pattern substitution function old new delta subevalvar 1576 1588 +12 Signed-off-by: Denys Vlasenko --- shell/ash.c | 5 +++++ shell/ash_test/ash-quoting/dollar_repl_bash2.right | 4 ++++ shell/ash_test/ash-quoting/dollar_repl_bash2.tests | 8 ++++++++ shell/hush_test/hush-quoting/dollar_repl_bash2.right | 4 ++++ shell/hush_test/hush-quoting/dollar_repl_bash2.tests | 8 ++++++++ 5 files changed, 29 insertions(+) create mode 100644 shell/ash_test/ash-quoting/dollar_repl_bash2.right create mode 100755 shell/ash_test/ash-quoting/dollar_repl_bash2.tests create mode 100644 shell/hush_test/hush-quoting/dollar_repl_bash2.right create mode 100755 shell/hush_test/hush-quoting/dollar_repl_bash2.tests diff --git a/shell/ash.c b/shell/ash.c index 771fc8bf9..4ca4c6c56 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7073,6 +7073,11 @@ subevalvar(char *start, char *str, int strloc, repl = NULL; break; } + /* Skip over quoted 'str'. Example: ${var/'/'} - second / is not a separator */ + if ((unsigned char)*repl == CTLQUOTEMARK) { + while ((unsigned char)*++repl != CTLQUOTEMARK) + continue; + } if (*repl == '/') { *repl = '\0'; break; diff --git a/shell/ash_test/ash-quoting/dollar_repl_bash2.right b/shell/ash_test/ash-quoting/dollar_repl_bash2.right new file mode 100644 index 000000000..e3fcd5807 --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_repl_bash2.right @@ -0,0 +1,4 @@ +axxb +axxb +axxb +axxb diff --git a/shell/ash_test/ash-quoting/dollar_repl_bash2.tests b/shell/ash_test/ash-quoting/dollar_repl_bash2.tests new file mode 100755 index 000000000..45c7a10e2 --- /dev/null +++ b/shell/ash_test/ash-quoting/dollar_repl_bash2.tests @@ -0,0 +1,8 @@ +v="x/x" +# The second / is quoted, should not be treated as separator +echo a${v/'/'}b +# The second / is escaped, should not be treated as separator +echo a${v/\/}b + +echo "a${v/'/'}b" +echo "a${v/\/}b" diff --git a/shell/hush_test/hush-quoting/dollar_repl_bash2.right b/shell/hush_test/hush-quoting/dollar_repl_bash2.right new file mode 100644 index 000000000..e3fcd5807 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_repl_bash2.right @@ -0,0 +1,4 @@ +axxb +axxb +axxb +axxb diff --git a/shell/hush_test/hush-quoting/dollar_repl_bash2.tests b/shell/hush_test/hush-quoting/dollar_repl_bash2.tests new file mode 100755 index 000000000..45c7a10e2 --- /dev/null +++ b/shell/hush_test/hush-quoting/dollar_repl_bash2.tests @@ -0,0 +1,8 @@ +v="x/x" +# The second / is quoted, should not be treated as separator +echo a${v/'/'}b +# The second / is escaped, should not be treated as separator +echo a${v/\/}b + +echo "a${v/'/'}b" +echo "a${v/\/}b" -- cgit v1.2.3-55-g6feb From 01f2b5976d03fb50229d28297cf5ebd8be533203 Mon Sep 17 00:00:00 2001 From: Jones Syue Date: Mon, 29 Jan 2024 10:58:41 +0800 Subject: crond: log5 fix typo, replace log level '4' with '5' log5() with crondlog(5, msg, va) seems making logging more consistent. function old new delta ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/0 up/down: 0/0) Total: 0 bytes Signed-off-by: Jones Syue Signed-off-by: Bernhard Reutner-Fischer --- miscutils/crond.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miscutils/crond.c b/miscutils/crond.c index 3bbb5ac83..b3762d327 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -190,7 +190,7 @@ static void log5(const char *msg, ...) { va_list va; va_start(va, msg); - crondlog(4, msg, va); + crondlog(5, msg, va); va_end(va); } -- cgit v1.2.3-55-g6feb From e1a68741067167dc4837e0a26d3d5c318a631fc7 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Fri, 19 Jan 2024 15:41:17 +0000 Subject: awk: fix segfault when compiled by clang A 32-bit build of BusyBox using clang segfaulted in the test "awk assign while assign". Specifically, on line 7 of the test input where the adjustment of the L.v pointer when the Fields array was reallocated L.v += Fields - old_Fields_ptr; was out by 4 bytes. Rearrange to code so both gcc and clang generate code that works. Signed-off-by: Ron Yorston Signed-off-by: Bernhard Reutner-Fischer --- editors/awk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/awk.c b/editors/awk.c index aa485c782..0981c6735 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -3006,7 +3006,7 @@ static var *evaluate(node *op, var *res) if (old_Fields_ptr) { //if (old_Fields_ptr != Fields) // debug_printf_eval("L.v moved\n"); - L.v += Fields - old_Fields_ptr; + L.v = Fields + (L.v - old_Fields_ptr); } if (opinfo & OF_STR2) { R.s = getvar_s(R.v); -- cgit v1.2.3-55-g6feb From 681e4f5d922b9f0ea968238750d5c5d748eac809 Mon Sep 17 00:00:00 2001 From: Yan Zhu Date: Tue, 12 Sep 2023 00:03:09 +0800 Subject: docproc: avoid segfault during file closing In the function find_export_symbols, since the fopen file does not exit when it fails, there is a dereference problem in fclose(fp), which will cause a segmentation fault. Signed-off-by: Yan Zhu Signed-off-by: Denys Vlasenko --- scripts/basic/docproc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/basic/docproc.c b/scripts/basic/docproc.c index 4464e1874..8828901a1 100644 --- a/scripts/basic/docproc.c +++ b/scripts/basic/docproc.c @@ -180,6 +180,7 @@ void find_export_symbols(char * filename) { fprintf(stderr, "docproc: "); perror(real_filename); + exit(1); } while (fgets(line, MAXLINESZ, fp)) { unsigned char *p; -- cgit v1.2.3-55-g6feb From fd47f056765aed515f4c71118813f07be1402bee Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 13 Apr 2024 18:40:20 +0200 Subject: lineedit: print prompt and editing operations to stderr For shells, this is mandated by standards function old new delta input_backward 215 231 +16 read_line_input 3015 3028 +13 draw_custom 66 78 +12 put_cur_glyph_and_inc_cursor 149 159 +10 put_prompt_custom 47 56 +9 show_history 40 46 +6 input_tab 927 933 +6 input_delete 136 142 +6 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 8/0 up/down: 78/0) Total: 78 bytes Signed-off-by: Denys Vlasenko --- libbb/lineedit.c | 62 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/libbb/lineedit.c b/libbb/lineedit.c index bdae10914..543a3f11c 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -345,7 +345,7 @@ static unsigned save_string(char *dst, unsigned maxsize) return i; } } -/* I thought just fputwc(c, stdout) would work. But no... */ +/* I thought just fputwc(c, stderr) would work. But no... */ static void BB_PUTCHAR(wchar_t c) { if (unicode_status == UNICODE_ON) { @@ -354,11 +354,11 @@ static void BB_PUTCHAR(wchar_t c) ssize_t len = wcrtomb(buf, c, &mbst); if (len > 0) { buf[len] = '\0'; - fputs_stdout(buf); + fputs(buf, stderr); } } else { /* In this case, c is always one byte */ - putchar(c); + bb_putchar_stderr(c); } } # if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS @@ -404,7 +404,7 @@ static void save_string(char *dst, unsigned maxsize) safe_strncpy(dst, command_ps, maxsize); } # endif -# define BB_PUTCHAR(c) bb_putchar(c) +# define BB_PUTCHAR(c) bb_putchar_stderr(c) /* Should never be called: */ int adjust_width_and_validate_wc(unsigned *width_adj, int wc); #endif @@ -463,7 +463,7 @@ static void put_cur_glyph_and_inc_cursor(void) if (c == BB_NUL) c = ' '; BB_PUTCHAR(c); - bb_putchar('\b'); + bb_putchar_stderr('\b'); #endif cmdedit_y++; if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { @@ -489,12 +489,12 @@ static void goto_new_line(void) put_till_end_and_adv_cursor(); /* "cursor == 0" is only if prompt is "" and user input is empty */ if (cursor == 0 || cmdedit_x != 0) - bb_putchar('\n'); + bb_putchar_stderr('\n'); } static void beep(void) { - bb_putchar('\007'); + bb_putchar_stderr('\007'); } /* Full or last/sole prompt line, reset edit cursor, calculate terminal cursor. @@ -502,7 +502,10 @@ static void beep(void) */ static void put_prompt_custom(bool is_full) { - fputs_stdout((is_full ? cmdedit_prompt : prompt_last_line)); + /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html + * says that shells must write $PSn to stderr, not stdout. + */ + fputs((is_full ? cmdedit_prompt : prompt_last_line), stderr); cursor = 0; cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */ cmdedit_x = cmdedit_prmt_len % cmdedit_termw; @@ -539,15 +542,15 @@ static void input_backward(unsigned num) /* This is longer by 5 bytes on x86. * Also gets miscompiled for ARM users * (busybox.net/bugs/view.php?id=2274). - * printf(("\b\b\b\b" + 4) - num); + * fprintf(("\b\b\b\b" + 4) - num, stderr); * return; */ do { - bb_putchar('\b'); + bb_putchar_stderr('\b'); } while (--num); return; } - printf(ESC"[%uD", num); + fprintf(stderr, ESC"[%uD", num); return; } @@ -572,7 +575,7 @@ static void input_backward(unsigned num) */ unsigned sv_cursor; /* go to 1st column; go up to first line */ - printf("\r" ESC"[%uA", cmdedit_y); + fprintf(stderr, "\r" ESC"[%uA", cmdedit_y); cmdedit_y = 0; sv_cursor = cursor; put_prompt_last_line(); /* sets cursor to 0 */ @@ -587,12 +590,12 @@ static void input_backward(unsigned num) cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw; cmdedit_y -= lines_up; /* go to 1st column; go up */ - printf("\r" ESC"[%uA", lines_up); + fprintf(stderr, "\r" ESC"[%uA", lines_up); /* go to correct column. * xterm, konsole, Linux VT interpret 0 as 1 below! wow. * need to *make sure* we skip it if cmdedit_x == 0 */ if (cmdedit_x) - printf(ESC"[%uC", cmdedit_x); + fprintf(stderr, ESC"[%uC", cmdedit_x); } } @@ -600,11 +603,11 @@ static void input_backward(unsigned num) static void draw_custom(int y, int back_cursor, bool is_full) { if (y > 0) /* up y lines */ - printf(ESC"[%uA", y); - bb_putchar('\r'); + fprintf(stderr, ESC"[%uA", y); + bb_putchar_stderr('\r'); put_prompt_custom(is_full); put_till_end_and_adv_cursor(); - printf(SEQ_CLEAR_TILL_END_OF_SCREEN); + fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); input_backward(back_cursor); } @@ -649,7 +652,7 @@ static void input_delete(int save) command_len--; put_till_end_and_adv_cursor(); /* Last char is still visible, erase it (and more) */ - printf(SEQ_CLEAR_TILL_END_OF_SCREEN); + fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); input_backward(cursor - j); /* back to old pos cursor */ } @@ -984,8 +987,8 @@ static void remove_chunk(int16_t *int_buf, int beg, int end) if (dbg_bmp) { int i; for (i = 0; int_buf[i]; i++) - bb_putchar((unsigned char)int_buf[i]); - bb_putchar('\n'); + bb_putchar_stderr((unsigned char)int_buf[i]); + bb_putchar_stderr('\n'); } } /* Caller ensures that match_buf points to a malloced buffer @@ -1162,7 +1165,7 @@ static void showfiles(void) int nc; for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) { - printf("%s%-*s", matches[n], + fprintf(stderr, "%s%-*s", matches[n], (int)(column_width - unicode_strwidth(matches[n])), "" ); } @@ -1460,7 +1463,7 @@ void FAST_FUNC show_history(const line_input_t *st) if (!st) return; for (i = 0; i < st->cnt_history; i++) - printf("%4d %s\n", i, st->history[i]); + fprintf(stderr, "%4d %s\n", i, st->history[i]); } # if ENABLE_FEATURE_EDITING_SAVEHISTORY @@ -1900,7 +1903,7 @@ static void ask_terminal(void) pfd.events = POLLIN; if (safe_poll(&pfd, 1, 0) == 0) { S.sent_ESC_br6n = 1; - fputs_stdout(ESC"[6n"); + fputs(ESC"[6n", stderr); fflush_all(); /* make terminal see it ASAP! */ } } @@ -2639,13 +2642,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman /* Control-k -- clear to end of line */ command_ps[cursor] = BB_NUL; command_len = cursor; - printf(SEQ_CLEAR_TILL_END_OF_SCREEN); + fputs(SEQ_CLEAR_TILL_END_OF_SCREEN, stderr); break; case CTRL('L'): vi_case(CTRL('L')|VI_CMDMODE_BIT:) /* Control-l -- clear screen */ /* cursor to top,left; clear to the end of screen */ - printf(ESC"[H" ESC"[J"); + fputs(ESC"[H" ESC"[J", stderr); draw_full(command_len - cursor); break; #if MAX_HISTORY > 0 @@ -2832,8 +2835,8 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman beep(); } else { command_ps[cursor] = ic; - bb_putchar(ic); - bb_putchar('\b'); + bb_putchar_stderr(ic); + bb_putchar_stderr('\b'); } break; case '\x1b': /* ESC */ @@ -3027,7 +3030,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman #undef read_line_input int FAST_FUNC read_line_input(const char* prompt, char* command, int maxsize) { - fputs_stdout(prompt); + /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html + * says that shells must write $PSn to stderr, not stdout. + */ + fputs(prompt, stderr); fflush_all(); if (!fgets(command, maxsize, stdin)) return -1; -- cgit v1.2.3-55-g6feb From 5a68a246e750359819d63bcff5ef97dd9c7788fc Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 31 May 2024 11:56:40 +0200 Subject: nproc: prepare for arbitrarily large CPU masks function old new delta get_malloc_cpu_affinity - 76 +76 nproc_main 216 206 -10 process_pid_str 250 206 -44 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 0/2 up/down: 76/-54) Total: 22 bytes Signed-off-by: Denys Vlasenko --- coreutils/nproc.c | 9 +++++---- include/libbb.h | 2 ++ libbb/Kbuild.src | 1 + libbb/alloc_affinity.c | 29 +++++++++++++++++++++++++++++ util-linux/taskset.c | 24 +----------------------- 5 files changed, 38 insertions(+), 27 deletions(-) create mode 100644 libbb/alloc_affinity.c diff --git a/coreutils/nproc.c b/coreutils/nproc.c index f1d11fa5a..a0d818c59 100644 --- a/coreutils/nproc.c +++ b/coreutils/nproc.c @@ -23,13 +23,11 @@ //usage: "\n --ignore=N Exclude N CPUs" //usage: ) -#include #include "libbb.h" int nproc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - unsigned long mask[1024]; int count = 0; #if ENABLE_LONG_OPTS int ignore = 0; @@ -52,9 +50,12 @@ int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } } else #endif - if (sched_getaffinity(0, sizeof(mask), (void*)mask) == 0) { + { int i; - for (i = 0; i < ARRAY_SIZE(mask); i++) { + unsigned sz = 2 * 1024; + unsigned long *mask = get_malloc_cpu_affinity(0, &sz); + sz /= sizeof(long); + for (i = 0; i < sz; i++) { unsigned long m = mask[i]; while (m) { if (m & 1) diff --git a/include/libbb.h b/include/libbb.h index ef5d04713..67d29f843 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -2015,6 +2015,8 @@ int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC; read_line_input(prompt, command, maxsize) #endif +unsigned long* FAST_FUNC get_malloc_cpu_affinity(int pid, unsigned *sz); + #ifndef COMM_LEN # ifdef TASK_COMM_LEN enum { COMM_LEN = TASK_COMM_LEN }; diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src index c3b30003f..a0e2a6da7 100644 --- a/libbb/Kbuild.src +++ b/libbb/Kbuild.src @@ -10,6 +10,7 @@ lib-y:= INSERT +lib-y += alloc_affinity.o lib-y += appletlib.o lib-y += ask_confirmation.o lib-y += bb_askpass.o diff --git a/libbb/alloc_affinity.c b/libbb/alloc_affinity.c new file mode 100644 index 000000000..b6d9f649a --- /dev/null +++ b/libbb/alloc_affinity.c @@ -0,0 +1,29 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2024 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include +#include "libbb.h" + +unsigned long* FAST_FUNC get_malloc_cpu_affinity(int pid, unsigned *sz) +{ + unsigned long *mask = NULL; + unsigned sz_in_bytes = *sz; + + for (;;) { + mask = xrealloc(mask, sz_in_bytes); + if (sched_getaffinity(pid, sz_in_bytes, (void*)mask) == 0) + break; /* got it */ + sz_in_bytes *= 2; + if (errno == EINVAL && (int)sz_in_bytes > 0) + continue; + bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid); + } + //bb_error_msg("get mask[0]:%lx sz_in_bytes:%d", mask[0], sz_in_bytes); + *sz = sz_in_bytes; + return mask; +} diff --git a/util-linux/taskset.c b/util-linux/taskset.c index 55c915e8d..a3aa06119 100644 --- a/util-linux/taskset.c +++ b/util-linux/taskset.c @@ -56,7 +56,6 @@ * -a/--all-tasks (affect all threads) * needs to get TIDs from /proc/PID/task/ and use _them_ as "pid" in sched_setaffinity(pid) */ - #include #include "libbb.h" @@ -96,27 +95,6 @@ static unsigned long from_mask(ul *mask, unsigned sz_in_bytes UNUSED_PARAM) } #endif -static unsigned long *get_aff(int pid, unsigned *sz) -{ - int r; - unsigned long *mask = NULL; - unsigned sz_in_bytes = *sz; - - for (;;) { - mask = xrealloc(mask, sz_in_bytes); - r = sched_getaffinity(pid, sz_in_bytes, (void*)mask); - if (r == 0) - break; - sz_in_bytes *= 2; - if (errno == EINVAL && (int)sz_in_bytes > 0) - continue; - bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid); - } - //bb_error_msg("get mask[0]:%lx sz_in_bytes:%d", mask[0], sz_in_bytes); - *sz = sz_in_bytes; - return mask; -} - #if ENABLE_FEATURE_TASKSET_CPULIST /* * Parse the CPU list and set the mask accordingly. @@ -222,7 +200,7 @@ static int process_pid_str(const char *pid_str, unsigned opts, char *aff) mask_size_in_bytes = SZOF_UL; current_new = "current"; print_aff: - mask = get_aff(pid, &mask_size_in_bytes); + mask = get_malloc_cpu_affinity(pid, &mask_size_in_bytes); if (opts & OPT_p) { #if ENABLE_FEATURE_TASKSET_CPULIST if (opts & OPT_c) { -- cgit v1.2.3-55-g6feb From 2075553a1b0eb843d4ee73a039b3e30bed9274e0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 31 May 2024 12:01:43 +0200 Subject: libbb: add bit counting function, use where appropriate Although "naive" counting function is not too slow and is smaller, using it on e.g. each of 1024 words of CPU mask feels wrong. function old new delta bb_popcnt_32 - 52 +52 get_prefix 323 321 -2 nproc_main 206 199 -7 d4_run_script 739 731 -8 ipcalc_main 533 507 -26 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 0/4 up/down: 52/-43) Total: 9 bytes Signed-off-by: Denys Vlasenko --- coreutils/nproc.c | 9 +++------ include/libbb.h | 7 +++++++ libbb/Kbuild.src | 1 + libbb/popcnt.c | 46 +++++++++++++++++++++++++++++++++++++++++++ networking/ifupdown.c | 8 +------- networking/ipcalc.c | 21 +------------------- networking/libiproute/utils.c | 11 +++++------ networking/udhcp/dhcpc.c | 15 ++------------ 8 files changed, 66 insertions(+), 52 deletions(-) create mode 100644 libbb/popcnt.c diff --git a/coreutils/nproc.c b/coreutils/nproc.c index a0d818c59..df63bf57a 100644 --- a/coreutils/nproc.c +++ b/coreutils/nproc.c @@ -56,13 +56,10 @@ int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) unsigned long *mask = get_malloc_cpu_affinity(0, &sz); sz /= sizeof(long); for (i = 0; i < sz; i++) { - unsigned long m = mask[i]; - while (m) { - if (m & 1) - count++; - m >>= 1; - } + if (mask[i] != 0) /* most mask[i] are usually 0 */ + count += bb_popcnt_long(mask[i]); } + IF_FEATURE_CLEAN_UP(free(mask);) } IF_LONG_OPTS(count -= ignore;) diff --git a/include/libbb.h b/include/libbb.h index 67d29f843..6914b5882 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -397,6 +397,13 @@ extern int *BB_GLOBAL_CONST bb_errno; uint64_t bb_bswap_64(uint64_t x) FAST_FUNC; #endif +unsigned FAST_FUNC bb_popcnt_32(uint32_t m); +#if ULONG_MAX > 0xffffffff +unsigned FAST_FUNC bb_popcnt_long(unsigned_long m); +#else +#define bb_popcnt_long(m) bb_popcnt_32(m) +#endif + unsigned long FAST_FUNC isqrt(unsigned long long N); unsigned long long monotonic_ns(void) FAST_FUNC; diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src index a0e2a6da7..cb8d2c2ec 100644 --- a/libbb/Kbuild.src +++ b/libbb/Kbuild.src @@ -72,6 +72,7 @@ lib-y += perror_nomsg.o lib-y += perror_nomsg_and_die.o lib-y += pidfile.o lib-y += platform.o +lib-y += popcnt.o lib-y += printable.o lib-y += printable_string.o lib-y += print_flags.o diff --git a/libbb/popcnt.c b/libbb/popcnt.c new file mode 100644 index 000000000..fe8cd240f --- /dev/null +++ b/libbb/popcnt.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ +/* + * Utility routines. + * + * Copyright (C) 2024 Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this source tree. + */ +#include "libbb.h" + +unsigned FAST_FUNC bb_popcnt_32(uint32_t m) +{ + /* replace each 2 bit group with the count of set bits in it */ + /* 00->00 01->01 10->01 11->10 */ + m = m - ((m >> 1) & 0x55555555); + /* in each 4 bit group, add two 2-bit counts */ + m = (m & 0x33333333) + ((m >> 2) & 0x33333333); + /* in each 8 bit group, add two 4-bit counts (in fact, 3-bit, 0nnn with n=0..4) */ + m = (m + (m >> 4)) & 0x0f0f0f0f; +#if 1 /* assume 32*32->32 multiply is fast */ + m = m * 0x01010101; /* top byte = m + (m<<8) + (m<<16) + (m<<24) */ + return m >> 24; +#else + /* 0000aaaa0000bbbb0000cccc0000dddd */ + /* + 0000aaaa0000bbbb0000cccc */ + /* = 0000xxxx000_a+b_000xxxxx000_c+d_ (we don't care about x bits) */ + m += m >> 8; /* in each 16-bit group, lowest 5 bits is the count */ + /* 0000xxxx000_a+b_000xxxxx000_c+d_ */ + /* + 0000xxxx000_a+b_ */ + /* = 0000xxxx000xxxxx00xxxxxx00a+b+cd */ + m += m >> 16; /* in each 32-bit group, lowest 6 bits is the count */ + return m & 0x3f; /* clear x bits */ +#endif +} + +#if ULONG_MAX > 0xffffffff +unsigned FAST_FUNC bb_popcnt_long(unsigned long m) +{ + BUILD_BUG_ON(sizeof(m) != 8); + /* 64-bit version of bb_popcnt_32 exists, but it uses 64-bit constants, + * which are awkward to generate on assembly level on most CPUs. + * For now, just add two 32-bit counts: + */ + return bb_popcnt_32((uint32_t)m) + bb_popcnt_32((uint32_t)(m >> 32)); +} +#endif diff --git a/networking/ifupdown.c b/networking/ifupdown.c index 6c4ae27f2..9c3640be7 100644 --- a/networking/ifupdown.c +++ b/networking/ifupdown.c @@ -306,7 +306,6 @@ static int count_netmask_bits(const char *dotted_quad) // d = ~d; /* 11110000 -> 00001111 */ /* Shorter version */ - int result; struct in_addr ip; unsigned d; @@ -316,12 +315,7 @@ static int count_netmask_bits(const char *dotted_quad) d = ~d; /* 11110000 -> 00001111 */ if (d & (d+1)) /* check that it is in 00001111 form */ return -1; /* no it is not */ - result = 32; - while (d) { - d >>= 1; - result--; - } - return result; + return bb_popcnt_32(~d); } # endif diff --git a/networking/ipcalc.c b/networking/ipcalc.c index 92e7b289d..3ec473c6b 100644 --- a/networking/ipcalc.c +++ b/networking/ipcalc.c @@ -71,25 +71,6 @@ static unsigned long get_netmask(unsigned long ipaddr) return 0; } -#if ENABLE_FEATURE_IPCALC_FANCY -static int get_prefix(unsigned long netmask) -{ - unsigned long msk = 0x80000000; - int ret = 0; - - netmask = htonl(netmask); - while (msk) { - if (netmask & msk) - ret++; - msk >>= 1; - } - return ret; -} -#else -int get_prefix(unsigned long netmask); -#endif - - #define NETMASK 0x01 #define BROADCAST 0x02 #define NETWORK 0x04 @@ -210,7 +191,7 @@ int ipcalc_main(int argc UNUSED_PARAM, char **argv) if (ENABLE_FEATURE_IPCALC_FANCY) { if (opt & NETPREFIX) { - printf("PREFIX=%i\n", get_prefix(netmask)); + printf("PREFIX=%i\n", bb_popcnt_32(netmask)); } if (opt & HOSTNAME) { diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c index 4ce230356..3cce4a06e 100644 --- a/networking/libiproute/utils.c +++ b/networking/libiproute/utils.c @@ -175,14 +175,13 @@ static void get_prefix_1(inet_prefix *dst, char *arg, int family) if (netmask_pfx.family == AF_INET) { /* fill in prefix length of dotted quad */ uint32_t mask = ntohl(netmask_pfx.data[0]); - uint32_t host = ~mask; + uint32_t inv = ~mask; - /* a valid netmask must be 2^n - 1 */ - if (host & (host + 1)) - goto bad; + /* a valid netmask must be 11..10..00 */ + if (inv & (inv + 1)) + goto bad; /* inv is not 00..01..11 */ - for (plen = 0; mask; mask <<= 1) - ++plen; + plen = bb_popcnt_32(mask); if (plen > dst->bitlen) goto bad; /* dst->flags |= PREFIXLEN_SPECIFIED; */ diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 07e2eadfe..e44086c2e 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -150,18 +150,6 @@ static int sprint_nip(char *dest, const char *pre, const uint8_t *ip) return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); } -/* really simple implementation, just count the bits */ -static int mton(uint32_t mask) -{ - int i = 0; - mask = ntohl(mask); /* 111110000-like bit pattern */ - while (mask) { - i++; - mask <<= 1; - } - return i; -} - #if ENABLE_FEATURE_UDHCPC_SANITIZEOPT /* Check if a given name represents a valid DNS name */ /* See RFC1035, 2.3.1 */ @@ -508,7 +496,8 @@ static void fill_envp(struct dhcp_packet *packet) /* Generate extra envvar for DHCP_SUBNET, $mask */ uint32_t subnet; move_from_unaligned32(subnet, opt_item->data); - putenvp(xasprintf("mask=%u", mton(subnet))); +//FIXME: we do not check that subnet has bit pattern 11..10..0 + putenvp(xasprintf("mask=%u", bb_popcnt_32(subnet))); } } else { unsigned ofs; -- cgit v1.2.3-55-g6feb From 41b47398f52c6eb8ef2a061127bc36c54e3cc372 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 31 May 2024 23:37:28 +0200 Subject: libbb: fix 64-bit bb_popcnt_long Signed-off-by: Denys Vlasenko --- include/libbb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libbb.h b/include/libbb.h index 6914b5882..01cdb1bdc 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -399,7 +399,7 @@ uint64_t bb_bswap_64(uint64_t x) FAST_FUNC; unsigned FAST_FUNC bb_popcnt_32(uint32_t m); #if ULONG_MAX > 0xffffffff -unsigned FAST_FUNC bb_popcnt_long(unsigned_long m); +unsigned FAST_FUNC bb_popcnt_long(unsigned long m); #else #define bb_popcnt_long(m) bb_popcnt_32(m) #endif -- cgit v1.2.3-55-g6feb From a6ce017a8a2db09c6f23aa6abf7ce21fd00c2fdf Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 1 Jun 2024 10:51:58 +0200 Subject: typo fix Signed-off-by: Denys Vlasenko --- networking/nc_bloaty.c | 2 +- networking/udhcp/d6_dhcpc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c index cfa133eae..d35c9e140 100644 --- a/networking/nc_bloaty.c +++ b/networking/nc_bloaty.c @@ -31,7 +31,7 @@ * Much of author's comments are still retained in the code. * * Functionality removed (rationale): - * - miltiple-port ranges, randomized port scanning (use nmap) + * - multiple-port ranges, randomized port scanning (use nmap) * - telnet support (use telnet) * - source routing * - multiple DNS checks diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index a72fd31bd..79cef1999 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -604,7 +604,7 @@ static NOINLINE int send_d6_info_request(void) return d6_mcast_from_client_data_ifindex(&packet, opt_ptr); } -/* Milticast a DHCPv6 Solicit packet to the network, with an optionally requested IP. +/* Multicast a DHCPv6 Solicit packet to the network, with an optionally requested IP. * * RFC 3315 17.1.1. Creation of Solicit Messages * -- cgit v1.2.3-55-g6feb