stdbuf - Run COMMAND, with modified buffering operations for its
standard streams.
$ stdbuf -i0 -oL -e4567 ls /
Would use unbuffered input, line-buffered output and 4567 bytes buffer for
stderr.
Twiddling stdio buffers is done by LD_PRELOAD'ing a library -- libstdbuf.so
Our implementation can be used interchangably with the coreutils version
and vice versa. The coreutils libstdbuf.so with glibc weights in with:
text data bss dec hex filename
2622 624 8 3254 cb6
/usr/lib/x86_64-linux-gnu/coreutils/libstdbuf.so
in debian, fyi. Our version:
1313 448 0 1761 6e1 coreutils/libstdbuf.so.1.32.0.git
function old new delta
stdbuf_main - 212 +212
.rodata 183904 184096 +192
packed_usage 33147 33248 +101
stdbuf_longopts - 26 +26
applet_main 3160 3168 +8
applet_names 2721 2728 +7
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 4/0 up/down: 546/0) Total: 546 bytes
text data bss dec hex filename
939202 4240 1992 945434 e6d1a busybox_old
939618 4240 1992 945850 e6eba busybox_unstripped
for glibc
$ size coreutils/stdbuf.o coreutils/libstdbuf.o coreutils/libstdbuf.so
text data bss dec hex filename
313 0 0 313 139 coreutils/stdbuf.o
497 8 0 505 1f9 coreutils/libstdbuf.o
1563 480 0 2043 7fb coreutils/libstdbuf.so
for uClibc libstdbuf.o is around 400b
Signed-off-by: Bernhard Reutner-Fischer <[email protected]>
---
.gitignore | 1 +
Makefile.custom | 26 +++++++++
Makefile.flags | 2 +
coreutils/libstdbuf.c | 125 ++++++++++++++++++++++++++++++++++++++++++
coreutils/stdbuf.c | 115 ++++++++++++++++++++++++++++++++++++++
5 files changed, 269 insertions(+)
create mode 100644 coreutils/libstdbuf.c
create mode 100644 coreutils/stdbuf.c
diff --git a/.gitignore b/.gitignore
index becd9bf6d..47d4e7c64 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ Config.in
/busybox
/busybox_old
/busybox_unstripped*
+coreutils/libstdbuf.so*
#
# Backups / patches
diff --git a/Makefile.custom b/Makefile.custom
index 6f679c4e1..7599ee467 100644
--- a/Makefile.custom
+++ b/Makefile.custom
@@ -35,6 +35,28 @@ ifeq ($(CONFIG_FEATURE_INDIVIDUAL),y)
INSTALL_OPTS:= --binaries
LIBBUSYBOX_SONAME:= 0_lib/libbusybox.so.$(BB_VER)
endif
+# directory containing libbusybox.so or stdbuf LD_PRELOADed libstdbuf.so
+# FIXME: applets/install.sh uses plain CC but should use CC CFLAGS !
+#BUSYBOX_LIBDIR ?= $(dir $(abspath $(shell $(CC) $(CFLAGS)
-print-file-name=libc.so)))
+
+# start-of-install.sh-workarounds
+# FIXME: applets/install.sh should follow usage_compressed, I.e.:
+# applets/usage_compressed:test "$SED" || SED=sed
+SED ?= sed
+# FIXME: FORNOW: do what applets/install.sh wrongly does:
+BUSYBOX_LIBDIR:= $(shell $(CC) -print-file-name=libc.so | $(SED) -n
's%^.*\(/lib[^\/]*\)/libc.so%\1%p')
+ifeq ($(strip $(BUSYBOX_LIBDIR)),)
+BUSYBOX_LIBDIR:= /lib
+endif
+BUSYBOX_LIBDIR:= $(BUSYBOX_LIBDIR)/
+#end-of-install.sh-workarounds
+LIBSTDBUF_SONAME:=
+ifeq ($(CONFIG_LIBSTDBUF),y)
+LIBSTDBUF_SONAME:= libstdbuf.so
+DO_INSTALL_LIBS += coreutils/$(LIBSTDBUF_SONAME).$(BB_VER)
coreutils/$(LIBSTDBUF_SONAME)
+endif
+export BUSYBOX_LIBDIR LIBSTDBUF_SONAME
+
install: $(srctree)/applets/install.sh busybox busybox.links
$(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \
$(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS)
@@ -48,6 +70,10 @@ ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)
@echo --------------------------------------------------
@echo
endif
+ifeq ($(CONFIG_LIBSTDBUF),y)
+ @# Undo install.sh chmod 0644 "$prefix/$libdir/`basename $i`" || exit 1
+ @chmod 0755 $(CONFIG_PREFIX)/$(BUSYBOX_LIBDIR)$(LIBSTDBUF_SONAME)
+endif
install-noclobber: INSTALL_OPTS+=--noclobber
install-noclobber: install
diff --git a/Makefile.flags b/Makefile.flags
index 667481983..d943427de 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -108,6 +108,8 @@ ARCH_FPIC ?= -fpic
ARCH_FPIE ?= -fpie
ARCH_PIE ?= -pie
+export ARCH_FPIC ARCH_FPIE ARCH_PIE
+
# Usage: $(eval $(call pkg_check_modules,VARIABLE-PREFIX,MODULES))
define pkg_check_modules
$(1)_CFLAGS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags $(2))
diff --git a/coreutils/libstdbuf.c b/coreutils/libstdbuf.c
new file mode 100644
index 000000000..ba1bc92dc
--- /dev/null
+++ b/coreutils/libstdbuf.c
@@ -0,0 +1,125 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stdbuf - Run COMMAND, with modified buffering operations for its
+ * standard streams. libstdbuf.so LD_PRELOAD helper.
+ * Copyright (c) 2017-2020 Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+//config:config LIBSTDBUF
+//config: bool "libstdbuf (0.4 / 1.2 kb)"
+//config: default STDBUF
+//config: help
+//config: Build libstdbuf.so LD_PRELOAD helper library
+//config: If you disable this option the system libstdbuf.so is used
+//config: for the stdlib(1) applet.
+//config: Note that busybox' libstdbuf.so is compatible with the
+//config: corresponding library from coreutils, so can be used
+//config: interchangeably (and vice versa).
+//kbuild:extra-$(CONFIG_LIBSTDBUF) += libstdbuf.so
+//kbuild:# Add libstdbuf.o so it's .libstdbuf.o.cmd is read.
+//kbuild:# Otherwise we'd not see cmd_ and wrongly think the rule changed:
+//kbuild:extra-$(CONFIG_LIBSTDBUF) += libstdbuf.o
+//kbuild:clean-files += libstdbuf.so libstdbuf.so.*
+//kbuild:CFLAGS_libstdbuf.o:=$(ARCH_FPIC)
+//kbuild:LDFLAGS_libstdbuf.so.$(BB_VER):=$(ARCH_FPIC) -shared
-Wl,-soname=$(notdir $(LIBSTDBUF_SONAME)) -Wl,--warn-common
-Wl,--warn-shared-textrel -Wl,-Map,$(@).map # -Wl,-z,now -Wl,--enable-new-dtags
+//kbuild:coreutils/libstdbuf.so.$(BB_VER): coreutils/libstdbuf.o
+//kbuild: $(call if_changed,ld)
+//kbuild:coreutils/libstdbuf.so: coreutils/libstdbuf.so.$(BB_VER)
+//kbuild: @echo ' LINK $@' # FIXME {quiet,}cmd_ln
+//kbuild: $(Q)ln -sf $(<F) $@
+
+#include "platform.h"
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#if !defined likely && defined __GNUC__ && __GNUC__ >= 3
+# define likely(x) __builtin_expect((!!(x)),1)
+#else
+# define likely(x) (x)
+#endif
+#if !defined unlikely && defined __GNUC__ && __GNUC__ >= 3
+# define unlikely(x) __builtin_expect((!!(x)),0)
+#else
+# define unlikely(x) (x)
+#endif
+
+static void do_setvbuf(FILE *stream, const char* const file, const char *mode)
+{
+ int md;
+ size_t sz = 0;
+ char *buf = NULL;
+ const char errors[] ALIGN1 = "invalid\0could not set"
+#if defined __GLIBC__ && !defined(__UCLIBC__)
+ "\0OOM allocating"
+#endif
+ ;
+ unsigned char x = -1;
+#if 00 && defined(__GNUC__) && __GNUC__ > 8
+# if 1
+ const char files[] ALIGN1 = "stderr\0stdout\0stdin";
+ const char* const file = files + (2-(fileno(stream))) * 7;
+# else
+ const char files[] ALIGN1 = "stdin\0\0stdout\0stderr";
+ const char* const file = files + 7 * fileno(stream);
+# endif
+#else
+# if 00
+ const char* file;
+ switch (fileno(stream)) {
+ case 0: file = "stdin"; break;
+ case 1: file = "stdout"; break;
+ default: file = "stderr"; break;
+ }
+# endif
+#endif
+ if (mode == NULL)
+ return;
+ else if (*mode == '0')
+ md = _IONBF;
+ else if (*mode == 'L')
+ md = _IOLBF;
+ else {
+ char *invalid;
+ md = _IOFBF;
+ /* Note: We could call into libbusybox.so if available. */
+ errno = 0;
+#if defined SIZE_MAX && defined ULLONG_MAX && SIZE_MAX == ULLONG_MAX
+ sz = strtoull(mode, &invalid, 10);
+#else
+ sz = strtoul(mode, &invalid, 10);
+#endif
+ /* Note: if (sz > 0) might be sufficient, but be paranoid */
+ if (errno == 0 && *invalid == '\0' && sz > 0) {
+ /* BUG: glibc seem to ignore size if buf==NULL */
+ /* uClibc behaves properly, i guess musl does too */
+#if defined __GLIBC__ && !defined(__UCLIBC__)
+ buf = malloc (sz);
+ if (buf == NULL) {
+ x = 22; // OOM allocating
+ }
+#endif
+ } else {
+ x = 0; // invalid
+ }
+ }
+ if (likely(x == (unsigned char)-1)) {
+ if (likely(setvbuf(stream, buf, md, sz) == 0))
+ return;
+ x = 8; // could not set
+ }
+ fprintf(stderr, "%s buffering of %s to mode %s\n", errors + x, file,
mode);
+#if defined __GLIBC__ && !defined(__UCLIBC__)
+ /* IF_FEATURE_CLEAN_UP() */ free(buf);
+#endif
+}
+
+static void INIT_FUNC
+busybox_adjust_stdbuf(void)
+{
+ do_setvbuf(stderr, "stderr", getenv("_STDBUF_E"));
+ do_setvbuf(stdin, "stdin", getenv("_STDBUF_I"));
+ do_setvbuf(stdout, "stdout", getenv("_STDBUF_O"));
+}
diff --git a/coreutils/stdbuf.c b/coreutils/stdbuf.c
new file mode 100644
index 000000000..c5f134b21
--- /dev/null
+++ b/coreutils/stdbuf.c
@@ -0,0 +1,115 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stdbuf - Run COMMAND, with modified buffering operations for its
+ * standard streams
+ * Copyright (c) 2017-2020 Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+//config:config STDBUF
+//config: bool "stdbuf (0.3 kb)"
+//config: default y
+//config: help
+//config: Run COMMAND, with modified buffering operations for its
+//config: standard streams.
+//config: Note: Your libc has to support LD_PRELOAD for this to work.
+//config:config FEATURE_STDBUF_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on STDBUF && LONG_OPTS
+//applet:IF_STDBUF(APPLET_NOEXEC(stdbuf, stdbuf, BB_DIR_USR_BIN, BB_SUID_DROP,
stdbuf))
+//kbuild:lib-$(CONFIG_STDBUF) += stdbuf.o
+//kbuild:CFLAGS_stdbuf.o:=-DLIBSTDBUF_SONAME=$(squote)$(quote)$(BUSYBOX_LIBDIR)$(notdir
$(LIBSTDBUF_SONAME))$(quote)$(squote)
+//usage:#define stdbuf_trivial_usage
+//usage: "OPTION... COMMAND"
+//usage:#define stdbuf_full_usage "\n\n"
+//usage: "Run COMMAND, with modified buffering operations for its
standard streams\n"
+//usage: "\n -i"
+//usage: IF_FEATURE_STDBUF_LONG_OPTIONS(",--input=MODE")
+//usage: " adjust standard input stream buffering"
+//usage: "\n -o"
+//usage: IF_FEATURE_STDBUF_LONG_OPTIONS(",--output=MODE")
+//usage: " adjust standard output stream buffering"
+//usage: "\n -e"
+//usage: IF_FEATURE_STDBUF_LONG_OPTIONS(",--error=MODE")
+//usage: " adjust standard error stream buffering"
+//usage: "\n\nMODE:"
+//usage: "\n\tL: line buffer (not for input)"
+//usage: "\n\t0: unbuffered"
+//usage: "\n\tNUMBER: of buffer bytes"
+//usage:#define stdbuf_example_usage
+//usage: "$ stdbuf -i0 -oL tail -f /x.log\n"
+
+#include "libbb.h"
+//#include "common_bufsiz.h"
+
+/* LIBSTDBUF_SONAME is supposedly something like:
+ * $(dirname $(readlink -f $(cc $CFLAGS
-print-file-name=libc.so)))/libstdbuf.so
+ */
+#ifndef LIBSTDBUF_SONAME
+/* Make sure this matches applets/install.sh fallback libdir ! */
+# define LIBSTDBUF_SONAME "/lib/libstdbuf.so"
+#endif
+
+#if !defined likely && defined __GNUC__ && __GNUC__ >= 3
+# define likely(x) __builtin_expect((!!(x)),1)
+#else
+# define likely(x) (x)
+#endif
+#if !defined unlikely && defined __GNUC__ && __GNUC__ >= 3
+# define unlikely(x) __builtin_expect((!!(x)),0)
+#else
+# define unlikely(x) (x)
+#endif
+
+#if ENABLE_FEATURE_STDBUF_LONG_OPTIONS
+#define getopt32 getopt32long
+static const char stdbuf_longopts[] ALIGN1 =
+ "input\0" Required_argument "i"
+ "output\0" Required_argument "o"
+ "error\0" Required_argument "e"
+;
+#endif
+
+int stdbuf_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int stdbuf_main(int argc UNUSED_PARAM, char **argv)
+{
+ const char * const LD_PRELOAD = "LD_PRELOAD";
+ char *old_env, *new_env = (char*) LIBSTDBUF_SONAME;
+ char STDBUF[] ALIGN1 = "_STDBUF_I";
+ unsigned i = 0;
+ unsigned char opt;
+ char *streams[3]; /* input, output, error */
+
+ opt = getopt32(argv, IF_FEATURE_STDBUF_LONG_OPTIONS("^")
+ "+" /* stop argument processing on first non-option arg
*/
+ "i:o:e:" "\0"
+ "-1" /* at least one non-option arg is required */
+ "i:o:e:" /* at least one of these args is required */
+ IF_FEATURE_STDBUF_LONG_OPTIONS(, stdbuf_longopts)
+ , &streams[0], &streams[1], &streams[2]);
+ for (; i < 3; i++) {
+ const char *value = "L";
+ if (! (opt & (1 << i))) /* skip OPT_i, OPT_o, OPT_e if not set
*/
+ continue;
+ if (i != 0)
+ STDBUF[sizeof("_STDBUF_I") - 2] = i == 1 ? 'O' : 'E';
+ /* Reject "L" for input */
+ if (i == 0 || *(streams[i]) != 'L') {
+ /* Note that the library takes just numbers, without
suffix. */
+ value = xasprintf("%llu", xatoull_sfx(streams[i],
kmg_i_suffixes));
+ }
+ xsetenv(STDBUF, value);
+ /* Not worth freeing value */
+// if (ENABLE_FEATURE_CLEAN_UP && *value != 'L')
+// free(value);
+ }
+ old_env = getenv(LD_PRELOAD);
+ if (unlikely(old_env != NULL))
+ new_env = xasprintf("%s:%s", new_env, old_env);
+ xsetenv(LD_PRELOAD, new_env);
+ /* Not worth freeing new_env */
+// if (ENABLE_FEATURE_CLEAN_UP && old_env)
+// free(new_env);
+ BB_EXECVP_or_die(argv + optind);
+}
--
2.35.2
_______________________________________________
busybox mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/busybox