commit c9e91a9
xplshn
·
2026-07-02 15:33:38 +0000 UTC
parent c54d5a0
sh: handle CTRL+C,S,Q,Z properly Signed-off-by: xplshn <anto@xplshn.com.ar>
14 files changed,
+940,
-560
M
Makefile
+20,
-38
1@@ -519,7 +519,7 @@ $(OBJ) $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_
2 $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS)
3
4 cmd/posix/bc.c: cmd/posix/bc.y
5- $(YACC) -d -o $@ cmd/posix/bc.y
6+ YACC='$(YACC)' sh scripts/genconfig.sh bc
7
8 cmd/posix/bc: cmd/posix/bc.c
9 $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ cmd/posix/bc.c $(LIB) $(LDLIBS)
10@@ -547,7 +547,7 @@ shared/libutil/libutil.a: $(LIBUTILOBJ)
11 cmd/posix/getconf: cmd/posix/getconf.h
12
13 cmd/posix/getconf.h:
14- scripts/getconf.sh > $@ || { rm -f $@; exit 1; }
15+ sh scripts/genconfig.sh getconf
16
17 box: $(LIB)
18 CC='$(CC)' CPPFLAGS='$(CPPFLAGS)' CFLAGS='$(CFLAGS)' \
19@@ -623,19 +623,13 @@ clean:
20 @rm -f $(LIB)
21 @printf " CLEAN compiled binaries\n"
22 @rm -f $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL)
23- @printf " CLEAN generated headers\n"
24- @rm -f cmd/posix/getconf.h cmd/posix/bc.c cmd/posix/bc.h
25- @rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h
26- @rm -f cmd/posix/sh/token.h cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h
27- @rm -f cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h
28- @printf " CLEAN generated sources\n"
29- @rm -f cmd/posix/awk/proctab.c
30+ @sh scripts/genconfig.sh clean
31 @printf " CLEAN build tools\n"
32- @rm -f cmd/posix/awk/maketab cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
33 @rm -f scripts/mkman/mkman
34 @rm -f scripts/mk/config.kv
35 @rm -f scripts/mk/config.mk
36 @rm -f scripts/mk/rules.mk
37+
38 @printf " CLEAN dev artifacts\n"
39 @rm -f cmd/dev/cc/cc1 cmd/dev/cc/cpp cmd/dev/as/as cmd/dev/ld/ld cmd/dev/ar/ar shared/libaruuelf.so
40 @rm -f cmd/dev/config.h cmd/dev/cc/config.h cmd/dev/version.h
41@@ -710,41 +704,29 @@ YAPOBJ =\
42 cmd/extra/yap/prompt.o\
43 cmd/extra/yap/term.o
44
45-cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h: cmd/posix/awk/awkgram.y
46- $(YACC) -d -o cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.y
47- @if [ ! -f cmd/posix/awk/awkgram.tab.h ]; then \
48- if [ -f y.tab.h ]; then mv y.tab.h cmd/posix/awk/awkgram.tab.h; \
49- elif [ -f cmd/posix/awk/y.tab.h ]; then mv cmd/posix/awk/y.tab.h cmd/posix/awk/awkgram.tab.h; fi; \
50- fi
51-
52-cmd/posix/awk/maketab: cmd/posix/awk/maketab.c cmd/posix/awk/awkgram.tab.h
53- $(CC) $(CFLAGS) -o $@ cmd/posix/awk/maketab.c
54+cmd/posix/awk/awkgram.tab.c: cmd/posix/awk/awkgram.y cmd/posix/awk/maketab.c
55+ CC='$(CC)' CFLAGS='$(CFLAGS)' YACC='$(YACC)' sh scripts/genconfig.sh awk
56
57-cmd/posix/awk/proctab.c: cmd/posix/awk/maketab
58- cmd/posix/awk/maketab cmd/posix/awk/awkgram.tab.h > $@
59+cmd/posix/awk/awkgram.tab.h: cmd/posix/awk/awkgram.tab.c ;
60+cmd/posix/awk/maketab: cmd/posix/awk/awkgram.tab.c ;
61+cmd/posix/awk/proctab.c: cmd/posix/awk/awkgram.tab.c ;
62
63 $(AWKOBJ): cmd/posix/awk/awk.h cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proto.h
64
65 cmd/posix/awk/awk: $(AWKOBJ) $(LIB)
66 $(CC) $(LDFLAGS) -o $@ $(AWKOBJ) $(LIB) $(LDLIBS) -lm
67
68-cmd/posix/sh/mknodes: cmd/posix/sh/mknodes.c
69- $(CC) $(CFLAGS) -o $@ cmd/posix/sh/mknodes.c
70-
71-cmd/posix/sh/mksyntax: cmd/posix/sh/mksyntax.c
72- $(CC) $(CPPFLAGS) -Icmd/posix/sh $(CFLAGS) -o $@ cmd/posix/sh/mksyntax.c
73-
74-cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h: cmd/posix/sh/mksyntax
75- cd cmd/posix/sh && ./mksyntax
76-
77-cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h: cmd/posix/sh/mknodes cmd/posix/sh/nodetypes cmd/posix/sh/nodes.c.pat
78- cd cmd/posix/sh && ./mknodes nodetypes nodes.c.pat
79-
80-cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h: cmd/posix/sh/mkbuiltins cmd/posix/sh/builtins.def cmd/posix/sh/shell.h scripts/mk/config.kv
81- cd cmd/posix/sh && sh mkbuiltins .
82-
83-cmd/posix/sh/token.h: cmd/posix/sh/mktokens
84- cd cmd/posix/sh && sh mktokens
85+cmd/posix/sh/builtins.c: cmd/posix/sh/mknodes.c cmd/posix/sh/mksyntax.c cmd/posix/sh/nodetypes cmd/posix/sh/nodes.c.pat cmd/posix/sh/mkbuiltins cmd/posix/sh/builtins.def cmd/posix/sh/shell.h cmd/posix/sh/mktokens
86+ CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' sh scripts/genconfig.sh sh
87+
88+cmd/posix/sh/syntax.c: cmd/posix/sh/builtins.c ;
89+cmd/posix/sh/syntax.h: cmd/posix/sh/builtins.c ;
90+cmd/posix/sh/nodes.c: cmd/posix/sh/builtins.c ;
91+cmd/posix/sh/nodes.h: cmd/posix/sh/builtins.c ;
92+cmd/posix/sh/builtins.h: cmd/posix/sh/builtins.c ;
93+cmd/posix/sh/token.h: cmd/posix/sh/builtins.c ;
94+cmd/posix/sh/mknodes: cmd/posix/sh/builtins.c ;
95+cmd/posix/sh/mksyntax: cmd/posix/sh/builtins.c ;
96
97 $(SHOBJ): $(SH_GENHDRS)
98
+47,
-164
1@@ -25,14 +25,39 @@ while [ $# -gt 0 ]; do
2 shift
3 done
4
5+# timestamp comparison
6+#
7+mtime() {
8+ stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0
9+}
10+newer_than() {
11+ local s t
12+ [ -e "$2" ] || return 0
13+ s=$(mtime "$1"); t=$(mtime "$2")
14+ [ "$s" -gt "$t" ]
15+}
16+any_newer_than() {
17+ local target="$1"; shift
18+ for src do
19+ newer_than "$src" "$target" && return 0
20+ done
21+ return 1
22+}
23+
24 [ -f build.cfg ] || { printf 'build.cfg not found; see the repository README\n' >&2; exit 1; }
25+
26+# generate config files if missing or build.cfg is newer
27+if [ ! -f scripts/mk/config.kv ] || newer_than build.cfg scripts/mk/config.kv; then
28+ sh scripts/genconfig.sh
29+fi
30+
31 # shellcheck disable=SC1091
32 . ./build.cfg
33
34 # load config.kv if it exists to override and provide detected TLS settings
35 if [ -f scripts/mk/config.kv ]; then
36 # shellcheck disable=SC1091
37- . scripts/mk/config.kv
38+ . ./scripts/mk/config.kv
39 fi
40
41 # cppflags derived from build.cfg; any feature_* added there is picked up automatically
42@@ -69,25 +94,6 @@ LIB="shared/libredline/libredline.a shared/libutil/libutil.a shared/libutf/libut
43 # set by multi-file builders for generated headers, cleared after use
44 EXTRA_HDR=
45
46-# timestamp comparison
47-#
48-mtime() {
49- stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0
50-}
51-newer_than() {
52- local s t
53- [ -e "$2" ] || return 0
54- s=$(mtime "$1"); t=$(mtime "$2")
55- [ "$s" -gt "$t" ]
56-}
57-any_newer_than() {
58- local target="$1"; shift
59- for src do
60- newer_than "$src" "$target" && return 0
61- done
62- return 1
63-}
64-
65 # parallel job queue
66 # objs_for must not run in a subshell, enqueue writes _qn/_queue in the current shell
67 #
68@@ -238,59 +244,28 @@ cfg_enabled() {
69 }
70
71 build_simple_tools() {
72- local cat="$1" src base var staged=""
73+ local cat="$1" src base var
74 for src in cmd/"$cat"/*.c; do
75 [ -f "$src" ] || continue
76 base=${src##*/}; base=${base%.c}
77 var=$(cfgvar "$cat" "$base")
78 cfg_enabled "$var" || continue
79- compile_c "$src" "${src%.c}.o"
80- staged="$staged ${src%.c}"
81- done
82- drain
83- local bin
84- for bin in $staged; do
85- if [ "${bin##*/}" = "diff" ]; then
86- link_bin "$bin" "${bin}.o" -- $LIB -lm
87+ local bin="cmd/$cat/$base"
88+ # shellcheck disable=SC2086
89+ any_newer_than "$bin" "$src" $HDR $EXTRA_HDR $LIB || continue
90+ if [ "$base" = "diff" ]; then
91+ enqueue " CC $bin" "$CC $src $CPPFLAGS $CFLAGS $LDFLAGS -o $bin $LIB -lm $LDLIBS"
92 else
93- link_bin "$bin" "${bin}.o" -- $LIB
94+ enqueue " CC $bin" "$CC $src $CPPFLAGS $CFLAGS $LDFLAGS -o $bin $LIB $LDLIBS"
95 fi
96 done
97+ drain
98 }
99
100 build_awk() {
101 cfg_enabled BUILD_POSIX_AWK || return 0
102 local dir=cmd/posix/awk
103-
104- any_newer_than "$dir/awkgram.tab.c" "$dir/awkgram.y" && {
105- if command -v yacc >/dev/null 2>&1; then
106- printf ' YACC %s/awkgram.tab.c\n' "$dir"
107- yacc -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
108- if [ ! -f "$dir/awkgram.tab.h" ]; then
109- if [ -f y.tab.h ]; then
110- mv y.tab.h "$dir/awkgram.tab.h"
111- elif [ -f "$dir/y.tab.h" ]; then
112- mv "$dir/y.tab.h" "$dir/awkgram.tab.h"
113- fi
114- fi
115- elif command -v bison >/dev/null 2>&1; then
116- printf ' BISON %s/awkgram.tab.c\n' "$dir"
117- bison -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
118- else
119- printf 'awk: no yacc/bison; using pre-generated awkgram.tab.c\n' >&2
120- fi
121- }
122-
123- any_newer_than "$dir/maketab" "$dir/maketab.c" "$dir/awkgram.tab.h" && {
124- printf ' CC %s/maketab\n' "$dir"
125- eval "$CC $CFLAGS -o $dir/maketab $dir/maketab.c"
126- }
127-
128- any_newer_than "$dir/proctab.c" "$dir/maketab" && {
129- printf ' GEN %s/proctab.c\n' "$dir"
130- "$dir/maketab" "$dir/awkgram.tab.h" > "$dir/proctab.c"
131- }
132-
133+ CC="$CC" CFLAGS="$CFLAGS" YACC="$YACC" sh scripts/genconfig.sh awk
134 EXTRA_HDR="$dir/awk.h $dir/awkgram.tab.h $dir/proto.h"
135 objs_for "$dir" maketab.c "-I$dir"
136 EXTRA_HDR=
137@@ -300,37 +275,7 @@ build_awk() {
138 build_sh() {
139 cfg_enabled BUILD_POSIX_SH || return 0
140 local dir=cmd/posix/sh
141-
142- any_newer_than "$dir/mknodes" "$dir/mknodes.c" && {
143- printf ' CC %s/mknodes\n' "$dir"
144- eval "$CC $CFLAGS -o $dir/mknodes $dir/mknodes.c"
145- }
146-
147- any_newer_than "$dir/mksyntax" "$dir/mksyntax.c" && {
148- printf ' CC %s/mksyntax\n' "$dir"
149- eval "$CC $CPPFLAGS -I$dir $CFLAGS -o $dir/mksyntax $dir/mksyntax.c"
150- }
151-
152- any_newer_than "$dir/token.h" "$dir/mktokens" && {
153- printf ' GEN %s/token.h\n' "$dir"
154- (cd "$dir" && sh mktokens)
155- }
156-
157- any_newer_than "$dir/syntax.c" "$dir/mksyntax" && {
158- printf ' GEN %s/syntax.c\n' "$dir"
159- (cd "$dir" && ./mksyntax)
160- }
161-
162- any_newer_than "$dir/nodes.c" "$dir/mknodes" "$dir/nodetypes" "$dir/nodes.c.pat" && {
163- printf ' GEN %s/nodes.c\n' "$dir"
164- (cd "$dir" && ./mknodes nodetypes nodes.c.pat)
165- }
166-
167- any_newer_than "$dir/builtins.c" "$dir/mkbuiltins" "$dir/builtins.def" "$dir/shell.h" scripts/mk/config.kv && {
168- printf ' GEN %s/builtins.c\n' "$dir"
169- (cd "$dir" && sh mkbuiltins .)
170- }
171-
172+ CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" sh scripts/genconfig.sh sh
173 EXTRA_HDR="$dir/syntax.h $dir/nodes.h $dir/builtins.h $dir/token.h"
174 objs_for "$dir" "mknodes.c mksyntax.c" "-DSHELL -I$dir"
175 EXTRA_HDR=
176@@ -349,28 +294,15 @@ build_make() {
177 build_bc() {
178 cfg_enabled BUILD_POSIX_BC || return 0
179 local src=cmd/posix/bc.c
180-
181- any_newer_than "$src" cmd/posix/bc.y && {
182- printf ' GEN %s\n' "$src"
183- if command -v yacc >/dev/null 2>&1; then
184- yacc -o "$src" cmd/posix/bc.y
185- elif command -v bison >/dev/null 2>&1; then
186- bison -o "$src" cmd/posix/bc.y
187- else
188- printf 'bc: no yacc/bison found\n' >&2; return 1
189- fi
190- }
191-
192- compile_c "$src" "${src%.c}.o"
193- drain
194- link_bin cmd/posix/bc "${src%.c}.o" -- $LIB
195+ YACC="$YACC" sh scripts/genconfig.sh bc
196+ # shellcheck disable=SC2086
197+ any_newer_than cmd/posix/bc "$src" $HDR $EXTRA_HDR $LIB || return 0
198+ printf ' CC cmd/posix/bc\n'
199+ eval "$CC $src $CPPFLAGS $CFLAGS $LDFLAGS -o cmd/posix/bc $LIB $LDLIBS"
200 }
201
202 build_posix() {
203- cfg_enabled BUILD_POSIX_GETCONF && [ ! -f cmd/posix/getconf.h ] && {
204- printf ' GEN cmd/posix/getconf.h\n'
205- scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
206- }
207+ cfg_enabled BUILD_POSIX_GETCONF && sh scripts/genconfig.sh getconf
208 build_simple_tools posix
209 build_awk
210 build_bc
211@@ -521,49 +453,9 @@ do_regen() {
212 . scripts/mk/config.kv
213 fi
214
215- # 3. clean the generated files to force regeneration
216- rm -f cmd/posix/getconf.h cmd/posix/bc.c cmd/posix/bc.h
217- rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c cmd/posix/awk/maketab
218- rm -f cmd/posix/sh/mknodes cmd/posix/sh/mksyntax cmd/posix/sh/token.h cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h
219-
220- # 4. run the generation steps
221- # for getconf.h
222- printf ' GEN cmd/posix/getconf.h\n'
223- scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
224-
225- # for bc.c from bc.y
226- printf ' GEN cmd/posix/bc.c\n'
227- if command -v yacc >/dev/null 2>&1; then
228- yacc -o cmd/posix/bc.c cmd/posix/bc.y
229- elif command -v bison >/dev/null 2>&1; then
230- bison -o cmd/posix/bc.c cmd/posix/bc.y
231- fi
232-
233- # for awk
234- local adir=cmd/posix/awk
235- if command -v yacc >/dev/null 2>&1; then
236- yacc -d -o "$adir/awkgram.tab.c" "$adir/awkgram.y"
237- if [ ! -f "$adir/awkgram.tab.h" ]; then
238- if [ -f y.tab.h ]; then
239- mv y.tab.h "$adir/awkgram.tab.h"
240- elif [ -f "$adir/y.tab.h" ]; then
241- mv "$adir/y.tab.h" "$adir/awkgram.tab.h"
242- fi
243- fi
244- elif command -v bison >/dev/null 2>&1; then
245- bison -d -o "$adir/awkgram.tab.c" "$adir/awkgram.y"
246- fi
247- eval "$CC $CFLAGS -o $adir/maketab $adir/maketab.c"
248- "$adir/maketab" "$adir/awkgram.tab.h" > "$adir/proctab.c"
249-
250- # for sh
251- local sdir=cmd/posix/sh
252- eval "$CC $CFLAGS -o $sdir/mknodes $sdir/mknodes.c"
253- eval "$CC $CPPFLAGS -I$sdir $CFLAGS -o $sdir/mksyntax $sdir/mksyntax.c"
254- (cd "$sdir" && sh mktokens)
255- (cd "$sdir" && ./mksyntax)
256- (cd "$sdir" && ./mknodes nodetypes nodes.c.pat)
257- (cd "$sdir" && sh mkbuiltins .)
258+ # 3. clean and generate files using scripts/genconfig.sh
259+ CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" YACC="$YACC" sh scripts/genconfig.sh clean
260+ CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" YACC="$YACC" sh scripts/genconfig.sh all
261 }
262
263 do_clean() {
264@@ -573,18 +465,9 @@ do_clean() {
265 find shared -name '*.a' -exec rm -f {} +
266 printf ' CLEAN compiled binaries\n'
267 find cmd -type f ! -name '*.*' -perm -100 -exec rm -f {} +
268- printf ' CLEAN generated headers\n'
269- rm -f cmd/posix/getconf.h
270- rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h
271- rm -f cmd/posix/sh/token.h cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h
272- rm -f cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h
273- rm -f cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h
274- printf ' CLEAN generated sources\n'
275- rm -f cmd/posix/awk/proctab.c
276- rm -f cmd/posix/bc.c
277+ sh scripts/genconfig.sh clean
278+ rm -f scripts/mk/config.mk scripts/mk/config.kv scripts/mk/rules.mk shared/libutil/nofork_list.h
279 printf ' CLEAN build tools\n'
280- rm -f cmd/posix/awk/maketab
281- rm -f cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
282 rm -f scripts/mkman/mkman
283 printf ' CLEAN dev artifacts\n'
284 rm -f cmd/dev/cc/cc1 cmd/dev/cc/cpp cmd/dev/as/as cmd/dev/ld/ld cmd/dev/ar/ar
+31,
-21
1@@ -107,9 +107,10 @@ all)
2 FEATURE_SORT_BIG=1; FEATURE_SORT_STABLE=1
3 FEATURE_OD_ENDIAN=1;
4 FEATURE_SH_EASTER=1; FEATURE_SH_HISTEDIT=1
5- FEATURE_INPROC_EXEC=0; FEATURE_SH_LOCAL=1
6- FEATURE_SH_LET=1; FEATURE_SH_ULIMIT=1
7- FEATURE_SH_SETVAR=1; FEATURE_SH_WORDEXP=1
8+ FEATURE_SH_LOCAL=1 FEATURE_NOEXEC=0;
9+ FEATURE_NOFORK=0; FEATURE_SH_LET=1;
10+ FEATURE_SH_ULIMIT=1 FEATURE_SH_SETVAR=1;
11+ FEATURE_SH_WORDEXP=1
12 FEATURE_CAL_EXT=1
13 FEATURE_LS_COLOR=1
14 FEATURE_DIFF3_MERGE=1; FEATURE_DIFF3_EDSCRIPT=1
15@@ -158,30 +159,39 @@ unset _p
16 # BUILD_DEV_AS=1 # enable a tool whose group isnt in PRESET_UTILS
17 FEATURE_SH_HISTEDIT=1
18
19-# FEATURE_INPROC_EXEC: when set to 1, wexecvp/wexecv/wpopen/wsystem call
20-# registered tool mains directly instead of fork+exec. this is only meaningful
21-# inside aruu-box, where every tool has registered its main at startup
22+# FEATURE_NOEXEC: when set to 1, wexec/wpopen/wsystem can execute registered
23+# tool mains directly inside the forked child process (skipping execve)
24+# This is equivalent to Busybox noexec applet support.
25+# This applies to ALL commands contained within aruu-box, you may also
26+# specify if some specific command is not an attack vector to also be nofork
27+# via enabling FEATURE_NOFORK=1 and specifying those commands in NOFORK_LIST
28 #
29-# the analogy is busybox: a single binary where sh can call grep, sed, awk, etc.
30-# without spawning a new process for each one. scripts run faster and the whole
31-# system needs no external PATH at all. aruu-box built with this flag is fully
32-# self-contained, exactly like a busybox applet collection
33+#FEATURE_NOEXEC=1
34 #
35-# this is optional. set to 0 to build the box without inproc dispatch (each
36-# tool call still fork+execs). for individual standalone tool binaries this
37-# flag has no practical effect because no other mains are registered
38+# FEATURE_NOEXEC_OVERRIDE: only meaningful when FEATURE_NOEXEC=1
39+# when set to 1, ARUU_NOEXEC=0 in the environment forces fork+exec at runtime
40 #
41-FEATURE_INPROC_EXEC=1
42+#FEATURE_NOEXEC_OVERRIDE=1
43 #
44-# FEATURE_INPROC_EXEC_OVERRIDE: only meaningful when FEATURE_INPROC_EXEC=1
45-# when set to 1, ARUU_INPROC_EXEC=0 in the environment forces fork+exec at
46-# runtime, letting a person reach an external tool that shares a name with
47-# a builtin without giving its full path. when set to 0, that env var is
48-# never read at all and inproc dispatch cannot be bypassed except by a tool
49-# explicitly calling wexec_set_inproc()
50+# FEATURE_NOFORK: when set to 1, wexecvp/wexecv can run simple commands
51+# (like cat, echo, pwd, test) directly in the current process without spawning
52+# a subprocess (noexec+nofork). This is equivalent to Busybox nofork applet support
53 #
54-FEATURE_INPROC_EXEC_OVERRIDE=1
55+FEATURE_NOFORK=1
56 #
57+# FEATURE_NOFORK_OVERRIDE: only meaningful when FEATURE_NOFORK=1
58+# when set to 1, ARUU_NOFORK=0 in the environment forces fork for nofork commands
59+# even tho they will still not do execvp() call at runtime
60+# unless FEATURE_NOEXEC_OVERRIDE was enabled at build time and ARUU_NOEXEC=0
61+#
62+#FEATURE_NOFORK_OVERRIDE=1
63+#
64+# NOFORK_LIST: list of builtins that can run without fork (noexec+nofork)
65+#
66+NOFORK_LIST="true false echo pwd test [ basename dirname cat clear yes"
67+#
68+
69+BUILD_EXTRA_YAP=0
70
71 # tls backend: set exactly one to 1 to enable tls
72 #
+49,
-1
1@@ -43,6 +43,7 @@
2 */
3
4 #include "../../../shared/paths.h"
5+#include "../../../shared/wexec.h"
6 #include "builtins.h"
7 #include "error.h"
8 #include "eval.h"
9@@ -999,7 +1000,8 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
10 }
11
12 /* Fork off a child process if necessary. */
13- if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN)
14+ if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN
15+ || (cmdentry.cmdtype == CMDWEXEC && !(wexec_get_nofork() && wexec_is_nofork(argv[0]))))
16 && ((flags & EV_EXIT) == 0 || have_traps()))
17 || ((flags & EV_BACKCMD) != 0
18 && (cmdentry.cmdtype != CMDBUILTIN || !safe_builtin(cmdentry.u.index, argc, argv)))) {
19@@ -1094,6 +1096,52 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
20 }
21 if (jp)
22 exitshell(exitstatus);
23+ } else if (cmdentry.cmdtype == CMDWEXEC) {
24+ savecmdname = commandname;
25+ savetopfile = getcurrentfile();
26+ cmdenviron = &varlist;
27+ e = -1;
28+ savehandler = handler;
29+ if (setjmp(jmploc.loc)) {
30+ e = exception;
31+ if (e == EXINT)
32+ exitstatus = SIGINT + 128;
33+ goto cmddone_wexec;
34+ }
35+ handler = &jmploc;
36+ mode = REDIR_PUSH;
37+ if (flags == EV_BACKCMD) {
38+ memout.nextc = memout.buf;
39+ mode |= REDIR_BACKQ;
40+ }
41+ redirect(cmd->ncmd.redirect, mode);
42+ outclearerror(out1);
43+ listsetvar(cmdenviron, VNOSET);
44+ commandname = argv[0];
45+ exitstatus = wexecvp(argv[0], argv);
46+ flushall();
47+ if (outiserror(out1)) {
48+ warning("write error on stdout");
49+ if (exitstatus == 0 || exitstatus == 1)
50+ exitstatus = 2;
51+ }
52+ cmddone_wexec:
53+ cmdenviron = NULL;
54+ out1 = &output;
55+ out2 = &errout;
56+ freestdout();
57+ handler = savehandler;
58+ commandname = savecmdname;
59+ if (jp)
60+ exitshell(exitstatus);
61+ if (flags == EV_BACKCMD) {
62+ backcmd->buf = memout.buf;
63+ backcmd->nleft = memout.buf != NULL ? memout.nextc - memout.buf : 0;
64+ memout.buf = NULL;
65+ memout.nextc = NULL;
66+ memout.bufend = NULL;
67+ memout.bufsize = 64;
68+ }
69 } else if (cmdentry.cmdtype == CMDBUILTIN) {
70 #ifdef DEBUG
71 trputs("builtin command: ");
+45,
-15
1@@ -34,6 +34,7 @@
2
3 #include <errno.h>
4 #include <fcntl.h>
5+#include <signal.h>
6 #include <stdlib.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9@@ -102,17 +103,35 @@ shellexec(char **argv, char **envp, const char *path, int idx)
10 char *cmdname;
11 const char *opt;
12 int e;
13+#if FEATURE_NOEXEC
14+ struct sigaction sa;
15+ int i;
16+ int ret;
17+#endif
18
19-#if FEATURE_INPROC_EXEC
20+#if FEATURE_NOEXEC
21 {
22 /* basename of argv[0] for slash-free lookup */
23- const char *base = strrchr(argv[0], '/');
24- base = base ? base + 1 : argv[0];
25- if (wexec_get_inproc() && wexec_is_builtin(base)) {
26- int ret;
27+ const char *base = strrchr(argv[0], '/');
28+ extern char **environ;
29+ base = base ? base + 1 : argv[0];
30+ environ = envp;
31+ if (wexec_get_noexec() && wexec_is_builtin(base)) {
32+ /* reset caught signal handlers to default for inproc command */
33+ for (i = 1; i < NSIG; i++) {
34+ if (sigaction(i, NULL, &sa) == 0) {
35+ if (sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
36+ sa.sa_handler = SIG_DFL;
37+ sa.sa_flags = 0;
38+ sigemptyset(&sa.sa_mask);
39+ sigaction(i, &sa, NULL);
40+ }
41+ }
42+ }
43+
44 fflush(stdout);
45 fflush(stderr);
46- ret = wexecvp(base, argv);
47+ ret = wexec_call_builtin(base, argv);
48 fflush(stdout);
49 fflush(stderr);
50 _exit(ret);
51@@ -331,9 +350,9 @@ printentry(struct tblentry *cmdp, int verbose)
52 ckfree(name);
53 INTON;
54 }
55-#if FEATURE_INPROC_EXEC
56+#if FEATURE_NOEXEC || FEATURE_NOFORK
57 } else if (cmdp->cmdtype == CMDWEXEC) {
58- out1fmt("inproc %s", cmdp->cmdname);
59+ out1fmt("builtin %s", cmdp->cmdname);
60 #endif
61 #ifdef DEBUG
62 } else {
63@@ -392,10 +411,10 @@ find_command(const char *name, struct cmdentry *entry, int act, const char *path
64 goto success;
65 }
66
67-#if FEATURE_INPROC_EXEC
68- /* when inproc dispatch is active, registered wexec builtins are available
69+#if FEATURE_NOEXEC || FEATURE_NOFORK
70+ /* when builtin dispatch is active, registered wexec builtins are available
71 * even if they are not present on the filesystem */
72- if (wexec_get_inproc() && wexec_is_builtin(name)) {
73+ if ((wexec_get_noexec() || wexec_get_nofork()) && wexec_is_builtin(name)) {
74 INTOFF;
75 cmdp = cmdlookup(name, 1);
76 if (cmdp->cmdtype == CMDFUNCTION)
77@@ -821,12 +840,23 @@ typecmd_impl(int argc, char **argv, int cmd, const char *path)
78 out1fmt("%s is a shell builtin\n", argv[i]);
79 break;
80
81-#if FEATURE_INPROC_EXEC
82+#if FEATURE_NOEXEC || FEATURE_NOFORK
83 case CMDWEXEC:
84- if (cmd == TYPECMD_SMALLV)
85+ if (cmd == TYPECMD_SMALLV) {
86 out1fmt("%s\n", argv[i]);
87- else
88- out1fmt("%s is an inproc builtin\n", argv[i]);
89+ } else {
90+ if (wexec_get_nofork() && wexec_is_nofork(argv[i])) {
91+ out1fmt(
92+ "%s is embedded via wexec and registered by mkbox & genconfig.sh as "
93+ "noexec+nofork\n",
94+ argv[i]
95+ );
96+ } else if (wexec_get_noexec()) {
97+ out1fmt("%s is embedded via wexec and registered by mkbox as noexec\n", argv[i]);
98+ } else {
99+ out1fmt("%s is a shell builtin\n", argv[i]);
100+ }
101+ }
102 break;
103 #endif
104
+1,
-1
1@@ -39,7 +39,7 @@
2 #define CMDNORMAL 0 /* command is an executable program */
3 #define CMDBUILTIN 1 /* command is a shell builtin */
4 #define CMDFUNCTION 2 /* command is a shell function */
5-#define CMDWEXEC 3 /* command is a registered inproc builtin */
6+#define CMDWEXEC 3 /* command is a registered builtin applet */
7
8 /* values for typecmd_impl's third parameter */
9 enum {
+2,
-0
1@@ -42,6 +42,7 @@
2 #include <unistd.h>
3
4 #include "../../../shared/paths.h"
5+#include "../../../shared/wexec.h"
6 #include "builtins.h"
7 #include "cd.h"
8 #include "error.h"
9@@ -100,6 +101,7 @@ main(int argc, char *argv[])
10 int login;
11
12 (void)setlocale(LC_ALL, "");
13+ wexec_register_env_lookup(lookupvar);
14 initcharset();
15 state = 0;
16 if (setjmp(main_handler.loc)) {
+78,
-76
1@@ -1,7 +1,7 @@
2 /* See LICENSE file for copyright and license details. */
3-#include "wexec.h"
4-#include "util.h"
5 #include "arg.h"
6+#include "util.h"
7+#include "wexec.h"
8
9 #include <fcntl.h>
10 #include <limits.h>
11@@ -17,56 +17,56 @@ static int aflag;
12 static int
13 canexec(int fd, const char *name)
14 {
15- struct stat st;
16+ struct stat st;
17
18- if (fstatat(fd, name, &st, 0) < 0 || !S_ISREG(st.st_mode))
19- return 0;
20- return faccessat(fd, name, X_OK, AT_EACCESS) == 0;
21+ if (fstatat(fd, name, &st, 0) < 0 || !S_ISREG(st.st_mode))
22+ return 0;
23+ return faccessat(fd, name, X_OK, AT_EACCESS) == 0;
24 }
25
26 static int
27 which(const char *path, const char *name)
28 {
29- char *ptr, *p;
30- size_t i, len;
31- int dirfd, found = 0;
32+ char *ptr, *p;
33+ size_t i, len;
34+ int dirfd, found = 0;
35
36- if (strchr(name, '/')) {
37- found = canexec(AT_FDCWD, name);
38- if (found)
39- puts(name);
40- return found;
41- }
42+ if (strchr(name, '/')) {
43+ found = canexec(AT_FDCWD, name);
44+ if (found)
45+ puts(name);
46+ return found;
47+ }
48
49- ptr = p = enstrdup(3, path);
50- len = strlen(p);
51- for (i = 0; i < len + 1; i++) {
52- if (ptr[i] != ':' && ptr[i] != '\0')
53- continue;
54- ptr[i] = '\0';
55- if ((dirfd = open(p, O_RDONLY)) >= 0) {
56- if (canexec(dirfd, name)) {
57- found = 1;
58- fputs(p, stdout);
59- if (i && ptr[i - 1] != '/')
60- fputc('/', stdout);
61- puts(name);
62- }
63- close(dirfd);
64- if (!aflag && found)
65- break;
66- }
67- p = ptr + i + 1;
68- }
69- free(ptr);
70+ ptr = p = enstrdup(3, path);
71+ len = strlen(p);
72+ for (i = 0; i < len + 1; i++) {
73+ if (ptr[i] != ':' && ptr[i] != '\0')
74+ continue;
75+ ptr[i] = '\0';
76+ if ((dirfd = open(p, O_RDONLY)) >= 0) {
77+ if (canexec(dirfd, name)) {
78+ found = 1;
79+ fputs(p, stdout);
80+ if (i && ptr[i - 1] != '/')
81+ fputc('/', stdout);
82+ puts(name);
83+ }
84+ close(dirfd);
85+ if (!aflag && found)
86+ break;
87+ }
88+ p = ptr + i + 1;
89+ }
90+ free(ptr);
91
92- return found;
93+ return found;
94 }
95
96 static void
97 usage(void)
98 {
99- eprintf("usage: %s [-a] name ...\n", argv0);
100+ eprintf("usage: %s [-a] name ...\n", argv0);
101 }
102
103 // ?man which: locate a command
104@@ -75,49 +75,51 @@ usage(void)
105 int
106 main(int argc, char *argv[])
107 {
108- char *path;
109- int found = 0, foundall = 1;
110+ char *path;
111+ int found = 0, foundall = 1;
112
113- ARGBEGIN {
114- // ?man -a: print or show all entries
115- case 'a':
116- aflag = 1;
117- break;
118- default:
119- usage();
120- } ARGEND
121+ ARGBEGIN
122+ {
123+ // ?man -a: print or show all entries
124+ case 'a':
125+ aflag = 1;
126+ break;
127+ default:
128+ usage();
129+ }
130+ ARGEND
131
132- if (!argc)
133- usage();
134+ if (!argc)
135+ usage();
136
137- if (!(path = getenv("PATH")))
138- enprintf(3, "$PATH is not set\n");
139+ if (!(path = getenv("PATH")))
140+ enprintf(3, "$PATH is not set\n");
141
142- for (; *argv; argc--, argv++) {
143-#if FEATURE_INPROC_EXEC
144- /* inproc builtins are function pointers with no filesystem path */
145- if (wexec_get_inproc() && wexec_is_builtin(*argv)) {
146- printf("%s: inproc builtin\n", *argv);
147- found = 1;
148- if (!aflag)
149- continue;
150- }
151+ for (; *argv; argc--, argv++) {
152+#if FEATURE_NOEXEC
153+ /* builtins are function pointers with no filesystem path */
154+ if (wexec_get_noexec() && wexec_is_builtin(*argv)) {
155+ printf("%s: builtin applet\n", *argv);
156+ found = 1;
157+ if (!aflag)
158+ continue;
159+ }
160 #endif
161- if (which(path, *argv)) {
162- found = 1;
163- } else {
164-#if FEATURE_INPROC_EXEC
165- /* already reported above when inproc was active */
166- if (!wexec_get_inproc() || !wexec_is_builtin(*argv))
167+ if (which(path, *argv)) {
168+ found = 1;
169+ } else {
170+#if FEATURE_NOEXEC
171+ /* already reported above when builtin was active */
172+ if (!wexec_get_noexec() || !wexec_is_builtin(*argv))
173 #endif
174- {
175- weprintf("%s: not an external command\n", *argv);
176- foundall = 0;
177- }
178- }
179- }
180+ {
181+ weprintf("%s: not an external command\n", *argv);
182+ foundall = 0;
183+ }
184+ }
185+ }
186
187- if (fshut(stdout, "<stdout>"))
188- return 2;
189- return found ? foundall ? 0 : 1 : 2;
190+ if (fshut(stdout, "<stdout>"))
191+ return 2;
192+ return found ? foundall ? 0 : 1 : 2;
193 }
+302,
-148
1@@ -3,6 +3,8 @@
2
3 set -e
4
5+target=${1:-config}
6+
7 # load build.cfg
8 if [ -f build.cfg ]; then
9 . ./build.cfg
10@@ -11,45 +13,128 @@ else
11 exit 1
12 fi
13
14-mkdir -p scripts/mk
15-
16-mk_file="scripts/mk/config.mk"
17-kv_file="scripts/mk/config.kv"
18-
19-# clear existing files
20-: > "${mk_file}"
21-: > "${kv_file}"
22-
23-# write version prefix manprefix
24-echo "VERSION = ${VERSION}" >> "${mk_file}"
25-echo "VERSION=\"${VERSION}\"" >> "${kv_file}"
26-
27-echo "PREFIX = ${PREFIX}" >> "${mk_file}"
28-echo "PREFIX=\"${PREFIX}\"" >> "${kv_file}"
29+# helper to check if source is newer than target
30+newer_than() {
31+ [ -e "$2" ] || return 0
32+ local s t
33+ s=$(stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0)
34+ t=$(stat -c '%Y' "$2" 2>/dev/null || stat -f '%m' "$2" 2>/dev/null || echo 0)
35+ [ "$s" -gt "$t" ]
36+}
37
38-# default manprefix to prefix/share/man if empty
39-man_pref="${MANPREFIX:-/usr/local/share/man}"
40-echo "MANPREFIX = ${man_pref}" >> "${mk_file}"
41-echo "MANPREFIX=\"${man_pref}\"" >> "${kv_file}"
42+gen_getconf() {
43+ if newer_than scripts/getconf.sh cmd/posix/getconf.h; then
44+ printf " GEN cmd/posix/getconf.h\n"
45+ scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
46+ fi
47+}
48
49-# toolchain defaults
50-echo "RANLIB = ${RANLIB:-ranlib}" >> "${mk_file}"
51-echo "RANLIB=\"${RANLIB:-ranlib}\"" >> "${kv_file}"
52+gen_bc() {
53+ if newer_than cmd/posix/bc.y cmd/posix/bc.c; then
54+ printf " GEN cmd/posix/bc.c\n"
55+ CC=${CC:-cc}
56+ YACC=${YACC:-}
57+ if [ -z "$YACC" ]; then
58+ if command -v yacc >/dev/null 2>&1; then
59+ YACC="yacc"
60+ elif command -v bison >/dev/null 2>&1; then
61+ YACC="bison -y"
62+ else
63+ YACC="yacc"
64+ fi
65+ fi
66+ if [ "$YACC" = "bison -y" ]; then
67+ bison -o cmd/posix/bc.c cmd/posix/bc.y
68+ else
69+ $YACC -o cmd/posix/bc.c cmd/posix/bc.y
70+ fi
71+ fi
72+}
73
74-echo "AR = ${AR:-ar}" >> "${mk_file}"
75-echo "AR=\"${AR:-ar}\"" >> "${kv_file}"
76+gen_awk() {
77+ local dir="cmd/posix/awk"
78+ CC=${CC:-cc}
79+ YACC=${YACC:-}
80+ if [ -z "$YACC" ]; then
81+ if command -v yacc >/dev/null 2>&1; then
82+ YACC="yacc"
83+ elif command -v bison >/dev/null 2>&1; then
84+ YACC="bison -y"
85+ else
86+ YACC="yacc"
87+ fi
88+ fi
89+
90+ if newer_than "$dir/awkgram.y" "$dir/awkgram.tab.c" || newer_than "$dir/awkgram.y" "$dir/awkgram.tab.h"; then
91+ printf " YACC $dir/awkgram.tab.c\n"
92+ if [ "$YACC" = "bison -y" ]; then
93+ bison -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
94+ else
95+ $YACC -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
96+ if [ ! -f "$dir/awkgram.tab.h" ]; then
97+ if [ -f y.tab.h ]; then
98+ mv y.tab.h "$dir/awkgram.tab.h"
99+ elif [ -f "$dir/y.tab.h" ]; then
100+ mv "$dir/y.tab.h" "$dir/awkgram.tab.h"
101+ fi
102+ fi
103+ fi
104+ fi
105+
106+ if newer_than "$dir/maketab.c" "$dir/maketab" || newer_than "$dir/awkgram.tab.h" "$dir/maketab"; then
107+ printf " CC $dir/maketab\n"
108+ $CC -o "$dir/maketab" "$dir/maketab.c"
109+ fi
110+
111+ if newer_than "$dir/maketab" "$dir/proctab.c" || newer_than "$dir/awkgram.tab.h" "$dir/proctab.c"; then
112+ printf " GEN $dir/proctab.c\n"
113+ "$dir/maketab" "$dir/awkgram.tab.h" > "$dir/proctab.c"
114+ fi
115+}
116
117-echo "ARFLAGS = ${ARFLAGS:-rc}" >> "${mk_file}"
118-echo "ARFLAGS=\"${ARFLAGS:-rc}\"" >> "${kv_file}"
119+gen_sh() {
120+ local dir="cmd/posix/sh"
121+ CC=${CC:-cc}
122+
123+ if newer_than "$dir/mknodes.c" "$dir/mknodes"; then
124+ printf " CC $dir/mknodes\n"
125+ $CC -o "$dir/mknodes" "$dir/mknodes.c"
126+ fi
127+
128+ if newer_than "$dir/mksyntax.c" "$dir/mksyntax"; then
129+ printf " CC $dir/mksyntax\n"
130+ $CC -Ishared -I"$dir" -o "$dir/mksyntax" "$dir/mksyntax.c"
131+ fi
132+
133+ if newer_than "$dir/mktokens" "$dir/token.h"; then
134+ printf " GEN $dir/token.h\n"
135+ (cd "$dir" && sh mktokens)
136+ fi
137+
138+ if newer_than "$dir/mksyntax" "$dir/syntax.c" || newer_than "$dir/mksyntax" "$dir/syntax.h"; then
139+ printf " GEN $dir/syntax.c\n"
140+ (cd "$dir" && ./mksyntax)
141+ fi
142+
143+ if newer_than "$dir/mknodes" "$dir/nodes.c" || newer_than "$dir/nodetypes" "$dir/nodes.c" || newer_than "$dir/nodes.c.pat" "$dir/nodes.c"; then
144+ printf " GEN $dir/nodes.c\n"
145+ (cd "$dir" && ./mknodes nodetypes nodes.c.pat)
146+ fi
147+
148+ if newer_than "$dir/mkbuiltins" "$dir/builtins.c" || newer_than "$dir/builtins.def" "$dir/builtins.c" || newer_than "$dir/shell.h" "$dir/builtins.c" || [ ! -f "$dir/builtins.c" ]; then
149+ printf " GEN $dir/builtins.c\n"
150+ (cd "$dir" && sh mkbuiltins .)
151+ fi
152+}
153
154-# build toggles
155-echo "" >> "${mk_file}"
156-echo "# build toggles" >> "${mk_file}"
157-for g in BUILD_POSIX BUILD_XSI BUILD_NET BUILD_PSEUDO BUILD_EXTRA BUILD_LINUX BUILD_DEV; do
158- eval "val=\${$g:-0}"
159- echo "$g = $val" >> "${mk_file}"
160- echo "$g=\"$val\"" >> "${kv_file}"
161-done
162+clean() {
163+ printf " CLEAN generated files\n"
164+ rm -f cmd/posix/getconf.h
165+ rm -f cmd/posix/bc.c cmd/posix/bc.h
166+ rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c cmd/posix/awk/maketab
167+ rm -f cmd/posix/sh/mknodes cmd/posix/sh/mksyntax cmd/posix/sh/token.h cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h
168+ rm -f scripts/mk/config.mk scripts/mk/config.kv scripts/mk/rules.mk shared/libutil/nofork_list.h
169+}
170
171 # dynamic tool discovery function
172 list_tools() {
173@@ -100,110 +185,6 @@ write_subtarget() {
174 fi
175 }
176
177-# write subtargets for each category dynamically
178-for catg in posix xsi net pseudo extra linux dev; do
179- echo "" >> "${mk_file}"
180- echo "# ${catg} subtargets" >> "${mk_file}"
181- tools=$(list_tools "$catg")
182- for t in $tools; do
183- write_subtarget "$catg" "$t"
184- done
185-
186- # build the enabled list using lowercase tools
187- enabled=""
188- for t in $tools; do
189- u_group=$(echo "$catg" | tr '[:lower:]' '[:upper:]')
190- u_tool=$(echo "$t" | tr 'a-z-' 'A-Z_')
191-
192- # special cases for dev tools mapping to different variables
193- if [ "$catg" = "dev" ] && [ "$u_tool" = "AS" ]; then
194- var_name="BUILD_DEV_CC"
195- else
196- var_name="BUILD_${u_group}_${u_tool}"
197- fi
198-
199- eval "has_override=\${$var_name+yes}"
200- eval "val=\${$var_name:-}"
201- if [ "$has_override" != "yes" ]; then
202- if [ "$catg" = "dev" ] && [ "$u_tool" = "AS" ]; then
203- eval "val=\$BUILD_DEV"
204- else
205- eval "val=\$BUILD_${u_group}"
206- fi
207- fi
208-
209- if [ "$val" = "1" ]; then
210- # map to the correct path
211- case "$catg/$t" in
212- posix/awk) path="cmd/posix/awk/awk" ;;
213- posix/sh) path="cmd/posix/sh/sh" ;;
214- posix/make) path="cmd/posix/make/make" ;;
215- extra/yap) path="cmd/extra/yap/yap" ;;
216- dev/ar) path="cmd/dev/ar/ar" ;;
217- dev/as) path="cmd/dev/as/as" ;;
218- dev/ld) path="cmd/dev/ld/ld" ;;
219- dev/cc) path="cmd/dev/cc/cc" ;;
220- *) path="cmd/$catg/$t" ;;
221- esac
222- enabled="$enabled $path"
223- fi
224- done
225- u_catg=$(echo "$catg" | tr '[:lower:]' '[:upper:]')
226- # write the list of enabled binaries
227- echo "${u_catg}_BIN_CONFIG =${enabled}" >> "${mk_file}"
228-done
229-
230-# write feature flags
231-echo "" >> "${mk_file}"
232-echo "# feature flags" >> "${mk_file}"
233-features=$(set | grep '^FEATURE_[A-Za-z0-9_]*=' | cut -d= -f1 | sort)
234-for f in $features; do
235- eval "val=\$$f"
236- echo "$f = $val" >> "${mk_file}"
237- echo "$f=\"$val\"" >> "${kv_file}"
238-done
239-
240-# write TLS variables to config files
241-echo "" >> "${mk_file}"
242-echo "CPPFLAGS_TLS = ${CPPFLAGS_TLS}" >> "${mk_file}"
243-echo "CPPFLAGS_TLS=\"${CPPFLAGS_TLS}\"" >> "${kv_file}"
244-
245-echo "LDFLAGS_TLS = ${LDFLAGS_TLS}" >> "${mk_file}"
246-echo "LDFLAGS_TLS=\"${LDFLAGS_TLS}\"" >> "${kv_file}"
247-
248-echo "LDLIBS_TLS = ${LDLIBS_TLS}" >> "${mk_file}"
249-echo "LDLIBS_TLS=\"${LDLIBS_TLS}\"" >> "${kv_file}"
250-
251-# write CPPFLAGS with all feature macros
252-echo "" >> "${mk_file}"
253-echo "CPPFLAGS =\\" >> "${mk_file}"
254-echo " -Ishared\\" >> "${mk_file}"
255-echo " -DPREFIX=\\\"\\\$(PREFIX)\\\"\\" >> "${mk_file}"
256-echo " -D_DEFAULT_SOURCE\\" >> "${mk_file}"
257-echo " -D_GNU_SOURCE\\" >> "${mk_file}"
258-echo " -D_NETBSD_SOURCE\\" >> "${mk_file}"
259-echo " -D_BSD_SOURCE\\" >> "${mk_file}"
260-echo " -D_XOPEN_SOURCE=700\\" >> "${mk_file}"
261-echo " -D_FILE_OFFSET_BITS=64\\" >> "${mk_file}"
262-for f in $features; do
263- if [ "$f" = "FEATURE_USE_SSL" ]; then
264- continue
265- fi
266- echo " -D$f=\$($f)\\" >> "${mk_file}"
267-done
268-echo " \$(CPPFLAGS_TLS)" >> "${mk_file}"
269-
270-# write toolchain configuration
271-echo "" >> "${mk_file}"
272-echo "CC = ${CC:-cc}" >> "${mk_file}"
273-echo "CFLAGS = ${CFLAGS:-}" >> "${mk_file}"
274-echo "LDFLAGS = ${LDFLAGS:-} \$(LDFLAGS_TLS)" >> "${mk_file}"
275-echo "LDLIBS = ${LDLIBS:-} \$(LDLIBS_TLS)" >> "${mk_file}"
276-
277-# write compilation rules for subdirectories that need special flags or nested structures
278-rules_file="scripts/mk/rules.mk"
279-echo "# autogenerated compilation rules" > "${rules_file}"
280-
281 gen_dir_rules() {
282 local dir="$1" flags="$2" extra_files="$3" f obj files uniq_files
283 [ -d "$dir" ] || return 0
284@@ -224,13 +205,186 @@ gen_dir_rules() {
285 done
286 }
287
288-echo "" >> "${rules_file}"
289-gen_dir_rules "cmd/posix/awk" "-Icmd/posix/awk" "cmd/posix/awk/awkgram.tab.c cmd/posix/awk/proctab.c"
290-gen_dir_rules "cmd/posix/sh" "-DSHELL -Icmd/posix/sh" "cmd/posix/sh/syntax.c cmd/posix/sh/nodes.c cmd/posix/sh/builtins.c"
291-gen_dir_rules "cmd/extra/yap" "" ""
292-gen_dir_rules "cmd/dev/ar" "-Icmd/dev/ar" ""
293-gen_dir_rules "cmd/dev/xcutil" "-Icmd/dev/xcutil" ""
294-gen_dir_rules "cmd/dev/ld" "-Icmd/dev/xcutil -Icmd/dev/ld" ""
295-gen_dir_rules "cmd/dev/as" "-Icmd/dev/xcutil -Icmd/dev/as" ""
296-gen_dir_rules "cmd/dev/as/arch/x64" "-Icmd/dev/xcutil -Icmd/dev/as -Icmd/dev/as/arch/x64" ""
297-gen_dir_rules "cmd/dev/cc" "-Icmd/dev/cc" ""
298+case "$target" in
299+ getconf) gen_getconf ;;
300+ bc) gen_bc ;;
301+ awk) gen_awk ;;
302+ sh) gen_sh ;;
303+ clean) clean ;;
304+ all)
305+ gen_getconf
306+ gen_bc
307+ gen_awk
308+ gen_sh
309+ ;;
310+ config)
311+ mkdir -p scripts/mk
312+
313+ mk_file="scripts/mk/config.mk"
314+ kv_file="scripts/mk/config.kv"
315+
316+ # clear existing files
317+ : > "${mk_file}"
318+ : > "${kv_file}"
319+
320+ # write version prefix manprefix
321+ echo "VERSION = ${VERSION}" >> "${mk_file}"
322+ echo "VERSION=\"${VERSION}\"" >> "${kv_file}"
323+
324+ echo "PREFIX = ${PREFIX}" >> "${mk_file}"
325+ echo "PREFIX=\"${PREFIX}\"" >> "${kv_file}"
326+
327+ # default manprefix to prefix/share/man if empty
328+ man_pref="${MANPREFIX:-/usr/local/share/man}"
329+ echo "MANPREFIX = ${man_pref}" >> "${mk_file}"
330+ echo "MANPREFIX=\"${man_pref}\"" >> "${kv_file}"
331+
332+ # toolchain defaults
333+ echo "RANLIB = ${RANLIB:-ranlib}" >> "${mk_file}"
334+ echo "RANLIB=\"${RANLIB:-ranlib}\"" >> "${kv_file}"
335+
336+ echo "AR = ${AR:-ar}" >> "${mk_file}"
337+ echo "AR=\"${AR:-ar}\"" >> "${kv_file}"
338+
339+ echo "ARFLAGS = ${ARFLAGS:-rc}" >> "${mk_file}"
340+ echo "ARFLAGS=\"${ARFLAGS:-rc}\"" >> "${kv_file}"
341+
342+ # build toggles
343+ echo "" >> "${mk_file}"
344+ echo "# build toggles" >> "${mk_file}"
345+ for g in BUILD_POSIX BUILD_XSI BUILD_NET BUILD_PSEUDO BUILD_EXTRA BUILD_LINUX BUILD_DEV; do
346+ eval "val=\${$g:-0}"
347+ echo "$g = $val" >> "${mk_file}"
348+ echo "$g=\"$val\"" >> "${kv_file}"
349+ done
350+
351+ # write subtargets for each category dynamically
352+ for catg in posix xsi net pseudo extra linux dev; do
353+ echo "" >> "${mk_file}"
354+ echo "# ${catg} subtargets" >> "${mk_file}"
355+ tools=$(list_tools "$catg")
356+ for t in $tools; do
357+ write_subtarget "$catg" "$t"
358+ done
359+
360+ # build the enabled list using lowercase tools
361+ enabled=""
362+ for t in $tools; do
363+ u_group=$(echo "$catg" | tr '[:lower:]' '[:upper:]')
364+ u_tool=$(echo "$t" | tr 'a-z-' 'A-Z_')
365+
366+ # special cases for dev tools mapping to different variables
367+ if [ "$catg" = "dev" ] && [ "$u_tool" = "AS" ]; then
368+ var_name="BUILD_DEV_CC"
369+ else
370+ var_name="BUILD_${u_group}_${u_tool}"
371+ fi
372+
373+ eval "has_override=\${$var_name+yes}"
374+ eval "val=\${$var_name:-}"
375+ if [ "$has_override" != "yes" ]; then
376+ if [ "$catg" = "dev" ] && [ "$u_tool" = "AS" ]; then
377+ eval "val=\$BUILD_DEV"
378+ else
379+ eval "val=\$BUILD_${u_group}"
380+ fi
381+ fi
382+
383+ if [ "$val" = "1" ]; then
384+ # map to the correct path
385+ case "$catg/$t" in
386+ posix/awk) path="cmd/posix/awk/awk" ;;
387+ posix/sh) path="cmd/posix/sh/sh" ;;
388+ posix/make) path="cmd/posix/make/make" ;;
389+ extra/yap) path="cmd/extra/yap/yap" ;;
390+ dev/ar) path="cmd/dev/ar/ar" ;;
391+ dev/as) path="cmd/dev/as/as" ;;
392+ dev/ld) path="cmd/dev/ld/ld" ;;
393+ dev/cc) path="cmd/dev/cc/cc" ;;
394+ *) path="cmd/$catg/$t" ;;
395+ esac
396+ enabled="$enabled $path"
397+ fi
398+ done
399+ u_catg=$(echo "$catg" | tr '[:lower:]' '[:upper:]')
400+ # write the list of enabled binaries
401+ echo "${u_catg}_BIN_CONFIG =${enabled}" >> "${mk_file}"
402+ done
403+
404+ # write feature flags
405+ echo "" >> "${mk_file}"
406+ echo "# feature flags" >> "${mk_file}"
407+ features=$(set | grep '^FEATURE_[A-Za-z0-9_]*=' | cut -d= -f1 | sort)
408+ for f in $features; do
409+ eval "val=\$$f"
410+ echo "$f = $val" >> "${mk_file}"
411+ echo "$f=\"$val\"" >> "${kv_file}"
412+ done
413+
414+ # write TLS variables to config files
415+ echo "" >> "${mk_file}"
416+ echo "CPPFLAGS_TLS = ${CPPFLAGS_TLS}" >> "${mk_file}"
417+ echo "CPPFLAGS_TLS=\"${CPPFLAGS_TLS}\"" >> "${kv_file}"
418+
419+ echo "LDFLAGS_TLS = ${LDFLAGS_TLS}" >> "${mk_file}"
420+ echo "LDFLAGS_TLS=\"${LDFLAGS_TLS}\"" >> "${kv_file}"
421+
422+ echo "LDLIBS_TLS = ${LDLIBS_TLS}" >> "${mk_file}"
423+ echo "LDLIBS_TLS=\"${LDLIBS_TLS}\"" >> "${kv_file}"
424+
425+ # write CPPFLAGS with all feature macros
426+ echo "" >> "${mk_file}"
427+ echo "CPPFLAGS =\\" >> "${mk_file}"
428+ echo " -Ishared\\" >> "${mk_file}"
429+ echo " -DPREFIX=\\\"\\\$(PREFIX)\\\"\\" >> "${mk_file}"
430+ echo " -D_DEFAULT_SOURCE\\" >> "${mk_file}"
431+ echo " -D_GNU_SOURCE\\" >> "${mk_file}"
432+ echo " -D_NETBSD_SOURCE\\" >> "${mk_file}"
433+ echo " -D_BSD_SOURCE\\" >> "${mk_file}"
434+ echo " -D_XOPEN_SOURCE=700\\" >> "${mk_file}"
435+ echo " -D_FILE_OFFSET_BITS=64\\" >> "${mk_file}"
436+ for f in $features; do
437+ if [ "$f" = "FEATURE_USE_SSL" ]; then
438+ continue
439+ fi
440+ echo " -D$f=\$($f)\\" >> "${mk_file}"
441+ done
442+ echo " \$(CPPFLAGS_TLS)" >> "${mk_file}"
443+
444+ # write toolchain configuration
445+ echo "" >> "${mk_file}"
446+ echo "CC = ${CC:-cc}" >> "${mk_file}"
447+ echo "CFLAGS = ${CFLAGS:-}" >> "${mk_file}"
448+ echo "LDFLAGS = ${LDFLAGS:-} \$(LDFLAGS_TLS)" >> "${mk_file}"
449+ echo "LDLIBS = ${LDLIBS:-} \$(LDLIBS_TLS)" >> "${mk_file}"
450+
451+ # write compilation rules for subdirectories that need special flags or nested structures
452+ rules_file="scripts/mk/rules.mk"
453+ echo "# autogenerated compilation rules" > "${rules_file}"
454+
455+ echo "" >> "${rules_file}"
456+ gen_dir_rules "cmd/posix/awk" "-Icmd/posix/awk" "cmd/posix/awk/awkgram.tab.c cmd/posix/awk/proctab.c"
457+ gen_dir_rules "cmd/posix/sh" "-DSHELL -Icmd/posix/sh" "cmd/posix/sh/syntax.c cmd/posix/sh/nodes.c cmd/posix/sh/builtins.c"
458+ gen_dir_rules "cmd/extra/yap" "" ""
459+ gen_dir_rules "cmd/dev/ar" "-Icmd/dev/ar" ""
460+ gen_dir_rules "cmd/dev/xcutil" "-Icmd/dev/xcutil" ""
461+ gen_dir_rules "cmd/dev/ld" "-Icmd/dev/xcutil -Icmd/dev/ld" ""
462+ gen_dir_rules "cmd/dev/as" "-Icmd/dev/xcutil -Icmd/dev/as" ""
463+ gen_dir_rules "cmd/dev/as/arch/x64" "-Icmd/dev/xcutil -Icmd/dev/as -Icmd/dev/as/arch/x64" ""
464+ gen_dir_rules "cmd/dev/cc" "-Icmd/dev/cc" ""
465+
466+ # generate nofork_list.h
467+ nofork_h="shared/libutil/nofork_list.h"
468+ echo "/* autogenerated by genconfig.sh */" > "${nofork_h}"
469+ echo "static const char *nofork_list[] = {" >> "${nofork_h}"
470+ for app in ${NOFORK_LIST}; do
471+ echo " \"${app}\"," >> "${nofork_h}"
472+ done
473+ echo " NULL" >> "${nofork_h}"
474+ echo "};" >> "${nofork_h}"
475+ ;;
476+ *)
477+ printf "unknown target: %s\n" "$target" >&2
478+ exit 1
479+ ;;
480+esac
1@@ -118,9 +118,15 @@
2 #ifndef FEATURE_PATCH_STRIP_CR
3 #define FEATURE_PATCH_STRIP_CR 1
4 #endif
5-#ifndef FEATURE_INPROC_EXEC
6-#define FEATURE_INPROC_EXEC 0
7+#ifndef FEATURE_NOEXEC
8+#define FEATURE_NOEXEC 0
9 #endif
10-#ifndef FEATURE_INPROC_EXEC_OVERRIDE
11-#define FEATURE_INPROC_EXEC_OVERRIDE 0
12+#ifndef FEATURE_NOEXEC_OVERRIDE
13+#define FEATURE_NOEXEC_OVERRIDE 0
14+#endif
15+#ifndef FEATURE_NOFORK
16+#define FEATURE_NOFORK 0
17+#endif
18+#ifndef FEATURE_NOFORK_OVERRIDE
19+#define FEATURE_NOFORK_OVERRIDE 0
20 #endif
1@@ -38,6 +38,8 @@
2 #define CTRL_U 21
3 #define CTRL_W 23
4 #define CTRL_Y 25
5+#define CTRL_Z 26
6+#define CTRL_QUIT 28
7 #define BACKSPACE 127
8 #define ESC 27
9
10@@ -73,6 +75,8 @@ static char *kill_buffer = NULL;
11 static volatile sig_atomic_t winch_received = 0;
12 static struct sigaction orig_sigwinch;
13
14+static void redlineEditStop(struct redlineState *l);
15+
16 static void (*completionCallback)(const char *, struct redlineCompletions *) = NULL;
17
18 /* return the number of bytes that compose the utf-8 character starting at c */
19@@ -330,7 +334,7 @@ enableRawMode(int fd)
20 return -1;
21
22 raw = orig_termios;
23- raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
24+ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP);
25 raw.c_oflag &= ~(OPOST);
26 raw.c_cflag |= (CS8);
27 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
28@@ -1189,13 +1193,33 @@ redlineEditFeed(struct redlineState *l)
29 }
30
31 switch (c) {
32+ case 10:
33 case ENTER:
34 if (mlmode)
35 redlineEditMoveEnd(l);
36 return strdup(l->buf);
37 case CTRL_C:
38+ if (write(l->ofd, "^C", 2) == -1) {}
39 errno = EAGAIN;
40 return NULL;
41+ case CTRL_Z:
42+ break;
43+ case CTRL_QUIT:
44+ if (write(l->ofd, "^\\", 2) == -1) {}
45+ redlineEditStop(l);
46+ fflush(stdout);
47+ {
48+ struct sigaction sa, osa;
49+ sa.sa_handler = SIG_DFL;
50+ sigemptyset(&sa.sa_mask);
51+ sa.sa_flags = 0;
52+ sigaction(SIGQUIT, &sa, &osa);
53+ kill(getpid(), SIGQUIT);
54+ sigaction(SIGQUIT, &osa, NULL);
55+ }
56+ enableRawMode(l->ifd);
57+ refreshLine(l);
58+ break;
59 case BACKSPACE:
60 case 8:
61 redlineEditBackspace(l);
62@@ -1321,6 +1345,8 @@ redlineEditFeed(struct redlineState *l)
63 }
64 break;
65 default:
66+ if (c < 32)
67+ break;
68 utf8len = utf8ByteLen(c);
69 utf8[0] = c;
70 if (utf8len > 1) {
71@@ -1441,6 +1467,9 @@ redline(const char *prompt)
72 free(history[history_len]);
73 }
74 free(l.buf);
75+ if (res == NULL && errno == EAGAIN) {
76+ kill(getpid(), SIGINT);
77+ }
78 return res;
79 }
80
1@@ -5,6 +5,7 @@
2 #include "../util.h"
3
4 #include <errno.h>
5+#include <setjmp.h>
6 #include <signal.h>
7 #include <stdlib.h>
8 #include <string.h>
9@@ -12,7 +13,7 @@
10 #include <sys/wait.h>
11 #include <unistd.h>
12
13-#define MAX_BUILTINS 128
14+#define MAX_BUILTINS 256
15 #define MAX_POPEN 32
16
17 struct builtin {
18@@ -31,6 +32,22 @@ struct popen_entry {
19 static struct popen_entry popen_tab[MAX_POPEN];
20 static int npopen;
21
22+static char *(*wexec_env_lookup)(const char *name) = NULL;
23+
24+void
25+wexec_register_env_lookup(char *(*fn)(const char *name))
26+{
27+ wexec_env_lookup = fn;
28+}
29+
30+static const char *
31+wexec_getenv(const char *name)
32+{
33+ if (wexec_env_lookup)
34+ return wexec_env_lookup(name);
35+ return getenv(name);
36+}
37+
38 int
39 wpopen_track(FILE *fp, pid_t pid)
40 {
41@@ -85,49 +102,77 @@ wexec_is_builtin(const char *name)
42 return 0;
43 }
44
45-#if FEATURE_INPROC_EXEC
46-static int inproc_enabled = 1;
47+int
48+wexec_is_nofork(const char *name)
49+{
50+#include "nofork_list.h"
51+ int i;
52+ for (i = 0; nofork_list[i]; i++) {
53+ if (strcmp(nofork_list[i], name) == 0)
54+ return 1;
55+ }
56+ return 0;
57+}
58
59-#if FEATURE_INPROC_EXEC_OVERRIDE
60-static int inproc_env_read = 0;
61+#if FEATURE_NOEXEC
62+static int noexec_enabled = 1;
63
64-/* ARUU_INPROC_EXEC=0 forces fork+exec at runtime, even though the box was
65- * built with FEATURE_INPROC_EXEC=1. lets a person reach an external tool
66- * that shares a name with a builtin without giving its full path. only
67- * compiled in when the box is also built with FEATURE_INPROC_EXEC_OVERRIDE=1;
68- * without that flag this env var is never read and inproc dispatch cannot be
69- * bypassed except through an explicit wexec_set_inproc() call. read once
70- * and cached, an explicit wexec_set_inproc() call overrides it */
71+#if FEATURE_NOEXEC_OVERRIDE
72 static void
73-inproc_read_env(void)
74+noexec_read_env(void)
75 {
76- const char *e;
77-
78- if (inproc_env_read)
79- return;
80- inproc_env_read = 1;
81- e = getenv("ARUU_INPROC_EXEC");
82+ const char *e = wexec_getenv("ARUU_NOEXEC");
83 if (e && strcmp(e, "0") == 0)
84- inproc_enabled = 0;
85+ noexec_enabled = 0;
86+ else
87+ noexec_enabled = 1;
88 }
89 #endif
90
91 void
92-wexec_set_inproc(int enabled)
93+wexec_set_noexec(int enabled)
94+{
95+ noexec_enabled = enabled;
96+}
97+
98+int
99+wexec_get_noexec(void)
100+{
101+#if FEATURE_NOEXEC_OVERRIDE
102+ noexec_read_env();
103+#endif
104+ return noexec_enabled;
105+}
106+#endif
107+
108+#if FEATURE_NOFORK
109+static int nofork_enabled = 1;
110+
111+#if FEATURE_NOFORK_OVERRIDE
112+static void
113+nofork_read_env(void)
114 {
115-#if FEATURE_INPROC_EXEC_OVERRIDE
116- inproc_env_read = 1;
117+ const char *e = wexec_getenv("ARUU_NOFORK");
118+ if (e && strcmp(e, "0") == 0)
119+ nofork_enabled = 0;
120+ else
121+ nofork_enabled = 1;
122+}
123 #endif
124- inproc_enabled = enabled;
125+
126+void
127+wexec_set_nofork(int enabled)
128+{
129+ nofork_enabled = enabled;
130 }
131
132 int
133-wexec_get_inproc(void)
134+wexec_get_nofork(void)
135 {
136-#if FEATURE_INPROC_EXEC_OVERRIDE
137- inproc_read_env();
138+#if FEATURE_NOFORK_OVERRIDE
139+ nofork_read_env();
140 #endif
141- return inproc_enabled;
142+ return nofork_enabled;
143 }
144 #endif
145
146@@ -137,11 +182,11 @@ wwhich(const char *name)
147 const char *path, *p, *end;
148 char *buf;
149 size_t dlen, nlen;
150- struct stat st;
151+ struct stat st;
152
153-#if FEATURE_INPROC_EXEC
154- /* when inproc dispatch is active, registered builtins have no filesystem path */
155- if (inproc_enabled && wexec_is_builtin(name))
156+#if FEATURE_NOEXEC
157+ /* when builtin dispatch is active, registered builtins have no filesystem path */
158+ if (wexec_get_noexec() && wexec_is_builtin(name))
159 return NULL;
160 #endif
161
162@@ -152,7 +197,7 @@ wwhich(const char *name)
163 return NULL;
164 }
165
166- path = getenv("PATH");
167+ path = wexec_getenv("PATH");
168 if (path == NULL)
169 path = "/usr/local/bin:/usr/bin:/bin";
170
171@@ -163,9 +208,9 @@ wwhich(const char *name)
172 dlen = end ? (size_t)(end - p) : strlen(p);
173 /* skip empty components (current dir) for safety */
174 if (dlen > 0) {
175- buf = emalloc(dlen + 1 + nlen + 1);
176+ buf = emalloc(dlen + 1 + nlen + 1);
177 memcpy(buf, p, dlen);
178- buf[dlen] = '/';
179+ buf[dlen] = '/';
180 memcpy(buf + dlen + 1, name, nlen + 1);
181 if (stat(buf, &st) == 0 && S_ISREG(st.st_mode) && (st.st_mode & 0111))
182 return buf;
183@@ -178,13 +223,50 @@ wwhich(const char *name)
184 return NULL;
185 }
186
187-#if FEATURE_INPROC_EXEC
188-static int
189-call_builtin(const char *name, char *const *argv)
190+#if FEATURE_NOEXEC || FEATURE_NOFORK
191+static sigjmp_buf inproc_jmp;
192+static volatile sig_atomic_t inproc_active = 0;
193+static volatile int inproc_exit_status = 0;
194+
195+static void
196+inproc_sig_handler(int signo)
197+{
198+ if (inproc_active) {
199+ siglongjmp(inproc_jmp, signo);
200+ }
201+}
202+
203+int
204+wexec_call_builtin(const char *name, char *const *argv)
205 {
206- int i, argc;
207- char **p;
208+ struct sigaction sa, osa_int, osa_quit, osa_pipe;
209+ int i, argc, ret, sig;
210+ char **p;
211+
212+ sig = sigsetjmp(inproc_jmp, 1);
213+ if (sig != 0) {
214+ inproc_active = 0;
215+ sigaction(SIGINT, &osa_int, NULL);
216+ sigaction(SIGQUIT, &osa_quit, NULL);
217+ sigaction(SIGPIPE, &osa_pipe, NULL);
218+ if (sig == 1)
219+ return inproc_exit_status;
220+ kill(getpid(), sig);
221+ return 128 + sig;
222+ }
223+
224+ sa.sa_handler = inproc_sig_handler;
225+ sigemptyset(&sa.sa_mask);
226+ sa.sa_flags = 0;
227+ sigaction(SIGINT, &sa, &osa_int);
228+ sigaction(SIGQUIT, &sa, &osa_quit);
229
230+ sa.sa_handler = SIG_IGN;
231+ sigaction(SIGPIPE, &sa, &osa_pipe);
232+
233+ inproc_active = 1;
234+
235+ ret = -1;
236 argc = 0;
237 for (p = (char **)argv; *p; p++)
238 argc++;
239@@ -192,10 +274,20 @@ call_builtin(const char *name, char *const *argv)
240 if (strcmp(builtins[i].name, name) == 0) {
241 fflush(stdout);
242 fflush(stderr);
243- return builtins[i].fn(argc, (char **)argv);
244+ ret = builtins[i].fn(argc, (char **)argv);
245+ fflush(stdout);
246+ fflush(stderr);
247+ break;
248 }
249 }
250- return -1;
251+
252+ inproc_active = 0;
253+
254+ sigaction(SIGINT, &osa_int, NULL);
255+ sigaction(SIGQUIT, &osa_quit, NULL);
256+ sigaction(SIGPIPE, &osa_pipe, NULL);
257+
258+ return ret;
259 }
260 #endif
261
262@@ -211,6 +303,26 @@ fork_exec(const char *path, char *const *argv, int use_path)
263 return -1;
264 }
265 if (pid == 0) {
266+ const char *base = strrchr(path, '/');
267+ base = base ? base + 1 : path;
268+#if FEATURE_NOEXEC
269+ if (wexec_get_noexec() && wexec_is_builtin(base)) {
270+ struct sigaction sa;
271+ int i, ret;
272+ for (i = 1; i < NSIG; i++) {
273+ if (sigaction(i, NULL, &sa) == 0) {
274+ if (sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
275+ sa.sa_handler = SIG_DFL;
276+ sa.sa_flags = 0;
277+ sigemptyset(&sa.sa_mask);
278+ sigaction(i, &sa, NULL);
279+ }
280+ }
281+ }
282+ ret = wexec_call_builtin(base, argv);
283+ _exit(ret);
284+ }
285+#endif
286 if (use_path)
287 execvp(path, argv);
288 else
289@@ -231,11 +343,11 @@ fork_exec(const char *path, char *const *argv, int use_path)
290 int
291 wexecvp(const char *name, char *const *argv)
292 {
293-#if FEATURE_INPROC_EXEC
294- if (inproc_enabled && wexec_is_builtin(name)) {
295+#if FEATURE_NOFORK
296+ if (wexec_get_nofork() && wexec_is_builtin(name) && wexec_is_nofork(name)) {
297 fflush(stdout);
298 fflush(stderr);
299- return call_builtin(name, argv);
300+ return wexec_call_builtin(name, argv);
301 }
302 #endif
303 return fork_exec(name, argv, 1);
304@@ -244,15 +356,13 @@ wexecvp(const char *name, char *const *argv)
305 int
306 wexecv(const char *path, char *const *argv)
307 {
308-#if FEATURE_INPROC_EXEC
309- {
310- const char *base = strrchr(path, '/');
311- base = base ? base + 1 : path;
312- if (inproc_enabled && wexec_is_builtin(base)) {
313- fflush(stdout);
314- fflush(stderr);
315- return call_builtin(base, argv);
316- }
317+ const char *base = strrchr(path, '/');
318+ base = base ? base + 1 : path;
319+#if FEATURE_NOFORK
320+ if (wexec_get_nofork() && wexec_is_builtin(base) && wexec_is_nofork(base)) {
321+ fflush(stdout);
322+ fflush(stderr);
323+ return wexec_call_builtin(base, argv);
324 }
325 #endif
326 return fork_exec(path, argv, 0);
327@@ -265,6 +375,10 @@ wpopen(const char *name, char *const *argv, const char *mode)
328 pid_t pid;
329 int parent_end, child_end;
330 char *sh_argv[4];
331+#if FEATURE_NOEXEC
332+ struct sigaction sa;
333+ int i;
334+#endif
335
336 if (pipe(pfd) < 0) {
337 weprintf("pipe:");
338@@ -294,14 +408,27 @@ wpopen(const char *name, char *const *argv, const char *mode)
339 close(child_end);
340 }
341 if (argv) {
342-#if FEATURE_INPROC_EXEC
343- if (inproc_enabled && wexec_is_builtin(name)) {
344+#if FEATURE_NOEXEC
345+ if (wexec_get_noexec() && wexec_is_builtin(name)) {
346 int argc;
347 char **p;
348+
349+ /* reset caught signal handlers to default for inproc command */
350+ for (i = 1; i < NSIG; i++) {
351+ if (sigaction(i, NULL, &sa) == 0) {
352+ if (sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
353+ sa.sa_handler = SIG_DFL;
354+ sa.sa_flags = 0;
355+ sigemptyset(&sa.sa_mask);
356+ sigaction(i, &sa, NULL);
357+ }
358+ }
359+ }
360+
361 argc = 0;
362 for (p = (char **)argv; *p; p++)
363 argc++;
364- call_builtin(name, argv);
365+ wexec_call_builtin(name, argv);
366 fflush(stdout);
367 fflush(stderr);
368 _exit(0);
369@@ -314,9 +441,21 @@ wpopen(const char *name, char *const *argv, const char *mode)
370 sh_argv[1] = "-c";
371 sh_argv[2] = (char *)name;
372 sh_argv[3] = NULL;
373-#if FEATURE_INPROC_EXEC
374- if (inproc_enabled && wexec_is_builtin("sh")) {
375- call_builtin("sh", sh_argv);
376+#if FEATURE_NOEXEC
377+ if (wexec_get_noexec() && wexec_is_builtin("sh")) {
378+ /* reset caught signal handlers to default for inproc command */
379+ for (i = 1; i < NSIG; i++) {
380+ if (sigaction(i, NULL, &sa) == 0) {
381+ if (sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
382+ sa.sa_handler = SIG_DFL;
383+ sa.sa_flags = 0;
384+ sigemptyset(&sa.sa_mask);
385+ sigaction(i, &sa, NULL);
386+ }
387+ }
388+ }
389+
390+ wexec_call_builtin("sh", sh_argv);
391 fflush(stdout);
392 fflush(stderr);
393 _exit(0);
394@@ -372,14 +511,50 @@ wpclose(FILE *fp)
395 int
396 wsystem(const char *cmd)
397 {
398-#if FEATURE_INPROC_EXEC
399- if (inproc_enabled && wexec_is_builtin("sh")) {
400- char *sh_argv[4];
401- sh_argv[0] = "sh";
402- sh_argv[1] = "-c";
403- sh_argv[2] = (char *)cmd;
404- sh_argv[3] = NULL;
405- return wexecvp("sh", sh_argv);
406+#if FEATURE_NOEXEC
407+ if (wexec_get_noexec() && wexec_is_builtin("sh")) {
408+ pid_t pid;
409+ int st;
410+
411+ pid = fork();
412+ if (pid < 0) {
413+ weprintf("fork:");
414+ return -1;
415+ }
416+ if (pid == 0) {
417+ char *sh_argv[4];
418+ struct sigaction sa;
419+ int i;
420+ int ret;
421+
422+ /* reset caught signal handlers to default for inproc command */
423+ for (i = 1; i < NSIG; i++) {
424+ if (sigaction(i, NULL, &sa) == 0) {
425+ if (sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
426+ sa.sa_handler = SIG_DFL;
427+ sa.sa_flags = 0;
428+ sigemptyset(&sa.sa_mask);
429+ sigaction(i, &sa, NULL);
430+ }
431+ }
432+ }
433+
434+ sh_argv[0] = "sh";
435+ sh_argv[1] = "-c";
436+ sh_argv[2] = (char *)cmd;
437+ sh_argv[3] = NULL;
438+ ret = wexec_call_builtin("sh", sh_argv);
439+ _exit(ret);
440+ }
441+ if (waitpid(pid, &st, 0) < 0) {
442+ weprintf("waitpid:");
443+ return -1;
444+ }
445+ if (WIFEXITED(st))
446+ return WEXITSTATUS(st);
447+ if (WIFSIGNALED(st))
448+ return 128 + WTERMSIG(st);
449+ return -1;
450 }
451 #endif
452 {
453@@ -397,12 +572,27 @@ wsystem(const char *cmd)
454 void
455 wexecvp_self(const char *name, char *const *argv)
456 {
457-#if FEATURE_INPROC_EXEC
458- if (inproc_enabled && wexec_is_builtin(name)) {
459- int ret;
460+#if FEATURE_NOEXEC
461+ if (wexec_get_noexec() && wexec_is_builtin(name)) {
462+ struct sigaction sa;
463+ int i;
464+ int ret;
465+
466+ /* reset caught signal handlers to default for inproc command */
467+ for (i = 1; i < NSIG; i++) {
468+ if (sigaction(i, NULL, &sa) == 0) {
469+ if (sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
470+ sa.sa_handler = SIG_DFL;
471+ sa.sa_flags = 0;
472+ sigemptyset(&sa.sa_mask);
473+ sigaction(i, &sa, NULL);
474+ }
475+ }
476+ }
477+
478 fflush(stdout);
479 fflush(stderr);
480- ret = call_builtin(name, argv);
481+ ret = wexec_call_builtin(name, argv);
482 fflush(stdout);
483 fflush(stderr);
484 exit(ret);
485@@ -416,15 +606,30 @@ wexecvp_self(const char *name, char *const *argv)
486 void
487 wexecv_self(const char *path, char *const *argv)
488 {
489-#if FEATURE_INPROC_EXEC
490+#if FEATURE_NOEXEC
491 {
492 const char *base = strrchr(path, '/');
493 base = base ? base + 1 : path;
494- if (inproc_enabled && wexec_is_builtin(base)) {
495- int ret;
496+ if (wexec_get_noexec() && wexec_is_builtin(base)) {
497+ struct sigaction sa;
498+ int i;
499+ int ret;
500+
501+ /* reset caught signal handlers to default for inproc command */
502+ for (i = 1; i < NSIG; i++) {
503+ if (sigaction(i, NULL, &sa) == 0) {
504+ if (sa.sa_handler != SIG_IGN && sa.sa_handler != SIG_DFL) {
505+ sa.sa_handler = SIG_DFL;
506+ sa.sa_flags = 0;
507+ sigemptyset(&sa.sa_mask);
508+ sigaction(i, &sa, NULL);
509+ }
510+ }
511+ }
512+
513 fflush(stdout);
514 fflush(stderr);
515- ret = call_builtin(base, argv);
516+ ret = wexec_call_builtin(base, argv);
517 fflush(stdout);
518 fflush(stderr);
519 exit(ret);
520@@ -435,3 +640,17 @@ wexecv_self(const char *path, char *const *argv)
521 weprintf("execv %s:", path);
522 exit((errno == ENOENT) ? 127 : 126);
523 }
524+
525+void(exit)(int);
526+
527+void
528+wexec_exit(int code)
529+{
530+#if FEATURE_NOEXEC || FEATURE_NOFORK
531+ if (inproc_active) {
532+ inproc_exit_status = code;
533+ siglongjmp(inproc_jmp, 1);
534+ }
535+#endif
536+ (exit)(code);
537+}
1@@ -170,3 +170,9 @@ int net_del_route(const char *, const char *, const char *, const char *, int);
2 int net_flush_addrs(const char *);
3 int net_set_txqueuelen(const char *, int);
4 int net_set_name(const char *, const char *);
5+
6+#if FEATURE_NOEXEC || FEATURE_NOFORK
7+#include "wexec.h"
8+#define exit(code) wexec_exit(code)
9+#define _exit(code) wexec_exit(code)
10+#endif
1@@ -7,24 +7,33 @@
2
3 typedef int (*wexec_fn)(int argc, char **argv);
4
5-int wexec_register(const char *name, wexec_fn fn);
6-int wexec_is_builtin(const char *name);
7+int wexec_register(const char *name, wexec_fn fn);
8+int wexec_is_builtin(const char *name);
9+void wexec_register_env_lookup(char *(*fn)(const char *name));
10+#if FEATURE_NOEXEC || FEATURE_NOFORK
11+int wexec_call_builtin(const char *name, char *const *argv);
12+void wexec_exit(int code) __attribute__((noreturn));
13+#endif
14
15-/* search PATH for name; returns emalloc'd absolute path or NULL if not found.
16- * when FEATURE_INPROC_EXEC is compiled in and inproc dispatch is enabled,
17- * returns NULL for registered builtins (they have no filesystem path). */
18+/* search path for name, returns emallocd absolute path or null if not found
19+ * when feature_noexec is compiled in and builtin dispatch is enabled,
20+ * returns null for registered builtins (they have no filesystem path) */
21 char *wwhich(const char *name);
22
23-#if FEATURE_INPROC_EXEC
24-/* runtime toggle: disable to force fork+exec even when builtins are registered.
25- * when the box is also built with FEATURE_INPROC_EXEC_OVERRIDE=1,
26- * wexec_get_inproc() additionally honors ARUU_INPROC_EXEC=0 from the
27- * environment, read once on first use, so a builtin can be bypassed without
28- * a rebuild. without that second flag, inproc dispatch is hardwired on for
29- * the whole process and only an explicit wexec_set_inproc() call can
30- * change it. */
31-void wexec_set_inproc(int enabled);
32-int wexec_get_inproc(void);
33+int wexec_is_nofork(const char *name);
34+
35+#if FEATURE_NOEXEC
36+void wexec_set_noexec(int enabled);
37+int wexec_get_noexec(void);
38+#else
39+#define wexec_get_noexec() 0
40+#endif
41+
42+#if FEATURE_NOFORK
43+void wexec_set_nofork(int enabled);
44+int wexec_get_nofork(void);
45+#else
46+#define wexec_get_nofork() 0
47 #endif
48
49 int wexecvp(const char *name, char *const *argv);