From 5e0e54827fb0fa80d2c894eb67e8696921095935 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 13 Dec 2023 14:19:48 +0100 Subject: util-linux/lsusb.c: print manufacturer/product strings if available Just listing the vendor/product IDs is not always very helpful, so add logic to print the manufacturer and product strings similar to the "big" usbutils versions. Not all devices provide sensible strings though. The usbutils version works around this by falling back to looking up the vendor/product IDs in the hwdb and using those strings instead, which is not an option here - Instead simply trim() the strings for readability. lsusb | sort Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 002: ID 0bda:5539 Realtek Semiconductor Corp. Integrated_Webcam_HD Bus 001 Device 003: ID 0a5c:5842 Broadcom Corp. 58200 Bus 001 Device 030: ID 8087:0aaa Intel Corp. Bluetooth 9460/9560 Jefferson Peak (JfP) Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 006 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 006 Device 002: ID 0bda:5487 Realtek Semiconductor Corp. Dell dock Bus 006 Device 003: ID 0bda:5413 Realtek Semiconductor Corp. Dell dock Bus 006 Device 004: ID 413c:b06e Dell Computer Corp. Dell dock Bus 006 Device 005: ID 0451:8142 Texas Instruments, Inc. TUSB8041 4-Port Hub Bus 006 Device 006: ID 0bda:402e Realtek Semiconductor Corp. USB Audio Bus 006 Device 007: ID 413c:1010 Dell Computer Corp. USB 2.0 Hub [MTT] Bus 006 Device 008: ID 413c:b06f Dell Computer Corp. Dell dock Bus 006 Device 009: ID 046d:c016 Logitech, Inc. Optical Wheel Mouse Bus 006 Device 010: ID 413c:2110 Dell Computer Corp. Dell Wired Multimedia Keyboard Bus 006 Device 011: ID 0451:8142 Texas Instruments, Inc. TUSB8041 4-Port Hub Bus 006 Device 012: ID 0451:3410 Texas Instruments, Inc. TUSB3410 Microcontroller Bus 007 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 007 Device 002: ID 0bda:0487 Realtek Semiconductor Corp. Dell dock Bus 007 Device 003: ID 0bda:0413 Realtek Semiconductor Corp. Dell dock Bus 007 Device 004: ID 0bda:8153 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter ./busybox lsusb | sort Bus 001 Device 001: ID 1d6b:0002 Linux 6.1.0-13-amd64 xhci-hcd xHCI Host Controller Bus 001 Device 002: ID 0bda:5539 CNFHH53Q0324300ACA10 Integrated_Webcam_HD Bus 001 Device 003: ID 0a5c:5842 Broadcom Corp 58200 Bus 001 Device 030: ID 8087:0aaa Bus 002 Device 001: ID 1d6b:0003 Linux 6.1.0-13-amd64 xhci-hcd xHCI Host Controller Bus 003 Device 001: ID 1d6b:0002 Linux 6.1.0-13-amd64 xhci-hcd xHCI Host Controller Bus 004 Device 001: ID 1d6b:0003 Linux 6.1.0-13-amd64 xhci-hcd xHCI Host Controller Bus 005 Device 001: ID 1d6b:0002 Linux 6.1.0-13-amd64 dummy_hcd Dummy host controller Bus 006 Device 001: ID 1d6b:0002 Linux 6.1.0-13-amd64 xhci-hcd xHCI Host Controller Bus 006 Device 002: ID 0bda:5487 Dell Inc. Dell dock Bus 006 Device 003: ID 0bda:5413 Dell Inc. Dell dock Bus 006 Device 004: ID 413c:b06e Dell dock Bus 006 Device 005: ID 0451:8142 Bus 006 Device 006: ID 0bda:402e Generic USB Audio Bus 006 Device 007: ID 413c:1010 USB 2.0 Hub [MTT] Bus 006 Device 008: ID 413c:b06f Dell dock Bus 006 Device 009: ID 046d:c016 Logitech Optical USB Mouse Bus 006 Device 010: ID 413c:2110 Dell Dell Wired Multimedia Keyboard Bus 006 Device 011: ID 0451:8142 Bus 006 Device 012: ID 0451:3410 Texas Instruments TUSB3410 Boot Device Bus 007 Device 001: ID 1d6b:0003 Linux 6.1.0-13-amd64 xhci-hcd xHCI Host Controller Bus 007 Device 002: ID 0bda:0487 Dell Inc. Dell dock Bus 007 Device 003: ID 0bda:0413 Dell Inc. Dell dock Bus 007 Device 004: ID 0bda:8153 Realtek USB 10/100/1000 LAN ./scripts/bloat-o-meter busybox_unstripped{_orig,} function old new delta trim - 101 +101 fileAction 338 431 +93 add_sysfs_prop - 70 +70 open_read_close - 54 +54 read_close - 35 +35 .rodata 3268 3294 +26 ------------------------------------------------------------------------------ (add/remove: 5/0 grow/shrink: 2/0 up/down: 379/0) Total: 379 bytes Signed-off-by: Peter Korsgaard --- util-linux/lsusb.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c index 262c70a1b..0a9e505f4 100644 --- a/util-linux/lsusb.c +++ b/util-linux/lsusb.c @@ -24,6 +24,24 @@ #include "libbb.h" +static char * FAST_FUNC add_sysfs_prop(const char *dir, const char *suffix, + char *buf, size_t size) +{ + char *filename; + ssize_t len; + + filename = concat_path_file(dir, suffix); + len = open_read_close(filename, buf, size - 1); + free(filename); + + if (len < 0) + len = 0; + + buf[len] = '\0'; + + return trim(buf); +} + static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM, const char *fileName, struct stat *statbuf UNUSED_PARAM) @@ -61,7 +79,15 @@ static int FAST_FUNC fileAction(struct recursive_state *state UNUSED_PARAM, config_close(parser); if (busnum) { - printf("Bus %s Device %s: ID %04x:%04x\n", busnum, devnum, product_vid, product_did); + char name[256], *p; + + p = add_sysfs_prop(fileName, "/manufacturer", name, sizeof(name) - 1); + if (p != name) + p = stpcpy(p, " "); + add_sysfs_prop(fileName, "/product", p, name + sizeof(name) - p); + + printf("Bus %s Device %s: ID %04x:%04x %s\n", busnum, devnum, + product_vid, product_did, name); free(busnum); free(devnum); } -- cgit v1.2.3-55-g6feb From 789ccac7d9d1a9e433570ac9628992a01f946643 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 31 Dec 2023 15:49:54 +0100 Subject: awk: fix handling of empty fields Patch by M Rubon : Busybox awk handles references to empty (not provided in the input) fields differently during the first line of input, as compared to subsequent lines. $ (echo a ; echo b) | awk '$2 != 0' #wrong b No field $2 value is provided in the input. When awk references field $2 for the "a" line, it is seen to have a different behaviour than when it is referenced for the "b" line. Problem in BusyBox v1.36.1 embedded in OpenWrt 23.05.0 Same problem also in 21.02 versions of OpenWrt Same problem in BusyBox v1.37.0.git I get the correct expected output from Ubuntu gawk and Debian mawk, and from my fix. will@dev:~$ (echo a ; echo b) | awk '$2 != 0' #correct a b will@dev:~/busybox$ (echo a ; echo b ) | ./busybox awk '$2 != 0' #fixed a b I built and poked into the source code at editors/awk.c The function fsrealloc(int size) is core to allocating, initializing, reallocating, and reinitializing fields, both real input line fields and imaginary fields that the script references but do not exist in the input. When fsrealloc() needs more field space than it has previously allocated, it initializes those new fields differently than how they are later reinitialized for the next input line. This works fine for fields defined in the input, like $1, but does not work the first time when there is no input for that field (e.g. field $99) My one-line fix simply makes the initialization and clrvar() reinitialization use the same value for .type. I am not sure if there are regression tests to run, but I have not done those. I'm not sure if I understand why clrvar() is not setting .type to a default constant value, but in any case I have left that untouched. function old new delta ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 0/0 up/down: 0/0) Total: 0 bytes Signed-off-by: Denys Vlasenko --- editors/awk.c | 33 +++++++++++++++++---------------- testsuite/awk.tests | 7 +++++++ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/editors/awk.c b/editors/awk.c index bc95c4155..aa485c782 100644 --- a/editors/awk.c +++ b/editors/awk.c @@ -555,8 +555,9 @@ struct globals { //we are reusing ahash as fdhash, via define (see later) const char *g_progname; int g_lineno; - int nfields; - unsigned maxfields; + int num_fields; /* number of existing $N's */ + unsigned num_alloc_fields; /* current size of Fields[] */ + /* NB: Fields[0] corresponds to $1, not to $0 */ var *Fields; char *g_pos; char g_saved_ch; @@ -631,8 +632,8 @@ struct globals2 { // for fdhash in execution stage. #define g_progname (G1.g_progname ) #define g_lineno (G1.g_lineno ) -#define nfields (G1.nfields ) -#define maxfields (G1.maxfields ) +#define num_fields (G1.num_fields ) +#define num_alloc_fields (G1.num_alloc_fields) #define Fields (G1.Fields ) #define g_pos (G1.g_pos ) #define g_saved_ch (G1.g_saved_ch ) @@ -1966,30 +1967,30 @@ static void fsrealloc(int size) { int i, newsize; - if ((unsigned)size >= maxfields) { + if ((unsigned)size >= num_alloc_fields) { /* Sanity cap, easier than catering for over/underflows */ if ((unsigned)size > 0xffffff) bb_die_memory_exhausted(); - i = maxfields; - maxfields = size + 16; + i = num_alloc_fields; + num_alloc_fields = size + 16; - newsize = maxfields * sizeof(Fields[0]); + newsize = num_alloc_fields * sizeof(Fields[0]); debug_printf_eval("fsrealloc: xrealloc(%p, %u)\n", Fields, newsize); Fields = xrealloc(Fields, newsize); debug_printf_eval("fsrealloc: Fields=%p..%p\n", Fields, (char*)Fields + newsize - 1); /* ^^^ did Fields[] move? debug aid for L.v getting "upstaged" by R.v in evaluate() */ - for (; i < maxfields; i++) { - Fields[i].type = VF_SPECIAL; + for (; i < num_alloc_fields; i++) { + Fields[i].type = VF_SPECIAL | VF_DIRTY; Fields[i].string = NULL; } } - /* if size < nfields, clear extra field variables */ - for (i = size; i < nfields; i++) { + /* if size < num_fields, clear extra field variables */ + for (i = size; i < num_fields; i++) { clrvar(Fields + i); } - nfields = size; + num_fields = size; } static int regexec1_nonempty(const regex_t *preg, const char *s, regmatch_t pmatch[]) @@ -2126,7 +2127,7 @@ static void split_f0(void) /* set NF manually to avoid side effects */ clrvar(intvar[NF]); intvar[NF]->type = VF_NUMBER | VF_SPECIAL; - intvar[NF]->number = nfields; + intvar[NF]->number = num_fields; #undef fstrings } @@ -2976,7 +2977,7 @@ static var *evaluate(node *op, var *res) syntax_error(EMSG_TOO_FEW_ARGS); L.v = evaluate(op1, TMPVAR0); /* Does L.v point to $n variable? */ - if ((size_t)(L.v - Fields) < maxfields) { + if ((size_t)(L.v - Fields) < num_alloc_fields) { /* yes, remember where Fields[] is */ old_Fields_ptr = Fields; } @@ -3517,7 +3518,7 @@ static var *evaluate(node *op, var *res) res = intvar[F0]; } else { split_f0(); - if (i > nfields) + if (i > num_fields) fsrealloc(i); res = &Fields[i - 1]; } diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 5a792c241..063084a1c 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests @@ -592,6 +592,13 @@ testing 'awk gensub backslashes \\0' \ \\0|\\0 ' '' '' +# References to empty (not provided in the input) fields in first versus subsequent lines +testing 'awk references to empty fields' \ + 'awk '$sq'$2 != 0'$sq \ + 'a +b +' '' 'a\nb\n' + # The "b" in "abc" should not match Date: Wed, 20 Dec 2023 12:23:31 +0100 Subject: time: fix max resident set size unit The ru_maxrss is already in Kbytes and not pages. function old new delta ptok 21 - -21 time_main 1261 1217 -44 ------------------------------------------------------------------------------ (add/remove: 0/1 grow/shrink: 0/1 up/down: 0/-65) Total: -65 bytes fixes: https://bugs.busybox.net/show_bug.cgi?id=15751 Signed-off-by: Natanael Copa Signed-off-by: Denys Vlasenko --- miscutils/time.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/miscutils/time.c b/miscutils/time.c index 5a8fa4c0b..4b1b043c3 100644 --- a/miscutils/time.c +++ b/miscutils/time.c @@ -111,6 +111,7 @@ static void printargv(char *const *argv) } while (*++argv); } +#ifdef UNUSED /* Return the number of kilobytes corresponding to a number of pages PAGES. (Actually, we use it to convert pages*ticks into kilobytes*ticks.) @@ -136,6 +137,7 @@ static unsigned long ptok(const unsigned pagesize, const unsigned long pages) return tmp / 1024; /* then smaller. */ } #undef pagesize +#endif /* UNUSED */ /* summarize: Report on the system use of a command. @@ -250,9 +252,13 @@ static void summarize(const char *fmt, char **command, resource_t *resp) printargv(command); break; case 'D': /* Average unshared data size. */ + /* (linux kernel sets ru_idrss/isrss/ixrss to 0, + * docs say the value is in kbytes, so ptok() is wrong) */ printf("%lu", - (ptok(pagesize, (UL) resp->ru.ru_idrss) + - ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks); + (/*ptok(pagesize,*/ (UL) resp->ru.ru_idrss + + (UL) resp->ru.ru_isrss + ) / cpu_ticks + ); break; case 'E': { /* Elapsed real (wall clock) time. */ unsigned seconds = resp->elapsed_ms / 1000; @@ -275,13 +281,17 @@ static void summarize(const char *fmt, char **command, resource_t *resp) printf("%lu", resp->ru.ru_inblock); break; case 'K': /* Average mem usage == data+stack+text. */ + /* (linux kernel sets ru_idrss/isrss/ixrss to 0, + * docs say the value is in kbytes, so ptok() is wrong) */ printf("%lu", - (ptok(pagesize, (UL) resp->ru.ru_idrss) + - ptok(pagesize, (UL) resp->ru.ru_isrss) + - ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks); + (/*ptok(pagesize,*/ (UL) resp->ru.ru_idrss + + (UL) resp->ru.ru_isrss + + (UL) resp->ru.ru_ixrss + ) / cpu_ticks + ); break; case 'M': /* Maximum resident set size. */ - printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss)); + printf("%lu", (UL) resp->ru.ru_maxrss); break; case 'O': /* Outputs. */ printf("%lu", resp->ru.ru_oublock); @@ -334,7 +344,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) printf("%lu", resp->ru.ru_nswap); break; case 'X': /* Average shared text size. */ - printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks); + printf("%lu", /*ptok(pagesize,*/ (UL) resp->ru.ru_ixrss / cpu_ticks); break; case 'Z': /* Page size. */ printf("%u", pagesize); @@ -351,7 +361,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) printf("%lu", resp->ru.ru_nsignals); break; case 'p': /* Average stack segment. */ - printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks); + printf("%lu", /*ptok(pagesize,*/ (UL) resp->ru.ru_isrss / cpu_ticks); break; case 'r': /* Incoming socket messages received. */ printf("%lu", resp->ru.ru_msgrcv); @@ -360,7 +370,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) printf("%lu", resp->ru.ru_msgsnd); break; case 't': /* Average resident set size. */ - printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks); + printf("%lu", /*ptok(pagesize,*/ (UL) resp->ru.ru_idrss / cpu_ticks); break; case 'w': /* Voluntary context switches. */ printf("%lu", resp->ru.ru_nvcsw); -- cgit v1.2.3-55-g6feb From 1cac2585217f830d29f7e9d2a1226d55c80886b6 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 1 Jan 2024 22:22:15 +0100 Subject: time: implement %% and \escapes in -f FMT function old new delta time_main 1217 1316 +99 Signed-off-by: Denys Vlasenko --- miscutils/time.c | 39 ++++++++++++++++++--------------------- testsuite/time.tests | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 21 deletions(-) create mode 100755 testsuite/time.tests diff --git a/miscutils/time.c b/miscutils/time.c index 4b1b043c3..77d35a832 100644 --- a/miscutils/time.c +++ b/miscutils/time.c @@ -226,28 +226,19 @@ static void summarize(const char *fmt, char **command, resource_t *resp) } switch (*fmt) { -#ifdef NOT_NEEDED - /* Handle literal char */ - /* Usually we optimize for size, but there is a limit - * for everything. With this we do a lot of 1-byte writes */ - default: - bb_putchar(*fmt); - break; -#endif - case '%': switch (*++fmt) { -#ifdef NOT_NEEDED_YET - /* Our format strings do not have these */ - /* and we do not take format str from user */ default: - bb_putchar('%'); + /* Unknown % is printed as "?" */ + bb_putchar('?'); + if (!*fmt) { + /* Trailing -f '...%' prints "...?" but NOT newline */ + goto ret; + } /*FALLTHROUGH*/ case '%': - if (!*fmt) goto ret; bb_putchar(*fmt); break; -#endif case 'C': /* The command that got timed. */ printargv(command); break; @@ -381,14 +372,21 @@ static void summarize(const char *fmt, char **command, resource_t *resp) } break; -#ifdef NOT_NEEDED_YET - case '\\': /* Format escape. */ + default: /* *fmt is '\': format escape */ switch (*++fmt) { default: + /* Unknown \ is printed as "?\" */ + bb_putchar('?'); bb_putchar('\\'); + if (!*fmt) { + /* Trailing -f '...\': GNU time 1.9 prints + * "...?\COMMAND" (it's probably a bug). + */ + puts(command[0]); + goto ret; + } /*FALLTHROUGH*/ case '\\': - if (!*fmt) goto ret; bb_putchar(*fmt); break; case 't': @@ -399,12 +397,11 @@ static void summarize(const char *fmt, char **command, resource_t *resp) break; } break; -#endif } ++fmt; } - /* ret: */ bb_putchar('\n'); + ret: ; } /* Run command CMD and return statistics on it. @@ -438,7 +435,7 @@ int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int time_main(int argc UNUSED_PARAM, char **argv) { resource_t res; - /* $TIME has lowest prio (-v,-p,-f FMT overrride it) */ + /* $TIME has lowest prio (-v,-p,-f FMT override it) */ const char *output_format = getenv("TIME") ? : default_format; char *output_filename; int output_fd; diff --git a/testsuite/time.tests b/testsuite/time.tests new file mode 100755 index 000000000..4e31b3868 --- /dev/null +++ b/testsuite/time.tests @@ -0,0 +1,37 @@ +#!/bin/sh + +# Copyright 2024 by Denys Vlasenko +# Licensed under GPLv2, see file LICENSE in this source tree. + +. ./testing.sh + +# testing "description" "arguments" "result" "infile" "stdin" + +testing "time -f trailing backslash" \ + "time -f 'abc\' sleep 0 2>&1" \ + 'abc?\sleep\n' '' '' +# ^^^^^^^^^^^^^^ this is what GNU time version 1.9 prints + +testing "time -f trailing percent" \ + "time -f 'abc%' sleep 0 2>&1" \ + 'abc?' '' '' + +testing "time -f undefined backslash" \ + "time -f 'abc\^def' sleep 0 2>&1" \ + 'abc?\^def\n' '' '' + +testing "time -f undefined percent" \ + "time -f 'abc%^def' sleep 0 2>&1" \ + 'abc?^def\n' '' '' + +testing "time -f backslash tab and newline" \ + "time -f 'abc\ndef\txyz' sleep 0 2>&1" \ + 'abc +def xyz +' '' '' + +testing "time -f percent percent" \ + "time -f 'abc%%def' sleep 0 2>&1" \ + 'abc%def\n' '' '' + +exit $FAILCOUNT -- cgit v1.2.3-55-g6feb From 5dc9ece3b9e87af0dcb01449821ac827391ac116 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Tue, 19 Sep 2023 17:11:02 +0900 Subject: sed: check errors writing file with sed -i sed would currently not error if write failed when modifying a file. This can be reproduced with the following 'script': $ sudo mount -t tmpfs tmpfs -o size=1M /tmp/m $ sudo chmod 777 /tmp/m $ echo foo > /tmp/m/foo $ dd if=/dev/zero of=/tmp/m/fill bs=4k dd: error writing '/tmp/m/fill': No space left on device 256+0 records in 255+0 records out 1044480 bytes (1.0 MB, 1020 KiB) copied, 0.00234567 s, 445 MB/s $ busybox sed -i -e 's/.*/bar/' /tmp/m/foo $ echo $? 0 $ cat /tmp/m/foo new behaviour: $ echo foo > /tmp/m/foo $ ./busybox sed -i -e 's/.*/bar/' /tmp/m/foo sed: write error $ echo $? 4 $ cat /tmp/m/foo foo function old new delta sed_main 754 801 +47 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 47/0) Total: 47 bytes text data bss dec hex filename 75727 2510 1552 79789 137ad busybox_old 75774 2510 1552 79836 137dc busybox_unstripped Signed-off-by: Dominique Martinet Signed-off-by: Denys Vlasenko --- editors/sed.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/editors/sed.c b/editors/sed.c index 00dde60be..6179c5e80 100644 --- a/editors/sed.c +++ b/editors/sed.c @@ -1648,6 +1648,11 @@ int sed_main(int argc UNUSED_PARAM, char **argv) fchown(nonstdoutfd, statbuf.st_uid, statbuf.st_gid); process_files(); + fflush(G.nonstdout); + if (ferror(G.nonstdout)) { + xfunc_error_retval = 4; /* It's what gnu sed exits with... */ + bb_simple_error_msg_and_die(bb_msg_write_error); + } fclose(G.nonstdout); G.nonstdout = stdout; -- cgit v1.2.3-55-g6feb