From 3994d5a52b8fa70637bffe4173f165267e6169fb Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 22 Oct 2018 09:12:07 +0100 Subject: Allow shell scripts to be embedded in the binary To assist in the deployment of shell scripts it may be convenient to embed them in the BusyBox binary. This patch adds two configuration options to the shell: - 'Embed scripts in the binary' takes any files in the directory 'embed', concatenates them with null separators, compresses them and embeds them in the binary. - 'Allow the contents of embedded scripts to be listed' makes the shell argument '-L name' list the contents of the named script. Both options are off by default. When scripts are embedded in the binary: - The shell argument '-L' lists the names of the scripts. - Scripts can be run as 'sh name arg...'. - An alias is added for each script, equivalent to "alias name='sh name'". --- .gitignore | 5 ++ archival/libarchive/Kbuild.src | 1 + include/.gitignore | 1 + shell/Kbuild.src | 10 +++ shell/ash.c | 140 ++++++++++++++++++++++++++++++++++++++++- shell/embedded_scripts | 62 ++++++++++++++++++ 6 files changed, 218 insertions(+), 1 deletion(-) create mode 100755 shell/embedded_scripts diff --git a/.gitignore b/.gitignore index 517e750c1..cc485189e 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,8 @@ cscope.po.out # tags TAGS + +# +# user-supplied scripts +# +/embed diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src index e1a8a7529..12e66a88b 100644 --- a/archival/libarchive/Kbuild.src +++ b/archival/libarchive/Kbuild.src @@ -91,6 +91,7 @@ lib-$(CONFIG_FEATURE_SEAMLESS_LZMA) += open_transformer.o decompress_unlzma. lib-$(CONFIG_FEATURE_SEAMLESS_XZ) += open_transformer.o decompress_unxz.o lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += open_transformer.o decompress_bunzip2.o lib-$(CONFIG_FEATURE_COMPRESS_BBCONFIG) += open_transformer.o decompress_bunzip2.o +lib-$(CONFIG_ASH_EMBEDDED_SCRIPTS) += open_transformer.o decompress_bunzip2.o ifneq ($(lib-y),) lib-y += $(COMMON_FILES) diff --git a/include/.gitignore b/include/.gitignore index 75afff9ca..13a96e018 100644 --- a/include/.gitignore +++ b/include/.gitignore @@ -5,6 +5,7 @@ /autoconf.h /bbconfigopts_bz2.h /bbconfigopts.h +/embedded_scripts.h /NUM_APPLETS.h /usage_compressed.h /usage.h diff --git a/shell/Kbuild.src b/shell/Kbuild.src index a287fce4e..34dd93d48 100644 --- a/shell/Kbuild.src +++ b/shell/Kbuild.src @@ -10,3 +10,13 @@ INSERT lib-$(CONFIG_FEATURE_SH_MATH) += math.o lib-$(CONFIG_FEATURE_PRNG_SHELL) += random.o + +quiet_cmd_gen_embedded_scripts = GEN include/embedded_scripts.h + cmd_gen_embedded_scripts = $(srctree_slash)shell/embedded_scripts include/embedded_scripts.h embed + +ifdef CONFIG_ASH_EMBEDDED_SCRIPTS +shell/ash.o: include/embedded_scripts.h +endif + +include/embedded_scripts.h: $(wildcard embed/*) shell/embedded_scripts + $(call cmd,gen_embedded_scripts) diff --git a/shell/ash.c b/shell/ash.c index 7131609e4..9d7ef032c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -175,6 +175,25 @@ //config: from a GUI application. Disable this if your platform doesn't //config: support the required APIs. //config: +//config:config ASH_EMBEDDED_SCRIPTS +//config: bool "Embed scripts in the binary" +//config: default n +//config: depends on ASH || SH_IS_ASH || BASH_IS_ASH +//config: help +//config: Allow scripts to be compressed and embedded in the BusyBox +//config: binary. The scripts should be placed in the 'embed' directory. +//config: Such scripts can only be run by giving their name as an +//config: argument to the shell, though aliases are set up to do this. +//config: The names of the scripts are listed by the '-L' shell argument. +//config: +//config:config ASH_LIST_EMBEDDED_SCRIPTS +//config: bool "Allow the contents of embedded scripts to be listed" +//config: default n +//config: depends on ASH_EMBEDDED_SCRIPTS +//config: help +//config: Allow the contents of embedded script to be listed using +//config: the '-L name' shell argument. +//config: //config:endif # ash options //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) @@ -209,6 +228,16 @@ #include /* for setting $HOSTNAME */ #include "busybox.h" /* for applet_names */ +#if ENABLE_ASH_EMBEDDED_SCRIPTS +#include "embedded_scripts.h" +#else +#define NUM_SCRIPTS 0 +#endif + +#if NUM_SCRIPTS +static const char packed_scripts[] ALIGN1 = { PACKED_SCRIPTS }; +#endif + /* So far, all bash compat is controlled by one config option */ /* Separate defines document which part of code implements what */ /* function keyword */ @@ -11490,6 +11519,76 @@ setparam(char **argv) #endif } +#if NUM_SCRIPTS +#define BB_ARCHIVE_PUBLIC +# include "bb_archive.h" +static char *unpack_scripts(void) +{ + char *outbuf = NULL; + bunzip_data *bd; + int i; + jmp_buf jmpbuf; + + /* Setup for I/O error handling via longjmp */ + i = setjmp(jmpbuf); + if (i == 0) { + i = start_bunzip(&jmpbuf, + &bd, + /* src_fd: */ -1, + /* inbuf: */ packed_scripts, + /* len: */ sizeof(packed_scripts) + ); + } + /* read_bunzip can longjmp and end up here with i != 0 + * on read data errors! Not trivial */ + if (i == 0) { + /* Cannot use xmalloc: will leak bd in NOFORK case! */ + outbuf = malloc_or_warn(UNPACKED_SCRIPTS_LENGTH); + if (outbuf) + read_bunzip(bd, outbuf, UNPACKED_SCRIPTS_LENGTH); + } + dealloc_bunzip(bd); + return outbuf; +} + +static char * +check_embedded(const char *arg) +{ + int i; + char *scripts; + const char *s; + + i = 0; + for (s=script_names; *s; s+=strlen(s)+1) { + if (strcmp(arg, s) == 0) { + break; + } + ++i; + } + + if (i != NUM_SCRIPTS && (scripts=unpack_scripts()) != NULL) { + char *t = scripts; + while (i != 0) { + t += strlen(t) + 1; + --i; + } + return t; + } + + return NULL; +} + +static void +alias_embedded(void) +{ + const char *s; + + for (s=script_names; *s; s+=strlen(s)+1) { + setalias(s, auto_string(xasprintf("sh %s", s))); + } +} +#endif + /* * Process shell options. The global variable argptr contains a pointer * to the argument list; we advance it past the options. @@ -11600,6 +11699,26 @@ options(int cmdline, int *login_sh) *login_sh = 1; } break; +#if NUM_SCRIPTS + } else if (cmdline && (c == 'L')) { +#if ENABLE_ASH_LIST_EMBEDDED_SCRIPTS + if (*argptr) { + char *script; + + if ((script=check_embedded(*argptr)) != NULL) { + printf(script); + } + } else +#endif + { + const char *s; + + for (s=script_names; *s; s+=strlen(s)+1) { + printf("%s\n", s); + } + } + exit(0); +#endif } else { setoption(c, val); } @@ -14643,9 +14762,17 @@ init(void) } } - //usage:#define ash_trivial_usage //usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]" +//usage: IF_ASH_EMBEDDED_SCRIPTS( +//usage: " [-L" +//usage: ) +//usage: IF_ASH_LIST_EMBEDDED_SCRIPTS( +//usage: " [name]" +//usage: ) +//usage: IF_ASH_EMBEDDED_SCRIPTS( +//usage: "]" +//usage: ) //usage:#define ash_full_usage "\n\n" //usage: "Unix shell interpreter" @@ -14688,6 +14815,9 @@ procargs(char **argv) optlist[i] = 0; #if DEBUG == 2 debug = 1; +#endif +#if NUM_SCRIPTS + alias_embedded(); #endif /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */ if (xminusc) { @@ -14695,6 +14825,14 @@ procargs(char **argv) if (*xargv) goto setarg0; } else if (!sflag) { +#if NUM_SCRIPTS + char *script; + if ((script=check_embedded(*xargv)) != NULL) { + setinputstring(script); + xflag = 0; + goto setarg0; + } +#endif setinputfile(*xargv, 0); #if ENABLE_PLATFORM_MINGW32 convert_slashes(*xargv); diff --git a/shell/embedded_scripts b/shell/embedded_scripts new file mode 100755 index 000000000..0ca89b91e --- /dev/null +++ b/shell/embedded_scripts @@ -0,0 +1,62 @@ +#!/bin/sh + +target="$1" +loc="$2" + +test "$target" || exit 1 +test "$SED" || SED=sed +test "$DD" || DD=dd + +# Some people were bitten by their system lacking a (proper) od +od -v -b /dev/null +if test $? != 0; then + echo 'od tool is not installed or cannot accept "-v -b" options' + exit 1 +fi + +exec >"$target.$$" + +scripts="" +if [ -d "$loc" ] +then + scripts=$(cd $loc; ls * 2>/dev/null) +fi + +n=$(echo $scripts | wc -w) + +if [ $n -ne 0 ] +then + printf 'static const char script_names[] ALIGN1 = ' + for i in $scripts + do + printf '"%s\\0"' $i + done + printf '"\\0";\n' +fi +printf "#define NUM_SCRIPTS $n\n\n" + +if [ $n -ne 0 ] +then + printf '#define UNPACKED_SCRIPTS_LENGTH ' + for i in $scripts + do + cat $loc/$i + printf '\000' + done | wc -c + + printf '#define PACKED_SCRIPTS \\\n' + for i in $scripts + do + cat $loc/$i + printf '\000' + done | bzip2 -1 | $DD bs=2 skip=1 2>/dev/null | od -v -b \ + | grep -v '^ ' \ + | $SED -e 's/^[^ ]*//' \ + -e 's/ //g' \ + -e '/^$/d' \ + -e 's/\(...\)/0\1,/g' \ + -e 's/$/ \\/' + printf '\n' +fi + +mv -- "$target.$$" "$target" -- cgit v1.2.3-55-g6feb