[prev in list] [next in list] [prev in thread] [next in thread]
List: busybox
Subject: [RFC PATCH 1/1] ash: allow shell scripts to be embedded in the binary
From: Ron Yorston <rmy () pobox ! com>
Date: 2018-10-25 11:28:46
Message-ID: 5bd1a8ee.fj9g2+EE/qvwmBAE%rmy () pobox ! com
[Download RAW message or body]
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
and allows the scripts to be traced.
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...' or 'busybox name arg...'.
- In standalone shell mode scripts can be run by name and are
subject to tab completion.
Signed-off-by: Ron Yorston <rmy@pobox.com>
---
.gitignore | 5 +
Makefile | 6 +-
archival/libarchive/Kbuild.src | 1 +
include/.gitignore | 1 +
libbb/appletlib.c | 25 ++++-
libbb/lineedit.c | 18 ++-
scripts/embedded_scripts | 68 ++++++++++++
shell/ash.c | 196 ++++++++++++++++++++++++++++++++-
8 files changed, 313 insertions(+), 7 deletions(-)
create mode 100755 scripts/embedded_scripts
diff --git a/.gitignore b/.gitignore
index c03c2e8a6..becd9bf6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,3 +56,8 @@ cscope.po.out
#
tags
TAGS
+
+#
+# user-supplied scripts
+#
+/embed
diff --git a/Makefile b/Makefile
index 59ec83a6a..8a0dbdf49 100644
--- a/Makefile
+++ b/Makefile
@@ -850,11 +850,14 @@ quiet_cmd_gen_common_bufsiz = GEN include/common_bufsiz.h
cmd_gen_common_bufsiz = $(srctree)/scripts/generate_BUFSIZ.sh \
include/common_bufsiz.h quiet_cmd_split_autoconf = SPLIT include/autoconf.h -> \
include/config/*
cmd_split_autoconf = scripts/basic/split-include include/autoconf.h \
include/config +quiet_cmd_gen_embedded_scripts = GEN include/embedded_scripts.h
+ cmd_gen_embedded_scripts = scripts/embedded_scripts include/embedded_scripts.h \
embed #bbox# piggybacked generation of few .h files
-include/config/MARKER: scripts/basic/split-include include/autoconf.h
+include/config/MARKER: scripts/basic/split-include include/autoconf.h $(wildcard \
embed/*) scripts/embedded_scripts $(call cmd,split_autoconf)
$(call cmd,gen_bbconfigopts)
$(call cmd,gen_common_bufsiz)
+ $(call cmd,gen_embedded_scripts)
@touch $@
# Generate some files
@@ -974,6 +977,7 @@ MRPROPER_FILES += .config .config.old include/asm .version \
.old_version \ include/autoconf.h \
include/bbconfigopts.h \
include/bbconfigopts_bz2.h \
+ include/embedded_scripts.h \
include/usage_compressed.h \
include/applet_tables.h \
include/applets.h \
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/libbb/appletlib.c b/libbb/appletlib.c
index 319bcc263..3f15f663b 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -49,7 +49,11 @@
#endif
#include "usage_compressed.h"
-
+#if ENABLE_ASH_EMBEDDED_SCRIPTS
+#include "embedded_scripts.h"
+#else
+#define NUM_SCRIPTS 0
+#endif
/* "Do not compress usage text if uncompressed text is small
* and we don't include bunzip2 code for other reasons"
@@ -953,7 +957,7 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, const char \
*name, char **ar }
# endif /* NUM_APPLETS > 0 */
-# if ENABLE_BUSYBOX || NUM_APPLETS > 0
+# if ENABLE_BUSYBOX || NUM_APPLETS > 0 || NUM_SCRIPTS > 0
static NORETURN void run_applet_and_exit(const char *name, char **argv)
{
# if ENABLE_BUSYBOX
@@ -968,6 +972,23 @@ static NORETURN void run_applet_and_exit(const char *name, char \
**argv) run_applet_no_and_exit(applet, name, argv);
}
# endif
+# if NUM_SCRIPTS > 0
+ {
+ int script = find_script_by_name(name IF_FEATURE_SH_STANDALONE(, 0));
+ if (script >= 0) {
+ int i, argc = string_array_len(argv);
+ char **new_argv = xmalloc(sizeof(*argv)*(argc+2));
+
+ new_argv[0] = (char *)"ash";
+ new_argv[1] = (char *)name;
+ for (i=1; i<argc+1; ++i) {
+ new_argv[i+1] = argv[i];
+ }
+
+ run_applet_no_and_exit(APPLET_NO_ash, name, new_argv);
+ }
+ }
+# endif
/*bb_error_msg_and_die("applet not found"); - links in printf */
full_write2_str(applet_name);
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index b1e971f88..d21d8e616 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -41,6 +41,12 @@
#include "busybox.h"
#include "NUM_APPLETS.h"
#include "unicode.h"
+#if ENABLE_ASH_EMBEDDED_SCRIPTS
+#include "embedded_scripts.h"
+#else
+#define NUM_SCRIPTS 0
+#endif
+
#ifndef _POSIX_VDISABLE
# define _POSIX_VDISABLE '\0'
#endif
@@ -806,10 +812,16 @@ static NOINLINE unsigned complete_cmd_dir_file(const char \
*command, int type) }
pf_len = strlen(pfind);
-# if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1
+# if ENABLE_FEATURE_SH_STANDALONE && (NUM_APPLETS != 1 || NUM_SCRIPTS > 0)
if (type == FIND_EXE_ONLY && !dirbuf) {
- const char *p = applet_names;
-
+ const char *p;
+# if NUM_APPLETS != 1 && NUM_SCRIPTS > 0
+ for (i=0,p=applet_names; i<2; i++,p=script_names)
+# elif NUM_APPLETS != 1
+ p = applet_names;
+# else /* NUM_SCRIPTS > 0 */
+ p = script_names;
+# endif
while (*p) {
if (strncmp(pfind, p, pf_len) == 0)
add_match(xstrdup(p));
diff --git a/scripts/embedded_scripts b/scripts/embedded_scripts
new file mode 100755
index 000000000..7d5e80b6f
--- /dev/null
+++ b/scripts/embedded_scripts
@@ -0,0 +1,68 @@
+#!/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 >/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 '#ifdef ASH_MAIN\n'
+ printf 'const char script_names[] ALIGN1 = '
+ for i in $scripts
+ do
+ printf '"%s\\0"' $i
+ done
+ printf '"\\0";\n'
+ printf '#else\n'
+ printf 'extern const char script_names[] ALIGN1;\n'
+ printf '#endif\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'
+ printf 'int find_script_by_name(const char *arg\n'
+ printf '\t\tIF_FEATURE_SH_STANDALONE(, int offset)) FAST_FUNC;\n'
+fi
+
+mv -- "$target.$$" "$target"
diff --git a/shell/ash.c b/shell/ash.c
index dc1a55a6b..a27262ab5 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -148,6 +148,31 @@
//config: you to run the specified command or builtin,
//config: even when there is a function with the same name.
//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: at build time. In standalone shell mode such scripts can be
+//config: run directly and are subject to tab completion; otherwise they
+//config: can be run by giving their name as an argument to the shell.
+//config: For convenience shell aliases are created. The '-L' shell
+//config: argument lists the names of the scripts. Like applets scripts
+//config: can be run as 'busybox name ...' or by linking their name to
+//config: the binary.
+//config:
+//config:config ASH_LIST_EMBEDDED_SCRIPTS
+//config: bool "Allow embedded script content to be visible"
+//config: default n
+//config: depends on ASH_EMBEDDED_SCRIPTS
+//config: help
+//config: Allow the contents of embedded scripts to be listed by the
+//config: '-L name' shell argument. Also allow embedded scripts to be
+//config: traced by the 'xtrace' shell option. Select this if you don't
+//config: mind users seeing what's in your scripts.
+//config:
//config:endif # ash options
//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
@@ -181,6 +206,16 @@
#include <sys/times.h>
#include <sys/utsname.h> /* for setting $HOSTNAME */
#include "busybox.h" /* for applet_names */
+#if ENABLE_ASH_EMBEDDED_SCRIPTS
+#define ASH_MAIN
+#include "embedded_scripts.h"
+#else
+#define NUM_SCRIPTS 0
+#endif
+
+#if NUM_SCRIPTS > 0
+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 */
@@ -8001,6 +8036,13 @@ static void
tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char \
**envp) {
#if ENABLE_FEATURE_SH_STANDALONE
+# if NUM_SCRIPTS > 0
+ if (applet_no >= NUM_APPLETS) {
+ errno = ENOEXEC;
+ goto run_script;
+ }
+ else
+# endif
if (applet_no >= 0) {
if (APPLET_IS_NOEXEC(applet_no)) {
clearenv();
@@ -8023,6 +8065,9 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char \
*cmd, char **argv, c } while (errno == EINTR);
#else
execve(cmd, argv, envp);
+#endif
+#if ENABLE_FEATURE_SH_STANDALONE && NUM_SCRIPTS > 0
+ run_script:
#endif
if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
/* Run "cmd" as a shell script:
@@ -8052,6 +8097,96 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char \
*cmd, char **argv, c }
}
+#if NUM_SCRIPTS > 0
+# 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;
+}
+
+/*
+ * In standalone shell mode we sometimes want the index of the script
+ * and sometimes the index offset by NUM_APPLETS.
+ */
+int FAST_FUNC
+find_script_by_name(const char *arg IF_FEATURE_SH_STANDALONE(, int offset))
+{
+ const char *s = script_names;
+ int i = 0;
+
+ while (*s) {
+ if (strcmp(arg, s) == 0) {
+ return IF_FEATURE_SH_STANDALONE(offset+)i;
+ }
+ ++i;
+ while (*s++ != '\0')
+ continue;
+ }
+
+ return -1;
+}
+
+static char *
+get_script_content(const char *arg)
+{
+ int i;
+ char *scripts;
+
+ if ((i=find_script_by_name(arg IF_FEATURE_SH_STANDALONE(, 0))) < 0)
+ return NULL;
+
+ if ((scripts=unpack_scripts()) != NULL) {
+ char *t = scripts;
+ while (i != 0) {
+ while (*t++ != '\0')
+ continue;
+ --i;
+ }
+ return t;
+ }
+
+ return NULL;
+}
+
+#if !ENABLE_FEATURE_SH_STANDALONE
+static void
+alias_scripts(void)
+{
+ const char *s = script_names;
+
+ while (*s) {
+ setalias(s, auto_string(xasprintf("sh %s", s)));
+ while (*s++ != '\0')
+ continue;
+ }
+}
+# endif
+#endif
+
/*
* Exec a program. Never returns. If you change this routine, you may
* have to change the find_command routine as well.
@@ -8070,6 +8205,9 @@ static void shellexec(char *prog, char **argv, const char \
*path, int idx) if (strchr(prog, '/') != NULL
#if ENABLE_FEATURE_SH_STANDALONE
|| (applet_no = find_applet_by_name(prog)) >= 0
+# if NUM_SCRIPTS > 0
+ || (applet_no = find_script_by_name(prog, NUM_APPLETS)) >= 0
+# endif
#endif
) {
tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
@@ -10171,6 +10309,10 @@ evalcommand(union node *cmd, int flags)
*/
/* find_command() encodes applet_no as (-2 - applet_no) */
int applet_no = (- cmdentry.u.index - 2);
+#if NUM_SCRIPTS > 0
+ /* ignore embedded scripts */
+ if (applet_no < NUM_APPLETS)
+#endif
if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
char **sv_environ;
@@ -11083,6 +11225,30 @@ options(int cmdline, int *login_sh)
*login_sh = 1;
}
break;
+#if NUM_SCRIPTS > 0
+ } else if (cmdline && (c == 'L')) {
+#if ENABLE_ASH_LIST_EMBEDDED_SCRIPTS
+ /* list contents of an embedded script */
+ if (*argptr) {
+ char *script;
+
+ if ((script=get_script_content(*argptr)) != NULL) {
+ full_write1_str(script);
+ }
+ } else
+#endif
+ /* list names of all embedded scripts */
+ {
+ const char *s = script_names;
+
+ while (*s) {
+ printf("%s\n", s);
+ while (*s++ != '\0')
+ continue;
+ }
+ }
+ exit(0);
+#endif
} else {
setoption(c, val);
}
@@ -13353,6 +13519,11 @@ find_command(char *name, struct cmdentry *entry, int act, \
const char *path) #if ENABLE_FEATURE_SH_STANDALONE
{
int applet_no = find_applet_by_name(name);
+# if NUM_SCRIPTS > 0
+ /* embedded script indices are offset by NUM_APPLETS */
+ if (applet_no < 0)
+ applet_no = find_script_by_name(name, NUM_APPLETS);
+# endif
if (applet_no >= 0) {
entry->cmdtype = CMDNORMAL;
entry->u.index = -2 - applet_no;
@@ -14003,9 +14174,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"
@@ -14048,6 +14227,9 @@ procargs(char **argv)
optlist[i] = 0;
#if DEBUG == 2
debug = 1;
+#endif
+#if NUM_SCRIPTS > 0 && !ENABLE_FEATURE_SH_STANDALONE
+ alias_scripts();
#endif
/* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */
if (xminusc) {
@@ -14055,6 +14237,18 @@ procargs(char **argv)
if (*xargv)
goto setarg0;
} else if (!sflag) {
+#if NUM_SCRIPTS > 0
+ /* check for an embedded script */
+ char *script;
+ if ((script=get_script_content(*xargv)) != NULL) {
+ setinputstring(script);
+#if !ENABLE_ASH_LIST_EMBEDDED_SCRIPTS
+ /* prevent tracing */
+ xflag = 0;
+#endif
+ goto setarg0;
+ }
+#endif
setinputfile(*xargv, 0);
setarg0:
arg0 = *xargv++;
--
2.17.2
_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic