aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Config.in280
-rw-r--r--Makefile6
-rw-r--r--Makefile.flags3
-rw-r--r--archival/bbunzip.c60
-rw-r--r--archival/bzip2.c11
-rw-r--r--archival/dpkg.c4
-rw-r--r--archival/gzip.c19
-rw-r--r--archival/libarchive/decompress_unxz.c16
-rw-r--r--archival/lzop.c53
-rw-r--r--archival/tar.c25
-rw-r--r--archival/unzip.c21
-rw-r--r--configs/TEST_nommu_defconfig1
-rw-r--r--configs/TEST_noprintf_defconfig1
-rw-r--r--configs/TEST_rh9_defconfig1
-rw-r--r--configs/android2_defconfig1
-rw-r--r--configs/android_502_defconfig1
-rw-r--r--configs/android_defconfig1
-rw-r--r--configs/android_ndk_defconfig1
-rw-r--r--configs/cygwin_defconfig1
-rw-r--r--configs/freebsd_defconfig1
-rw-r--r--coreutils/cat.c99
-rw-r--r--coreutils/dd.c7
-rw-r--r--coreutils/nl.c20
-rw-r--r--coreutils/printf.c4
-rw-r--r--coreutils/shuf.c2
-rw-r--r--coreutils/test.c33
-rw-r--r--coreutils/uname.c165
-rw-r--r--coreutils/uudecode.c12
-rwxr-xr-xexamples/mdev.conf.change_blockdev.sh2
-rwxr-xr-xexamples/var_service/dhcp_if_pinger/run2
-rw-r--r--include/libbb.h13
-rw-r--r--include/platform.h6
-rw-r--r--libbb/Config.src13
-rw-r--r--libbb/appletlib.c47
-rw-r--r--libbb/bb_getgroups.c47
-rw-r--r--libbb/common_bufsiz.c1
-rw-r--r--libbb/copyfd.c3
-rw-r--r--libbb/dump.c53
-rw-r--r--libbb/getopt32.c4
-rw-r--r--libbb/inet_common.c46
-rw-r--r--libbb/messages.c6
-rw-r--r--libbb/print_numbered_lines.c1
-rw-r--r--libbb/safe_write.c12
-rw-r--r--libbb/vfork_daemon_rexec.c5
-rw-r--r--loginutils/add-remove-shell.c2
-rwxr-xr-xmake_single_applets.sh6
-rw-r--r--miscutils/beep.c2
-rw-r--r--miscutils/crond.c140
-rw-r--r--miscutils/makedevs.c41
-rw-r--r--miscutils/ttysize.c7
-rw-r--r--miscutils/watchdog.c66
-rw-r--r--modutils/modutils.c6
-rw-r--r--modutils/modutils.h1
-rw-r--r--networking/inetd.c20
-rw-r--r--networking/libiproute/iproute.c26
-rw-r--r--networking/libiproute/iprule.c73
-rw-r--r--networking/tls.c4
-rw-r--r--networking/tls_aes.c4
-rw-r--r--networking/tls_pstm.c2
-rw-r--r--networking/tls_pstm_montgomery_reduce.c2
-rw-r--r--networking/tls_pstm_mul_comba.c5
-rw-r--r--networking/tls_pstm_sqr_comba.c8
-rw-r--r--networking/udhcp/common.c27
-rw-r--r--networking/udhcp/common.h19
-rw-r--r--networking/udhcp/d6_common.h39
-rw-r--r--networking/udhcp/d6_dhcpc.c248
-rw-r--r--networking/udhcp/dhcpc.c4
-rw-r--r--networking/udhcp/dhcpc.h1
-rw-r--r--networking/udhcp/dhcpd.c8
-rw-r--r--networking/udhcp/packet.c2
-rw-r--r--procps/pgrep.c38
-rw-r--r--procps/pstree.c2
-rwxr-xr-xscripts/randomtest6
-rwxr-xr-xscripts/randomtest.loop26
-rwxr-xr-xscripts/trylink18
-rw-r--r--shell/ash.c109
-rw-r--r--shell/ash_test/ash-heredoc/heredoc1.right6
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc1.tests12
-rw-r--r--shell/ash_test/ash-heredoc/heredoc3.right10
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc3.tests21
-rw-r--r--shell/ash_test/ash-heredoc/heredoc8.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc8.tests3
-rw-r--r--shell/ash_test/ash-heredoc/heredoc9.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc9.tests9
-rwxr-xr-xshell/ash_test/ash-misc/shift1.tests2
-rw-r--r--shell/ash_test/ash-misc/sigint1.right1
-rwxr-xr-xshell/ash_test/ash-misc/sigint1.tests41
-rwxr-xr-xshell/ash_test/ash-misc/tickquote1.tests2
-rw-r--r--shell/ash_test/ash-misc/wait6.right2
-rwxr-xr-xshell/ash_test/ash-misc/wait6.tests6
-rw-r--r--shell/ash_test/ash-parsing/and_or_and_backgrounding.right (renamed from shell/hush_test/hush-bugs/and_or_and_backgrounding.right)0
-rwxr-xr-xshell/ash_test/ash-parsing/and_or_and_backgrounding.tests (renamed from shell/hush_test/hush-bugs/and_or_and_backgrounding.tests)1
-rw-r--r--shell/ash_test/ash-quoting/quoted_punct.right35
-rwxr-xr-xshell/ash_test/ash-quoting/quoted_punct.tests41
-rw-r--r--shell/ash_test/ash-quoting/unicode_8x_chars.right6
-rwxr-xr-xshell/ash_test/ash-quoting/unicode_8x_chars.tests28
-rw-r--r--shell/ash_test/ash-redir/redir5.right6
-rwxr-xr-xshell/ash_test/ash-redir/redir5.tests16
-rw-r--r--shell/ash_test/ash-redir/redir_to_bad_fd.right2
-rwxr-xr-xshell/ash_test/ash-redir/redir_to_bad_fd.tests3
-rwxr-xr-xshell/ash_test/ash-vars/var-utf8-length.tests2
-rwxr-xr-xshell/ash_test/ash-vars/var_bash1a.tests2
-rw-r--r--shell/ash_test/ash-vars/var_bash1b.right23
-rwxr-xr-xshell/ash_test/ash-vars/var_bash1b.tests24
-rw-r--r--shell/ash_test/ash-vars/var_bash6.right5
-rwxr-xr-xshell/ash_test/ash-vars/var_bash6.tests9
-rwxr-xr-xshell/ash_test/run-all102
-rw-r--r--shell/hush.c739
-rw-r--r--shell/hush_test/hush-heredoc/heredoc8.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc8.tests3
-rw-r--r--shell/hush_test/hush-misc/errexit1.right1
-rwxr-xr-xshell/hush_test/hush-misc/errexit1.tests5
-rw-r--r--shell/hush_test/hush-misc/shift1.right10
-rwxr-xr-xshell/hush_test/hush-misc/shift1.tests10
-rw-r--r--shell/hush_test/hush-misc/sigint1.right1
-rwxr-xr-xshell/hush_test/hush-misc/sigint1.tests41
-rw-r--r--shell/hush_test/hush-misc/tickquote1.right1
-rwxr-xr-xshell/hush_test/hush-misc/tickquote1.tests2
-rw-r--r--shell/hush_test/hush-misc/wait6.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait6.tests6
-rw-r--r--shell/hush_test/hush-parsing/and_or_and_backgrounding.right4
-rwxr-xr-xshell/hush_test/hush-parsing/and_or_and_backgrounding.tests30
-rw-r--r--shell/hush_test/hush-quoting/quoted_punct.right35
-rwxr-xr-xshell/hush_test/hush-quoting/quoted_punct.tests41
-rw-r--r--shell/hush_test/hush-quoting/unicode_8x_chars.right6
-rwxr-xr-xshell/hush_test/hush-quoting/unicode_8x_chars.tests28
-rw-r--r--shell/hush_test/hush-redir/redir3.right16
-rwxr-xr-xshell/hush_test/hush-redir/redir3.tests14
-rw-r--r--shell/hush_test/hush-redir/redir_errors.right14
-rwxr-xr-xshell/hush_test/hush-redir/redir_errors.tests9
-rw-r--r--shell/hush_test/hush-redir/redir_to_bad_fd.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir_to_bad_fd.tests3
-rw-r--r--shell/hush_test/hush-signals/signal4.right4
-rwxr-xr-xshell/hush_test/hush-signals/signal4.tests5
-rw-r--r--shell/hush_test/hush-signals/signal8.right3
-rwxr-xr-xshell/hush_test/hush-signals/signal8.tests18
-rw-r--r--shell/hush_test/hush-signals/signal9.right3
-rwxr-xr-xshell/hush_test/hush-signals/signal9.tests21
-rw-r--r--shell/hush_test/hush-vars/readonly0.right20
-rwxr-xr-xshell/hush_test/hush-vars/readonly0.tests42
-rw-r--r--shell/hush_test/hush-vars/readonly2.right5
-rwxr-xr-xshell/hush_test/hush-vars/readonly2.tests7
-rw-r--r--shell/hush_test/hush-vars/var-utf8-length.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-utf8-length.tests4
-rw-r--r--shell/hush_test/hush-vars/var_bash1a.right6
-rwxr-xr-xshell/hush_test/hush-vars/var_bash1a.tests11
-rw-r--r--shell/hush_test/hush-vars/var_bash1b.right23
-rwxr-xr-xshell/hush_test/hush-vars/var_bash1b.tests24
-rwxr-xr-xshell/hush_test/run-all51
-rw-r--r--shell/shell_common.c4
-rw-r--r--testsuite/msh/msh-supports-underscores-in-variable-names1
-rw-r--r--util-linux/blkdiscard.c7
-rw-r--r--util-linux/fdisk.c3
-rwxr-xr-xutil-linux/mkfs_ext2_test.sh12
-rw-r--r--util-linux/mount.c3
-rw-r--r--util-linux/setpriv.c451
-rw-r--r--util-linux/unshare.c7
157 files changed, 3033 insertions, 1220 deletions
diff --git a/Config.in b/Config.in
index 086812908..0353a0525 100644
--- a/Config.in
+++ b/Config.in
@@ -26,10 +26,14 @@ endchoice
26menu "Busybox Settings" 26menu "Busybox Settings"
27 27
28config DESKTOP 28config DESKTOP
29 bool "Enable options for full-blown desktop systems" 29 bool "Enable compatibility for full-blown desktop systems"
30 default y 30 default y
31 help 31 help
32 Enable options and features which are not essential. 32 Enable applet options and features which are not essential.
33 Many applet options have dedicated config options to (de)select them
34 under that applet; this options enables those options which have no
35 individual config item for them.
36
33 Select this if you plan to use busybox on full-blown desktop machine 37 Select this if you plan to use busybox on full-blown desktop machine
34 with common Linux distro, which needs higher level of command-line 38 with common Linux distro, which needs higher level of command-line
35 compatibility. 39 compatibility.
@@ -56,13 +60,12 @@ config INCLUDE_SUSv2
56 will be supported in head, tail, and fold. (Note: should 60 will be supported in head, tail, and fold. (Note: should
57 affect renice too.) 61 affect renice too.)
58 62
59config USE_PORTABLE_CODE 63config LONG_OPTS
60 bool "Avoid using GCC-specific code constructs" 64 bool "Support --long-options"
61 default n 65 default y
62 help 66 help
63 Use this option if you are trying to compile busybox with 67 Enable this if you want busybox applets to use the gnu --long-option
64 compiler other than gcc. 68 style, in addition to single character -a -b -c style options.
65 If you do use gcc, this option may needlessly increase code size.
66 69
67config SHOW_USAGE 70config SHOW_USAGE
68 bool "Show applet usage messages" 71 bool "Show applet usage messages"
@@ -98,33 +101,38 @@ config FEATURE_COMPRESS_USAGE
98 and have very little memory, this might not be a win. Otherwise, 101 and have very little memory, this might not be a win. Otherwise,
99 you probably want this. 102 you probably want this.
100 103
101config BUSYBOX 104config LFS
102 bool "Include busybox applet" 105 bool "Support files > 2 GB"
103 default y 106 default y
104 help 107 help
105 The busybox applet provides general help regarding busybox and 108 If you want to build BusyBox with large file support, then enable
106 allows the included applets to be listed. It's also required 109 this option. This will have no effect if your kernel or your C
107 if applet links are to be installed at runtime. 110 library lacks large file support for large files. Some of the
108 111 programs that can benefit from large file support include dd, gzip,
109 If you can live without these features disabling this will save 112 cp, mount, tar, and many others. If you want to access files larger
110 some space. 113 than 2 Gigabytes, enable this option.
111 114
112config FEATURE_INSTALLER 115config GLOBBING
113 bool "Support --install [-s] to install applet links at runtime" 116 bool "Allow busybox.exe to expand wildcards"
114 default y 117 default n
115 depends on BUSYBOX 118 depends on PLATFORM_MINGW32
116 help 119 help
117 Enable 'busybox --install [-s]' support. This will allow you to use 120 In Microsoft Windows expansion of wildcards on the command line
118 busybox at runtime to create hard links or symlinks for all the 121 ('globbing') is handled by the C runtime while the BusyBox shell
119 applets that are compiled into busybox. 122 does its own wildcard expansion. For best results when using the
123 shell globbing by the C runtime should be turned off. If you want
124 the BusyBox binary to handle wildcard expansion using the C runtime
125 set this to 'Y'.
120 126
121config INSTALL_NO_USR 127config SAFE_ENV
122 bool "Don't use /usr" 128 bool "Manipulate the environment through safe API calls"
123 default n 129 default n
130 depends on PLATFORM_MINGW32
124 help 131 help
125 Disable use of /usr. busybox --install and "make install" 132 Enable this option to use safe API calls when clearing environment
126 will install applets only to /bin and /sbin, 133 variables. This is necessary if BusyBox is to run on ReactOS or
127 never to /usr/bin or /usr/sbin. 134 64-bit Windows. The default is 'N', which must be used if BusyBox
135 is to run on Windows XP.
128 136
129config PAM 137config PAM
130 bool "Support PAM (Pluggable Authentication Modules)" 138 bool "Support PAM (Pluggable Authentication Modules)"
@@ -133,13 +141,6 @@ config PAM
133 Use PAM in some busybox applets (currently login and httpd) instead 141 Use PAM in some busybox applets (currently login and httpd) instead
134 of direct access to password database. 142 of direct access to password database.
135 143
136config LONG_OPTS
137 bool "Support --long-options"
138 default y
139 help
140 Enable this if you want busybox applets to use the gnu --long-option
141 style, in addition to single character -a -b -c style options.
142
143config FEATURE_DEVPTS 144config FEATURE_DEVPTS
144 bool "Use the devpts filesystem for Unix98 PTYs" 145 bool "Use the devpts filesystem for Unix98 PTYs"
145 default y 146 default y
@@ -150,18 +151,6 @@ config FEATURE_DEVPTS
150 /dev/ttyp<number> will be used. To use this option, you should have 151 /dev/ttyp<number> will be used. To use this option, you should have
151 devpts mounted. 152 devpts mounted.
152 153
153config FEATURE_CLEAN_UP
154 bool "Clean up all memory before exiting (usually not needed)"
155 default n
156 help
157 As a size optimization, busybox normally exits without explicitly
158 freeing dynamically allocated memory or closing files. This saves
159 space since the OS will clean up for us, but it can confuse debuggers
160 like valgrind, which report tons of memory and resource leaks.
161
162 Don't enable this unless you have a really good reason to clean
163 things up manually.
164
165config FEATURE_UTMP 154config FEATURE_UTMP
166 bool "Support utmp file" 155 bool "Support utmp file"
167 default y 156 default y
@@ -191,7 +180,7 @@ config FEATURE_PIDFILE
191 on applets which require pidfiles to run. 180 on applets which require pidfiles to run.
192 181
193config PID_FILE_PATH 182config PID_FILE_PATH
194 string "Path to directory for pidfile" 183 string "Directory for pidfiles"
195 default "/var/run" 184 default "/var/run"
196 depends on FEATURE_PIDFILE 185 depends on FEATURE_PIDFILE
197 help 186 help
@@ -200,8 +189,40 @@ config PID_FILE_PATH
200 this value. The option has no effect on applets that require you to 189 this value. The option has no effect on applets that require you to
201 specify a pidfile path. 190 specify a pidfile path.
202 191
192config BUSYBOX
193 bool "Include busybox applet"
194 default y
195 help
196 The busybox applet provides general help regarding busybox and
197 allows the included applets to be listed. It's also required
198 if applet links are to be installed at runtime. If you unselect
199 this option, running busybox without any arguments will give
200 just a cryptic error message:
201
202 $ busybox
203 busybox: applet not found
204
205 Running "busybox APPLET [ARGS...]" will still work, of course.
206
207config FEATURE_INSTALLER
208 bool "Support --install [-s] to install applet links at runtime"
209 default y
210 depends on BUSYBOX
211 help
212 Enable 'busybox --install [-s]' support. This will allow you to use
213 busybox at runtime to create hard links or symlinks for all the
214 applets that are compiled into busybox.
215
216config INSTALL_NO_USR
217 bool "Don't use /usr"
218 default n
219 help
220 Disable use of /usr. busybox --install and "make install"
221 will install applets only to /bin and /sbin,
222 never to /usr/bin or /usr/sbin.
223
203config FEATURE_SUID 224config FEATURE_SUID
204 bool "Support SUID/SGID handling" 225 bool "Drop SUID state for most applets"
205 default y 226 default y
206 help 227 help
207 With this option you can install the busybox binary belonging 228 With this option you can install the busybox binary belonging
@@ -209,16 +230,16 @@ config FEATURE_SUID
209 root-level operations even when run by ordinary users 230 root-level operations even when run by ordinary users
210 (for example, mounting of user mounts in fstab needs this). 231 (for example, mounting of user mounts in fstab needs this).
211 232
212 Busybox will automatically drop privileges for applets 233 With this option enabled, Busybox drops privileges for applets
213 that don't need root access. 234 that don't need root access, before entering their main() function.
214 235
215 If you are really paranoid and don't want to do this, build two 236 If you are really paranoid and don't want even initial busybox code
216 busybox binaries with different applets in them (and the appropriate 237 to run under root for evey applet, build two busybox binaries with
217 symlinks pointing to each binary), and only set the suid bit on the 238 different applets in them (and the appropriate symlinks pointing
218 one that needs it. 239 to each binary), and only set the suid bit on the one that needs it.
219 240
220 The applets which require root rights (need suid bit or 241 Some applets which require root rights (need suid bit on the binary
221 to be run by root) and will refuse to execute otherwise: 242 or to be run by root) and will refuse to execute otherwise:
222 crontab, login, passwd, su, vlock, wall. 243 crontab, login, passwd, su, vlock, wall.
223 244
224 The applets which will use root rights if they have them 245 The applets which will use root rights if they have them
@@ -226,16 +247,16 @@ config FEATURE_SUID
226 without root right nevertheless: 247 without root right nevertheless:
227 findfs, ping[6], traceroute[6], mount. 248 findfs, ping[6], traceroute[6], mount.
228 249
229 Note that if you DONT select this option, but DO make busybox 250 Note that if you DO NOT select this option, but DO make busybox
230 suid root, ALL applets will run under root, which is a huge 251 suid root, ALL applets will run under root, which is a huge
231 security hole (think "cp /some/file /etc/passwd"). 252 security hole (think "cp /some/file /etc/passwd").
232 253
233config FEATURE_SUID_CONFIG 254config FEATURE_SUID_CONFIG
234 bool "Runtime SUID/SGID configuration via /etc/busybox.conf" 255 bool "Enable SUID configuration via /etc/busybox.conf"
235 default y 256 default y
236 depends on FEATURE_SUID 257 depends on FEATURE_SUID
237 help 258 help
238 Allow the SUID / SGID state of an applet to be determined at runtime 259 Allow the SUID/SGID state of an applet to be determined at runtime
239 by checking /etc/busybox.conf. (This is sort of a poor man's sudo.) 260 by checking /etc/busybox.conf. (This is sort of a poor man's sudo.)
240 The format of this file is as follows: 261 The format of this file is as follows:
241 262
@@ -255,7 +276,7 @@ config FEATURE_SUID_CONFIG
255 276
256 [SUID] 277 [SUID]
257 su = ssx root.0 # applet su can be run by anyone and runs with 278 su = ssx root.0 # applet su can be run by anyone and runs with
258 # euid=0/egid=0 279 # euid=0,egid=0
259 su = ssx # exactly the same 280 su = ssx # exactly the same
260 281
261 mount = sx- root.disk # applet mount can be run by root and members 282 mount = sx- root.disk # applet mount can be run by root and members
@@ -283,24 +304,6 @@ config FEATURE_SUID_CONFIG_QUIET
283 check this option to avoid users to be notified about missing 304 check this option to avoid users to be notified about missing
284 permissions. 305 permissions.
285 306
286config SELINUX
287 bool "Support NSA Security Enhanced Linux"
288 default n
289 select PLATFORM_LINUX
290 help
291 Enable support for SELinux in applets ls, ps, and id. Also provide
292 the option of compiling in SELinux applets.
293
294 If you do not have a complete SELinux userland installed, this stuff
295 will not compile. Specifially, libselinux 1.28 or better is
296 directly required by busybox. If the installation is located in a
297 non-standard directory, provide it by invoking make as follows:
298 CFLAGS=-I<libselinux-include-path> \
299 LDFLAGS=-L<libselinux-lib-path> \
300 make
301
302 Most people will leave this set to 'N'.
303
304config FEATURE_PREFER_APPLETS 307config FEATURE_PREFER_APPLETS
305 bool "exec prefers applets" 308 bool "exec prefers applets"
306 default n 309 default n
@@ -309,8 +312,9 @@ config FEATURE_PREFER_APPLETS
309 call 'exec' to try and find an applicable busybox applet before 312 call 'exec' to try and find an applicable busybox applet before
310 searching the PATH. This is typically done by exec'ing 313 searching the PATH. This is typically done by exec'ing
311 /proc/self/exe. 314 /proc/self/exe.
315
312 This may affect shell, find -exec, xargs and similar applets. 316 This may affect shell, find -exec, xargs and similar applets.
313 They will use applets even if /bin/<applet> -> busybox link 317 They will use applets even if /bin/APPLET -> busybox link
314 is missing (or is not a link to busybox). However, this causes 318 is missing (or is not a link to busybox). However, this causes
315 problems in chroot jails without mounted /proc and with ps/top 319 problems in chroot jails without mounted /proc and with ps/top
316 (command name can be shown as 'exe' for applets started this way). 320 (command name can be shown as 'exe' for applets started this way).
@@ -325,6 +329,37 @@ config BUSYBOX_EXEC_PATH
325 executable. If you haven't got /proc, set this to wherever you 329 executable. If you haven't got /proc, set this to wherever you
326 want to run BusyBox from. 330 want to run BusyBox from.
327 331
332config SELINUX
333 bool "Support NSA Security Enhanced Linux"
334 default n
335 select PLATFORM_LINUX
336 help
337 Enable support for SELinux in applets ls, ps, and id. Also provide
338 the option of compiling in SELinux applets.
339
340 If you do not have a complete SELinux userland installed, this stuff
341 will not compile. Specifially, libselinux 1.28 or better is
342 directly required by busybox. If the installation is located in a
343 non-standard directory, provide it by invoking make as follows:
344
345 CFLAGS=-I<libselinux-include-path> \
346 LDFLAGS=-L<libselinux-lib-path> \
347 make
348
349 Most people will leave this set to 'N'.
350
351config FEATURE_CLEAN_UP
352 bool "Clean up all memory before exiting (usually not needed)"
353 default n
354 help
355 As a size optimization, busybox normally exits without explicitly
356 freeing dynamically allocated memory or closing files. This saves
357 space since the OS will clean up for us, but it can confuse debuggers
358 like valgrind, which report tons of memory and resource leaks.
359
360 Don't enable this unless you have a really good reason to clean
361 things up manually.
362
328# These are auto-selected by other options 363# These are auto-selected by other options
329 364
330config FEATURE_SYSLOG 365config FEATURE_SYSLOG
@@ -400,29 +435,25 @@ config BUILD_LIBBUSYBOX
400 Build a shared library libbusybox.so.N.N.N which contains all 435 Build a shared library libbusybox.so.N.N.N which contains all
401 busybox code. 436 busybox code.
402 437
403 This feature allows every applet to be built as a tiny 438 This feature allows every applet to be built as a really tiny
404 separate executable. Enabling it for "one big busybox binary" 439 separate executable linked against the library:
405 approach serves no purpose and increases code size. 440 $ size 0_lib/l*
406 You should almost certainly say "no" to this. 441 text data bss dec hex filename
442 939 212 28 1179 49b 0_lib/last
443 939 212 28 1179 49b 0_lib/less
444 919138 8328 1556 929022 e2cfe 0_lib/libbusybox.so.1.N.M
407 445
408### config FEATURE_FULL_LIBBUSYBOX 446 This is useful on NOMMU systems which are not capable
409### bool "Feature-complete libbusybox" 447 of sharing executables, but are capable of sharing code
410### default n if !FEATURE_SHARED_BUSYBOX 448 in dynamic libraries.
411### depends on BUILD_LIBBUSYBOX 449
412### help 450config FEATURE_LIBBUSYBOX_STATIC
413### Build a libbusybox with the complete feature-set, disregarding 451 bool "Pull in all external references into libbusybox"
414### the actually selected config. 452 default n
415### 453 depends on BUILD_LIBBUSYBOX
416### Normally, libbusybox will only contain the features which are 454 help
417### used by busybox itself. If you plan to write a separate 455 Make libbusybox library independent, not using or requiring
418### standalone application which uses libbusybox say 'Y'. 456 any other shared libraries.
419###
420### Note: libbusybox is GPL, not LGPL, and exports no stable API that
421### might act as a copyright barrier. We can and will modify the
422### exported function set between releases (even minor version number
423### changes), and happily break out-of-tree features.
424###
425### Say 'N' if in doubt.
426 457
427config FEATURE_INDIVIDUAL 458config FEATURE_INDIVIDUAL
428 bool "Produce a binary for each applet, linked against libbusybox" 459 bool "Produce a binary for each applet, linked against libbusybox"
@@ -470,39 +501,6 @@ config FEATURE_SHARED_BUSYBOX
470### 501###
471### Say 'N' unless you know what you are doing. 502### Say 'N' unless you know what you are doing.
472 503
473config LFS
474 bool "Build with Large File Support (for accessing files > 2 GB)"
475 default y
476 help
477 If you want to build BusyBox with large file support, then enable
478 this option. This will have no effect if your kernel or your C
479 library lacks large file support for large files. Some of the
480 programs that can benefit from large file support include dd, gzip,
481 cp, mount, tar, and many others. If you want to access files larger
482 than 2 Gigabytes, enable this option. Otherwise, leave it set to 'N'.
483
484config GLOBBING
485 bool "Allow busybox.exe to expand wildcards"
486 default n
487 depends on PLATFORM_MINGW32
488 help
489 In Microsoft Windows expansion of wildcards on the command line
490 ('globbing') is handled by the C runtime while the BusyBox shell
491 does its own wildcard expansion. For best results when using the
492 shell globbing by the C runtime should be turned off. If you want
493 the BusyBox binary to handle wildcard expansion using the C runtime
494 set this to 'Y'.
495
496config SAFE_ENV
497 bool "Manipulate the environment through safe API calls"
498 default n
499 depends on PLATFORM_MINGW32
500 help
501 Enable this option to use safe API calls when clearing environment
502 variables. This is necessary if BusyBox is to run on ReactOS or
503 64-bit Windows. The default is 'N', which must be used if BusyBox
504 is to run on Windows XP.
505
506config CROSS_COMPILER_PREFIX 504config CROSS_COMPILER_PREFIX
507 string "Cross Compiler prefix" 505 string "Cross Compiler prefix"
508 default "" 506 default ""
@@ -549,6 +547,14 @@ config EXTRA_LDLIBS
549 help 547 help
550 Additional LDLIBS to pass to the linker with -l. 548 Additional LDLIBS to pass to the linker with -l.
551 549
550config USE_PORTABLE_CODE
551 bool "Avoid using GCC-specific code constructs"
552 default n
553 help
554 Use this option if you are trying to compile busybox with
555 compiler other than gcc.
556 If you do use gcc, this option may needlessly increase code size.
557
552comment 'Installation Options ("make install" behavior)' 558comment 'Installation Options ("make install" behavior)'
553 559
554choice 560choice
@@ -709,10 +715,10 @@ config EFENCE
709 715
710endchoice 716endchoice
711 717
712endmenu
713
714source libbb/Config.in 718source libbb/Config.in
715 719
720endmenu
721
716comment "Applets" 722comment "Applets"
717 723
718source archival/Config.in 724source archival/Config.in
diff --git a/Makefile b/Makefile
index e68958092..8f3bc7ec0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
1VERSION = 1 1VERSION = 1
2PATCHLEVEL = 27 2PATCHLEVEL = 28
3SUBLEVEL = 0 3SUBLEVEL = 0
4EXTRAVERSION = .git 4EXTRAVERSION = .git
5NAME = Unnamed 5NAME = Unnamed
@@ -510,6 +510,8 @@ ifeq ($(dot-config),1)
510# To avoid any implicit rule to kick in, define an empty command 510# To avoid any implicit rule to kick in, define an empty command
511.config .kconfig.d: ; 511.config .kconfig.d: ;
512 512
513-include $(srctree)/arch/$(ARCH)/Makefile
514
513# Now we can define CFLAGS etc according to .config 515# Now we can define CFLAGS etc according to .config
514include $(srctree)/Makefile.flags 516include $(srctree)/Makefile.flags
515 517
@@ -533,8 +535,6 @@ endif
533# Defaults busybox but it is usually overridden in the arch makefile 535# Defaults busybox but it is usually overridden in the arch makefile
534all: busybox$(EXEEXT) doc 536all: busybox$(EXEEXT) doc
535 537
536-include $(srctree)/arch/$(ARCH)/Makefile
537
538# arch Makefile may override CC so keep this after arch Makefile is included 538# arch Makefile may override CC so keep this after arch Makefile is included
539#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) 539#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
540CHECKFLAGS += $(NOSTDINC_FLAGS) 540CHECKFLAGS += $(NOSTDINC_FLAGS)
diff --git a/Makefile.flags b/Makefile.flags
index 6b25632a0..6bb6a8fb1 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -15,8 +15,7 @@ CPPFLAGS += \
15 -include include/autoconf.h \ 15 -include include/autoconf.h \
16 -D_GNU_SOURCE -DNDEBUG \ 16 -D_GNU_SOURCE -DNDEBUG \
17 $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \ 17 $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \
18 -D"BB_VER=KBUILD_STR($(BB_VER))" -DBB_BT=AUTOCONF_TIMESTAMP \ 18 -D"BB_VER=KBUILD_STR($(BB_VER))" -D"MINGW_VER=KBUILD_STR($(MINGW_VER))"
19 -D"MINGW_VER=KBUILD_STR($(MINGW_VER))"
20 19
21CFLAGS += $(call cc-option,-Wall,) 20CFLAGS += $(call cc-option,-Wall,)
22CFLAGS += $(call cc-option,-Wshadow,) 21CFLAGS += $(call cc-option,-Wshadow,)
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index 311c7333e..1e3d6e586 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -7,24 +7,30 @@
7#include "libbb.h" 7#include "libbb.h"
8#include "bb_archive.h" 8#include "bb_archive.h"
9 9
10//kbuild:lib-$(CONFIG_ZCAT) += bbunzip.o
11//kbuild:lib-$(CONFIG_GUNZIP) += bbunzip.o
12//kbuild:lib-$(CONFIG_BZCAT) += bbunzip.o
13//kbuild:lib-$(CONFIG_BUNZIP2) += bbunzip.o
14
10/* lzop_main() uses bbunpack(), need this: */ 15/* lzop_main() uses bbunpack(), need this: */
11//kbuild:lib-$(CONFIG_LZOP) += bbunzip.o 16//kbuild:lib-$(CONFIG_LZOP) += bbunzip.o
12//kbuild:lib-$(CONFIG_LZOPCAT) += bbunzip.o 17//kbuild:lib-$(CONFIG_LZOPCAT) += bbunzip.o
13//kbuild:lib-$(CONFIG_UNLZOP) += bbunzip.o 18//kbuild:lib-$(CONFIG_UNLZOP) += bbunzip.o
14/* bzip2_main() too: */ 19/* bzip2_main() too: */
15//kbuild:lib-$(CONFIG_FEATURE_BZIP2_DECOMPRESS) += bbunzip.o 20//kbuild:lib-$(CONFIG_BZIP2) += bbunzip.o
16/* gzip_main() too: */ 21/* gzip_main() too: */
17//kbuild:lib-$(CONFIG_FEATURE_GZIP_DECOMPRESS) += bbunzip.o 22//kbuild:lib-$(CONFIG_GZIP) += bbunzip.o
18 23
19/* Note: must be kept in sync with archival/lzop.c */ 24/* Note: must be kept in sync with archival/lzop.c */
20enum { 25enum {
21 OPT_STDOUT = 1 << 0, 26 OPT_STDOUT = 1 << 0,
22 OPT_FORCE = 1 << 1, 27 OPT_FORCE = 1 << 1,
23 /* only some decompressors: */ 28 /* only some decompressors: */
24 OPT_VERBOSE = 1 << 2, 29 OPT_KEEP = 1 << 2,
25 OPT_QUIET = 1 << 3, 30 OPT_VERBOSE = 1 << 3,
26 OPT_DECOMPRESS = 1 << 4, 31 OPT_QUIET = 1 << 4,
27 OPT_TEST = 1 << 5, 32 OPT_DECOMPRESS = 1 << 5,
33 OPT_TEST = 1 << 6,
28 SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION, 34 SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION,
29}; 35};
30 36
@@ -182,10 +188,13 @@ int FAST_FUNC bbunpack(char **argv,
182 } 188 }
183 /* Delete _source_ file */ 189 /* Delete _source_ file */
184 del = filename; 190 del = filename;
191 if (option_mask32 & OPT_KEEP) /* ... unless -k */
192 del = NULL;
185 } 193 }
186 if (ENABLE_PLATFORM_MINGW32) 194 if (ENABLE_PLATFORM_MINGW32)
187 xclose(STDIN_FILENO); 195 xclose(STDIN_FILENO);
188 xunlink(del); 196 if (del)
197 xunlink(del);
189 free_name: 198 free_name:
190 if (new_name != filename) 199 if (new_name != filename)
191 free(new_name); 200 free(new_name);
@@ -242,7 +251,16 @@ char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
242int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 251int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
243int uncompress_main(int argc UNUSED_PARAM, char **argv) 252int uncompress_main(int argc UNUSED_PARAM, char **argv)
244{ 253{
254// (N)compress 4.2.4.4:
255// -d If given, decompression is done instead
256// -c Write output on stdout, don't remove original
257// -b Parameter limits the max number of bits/code
258// -f Forces output file to be generated
259// -v Write compression statistics
260// -V Output vesion and compile options
261// -r Recursive. If a filename is a directory, descend into it and compress everything
245 getopt32(argv, "cf"); 262 getopt32(argv, "cf");
263
246 argv += optind; 264 argv += optind;
247 265
248 return bbunpack(argv, unpack_Z_stream, make_new_name_generic, "Z"); 266 return bbunpack(argv, unpack_Z_stream, make_new_name_generic, "Z");
@@ -275,11 +293,12 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
275 * Ken Turkowski, Dave Mack and Peter Jannesen. 293 * Ken Turkowski, Dave Mack and Peter Jannesen.
276 */ 294 */
277//usage:#define gunzip_trivial_usage 295//usage:#define gunzip_trivial_usage
278//usage: "[-cft] [FILE]..." 296//usage: "[-cfkt] [FILE]..."
279//usage:#define gunzip_full_usage "\n\n" 297//usage:#define gunzip_full_usage "\n\n"
280//usage: "Decompress FILEs (or stdin)\n" 298//usage: "Decompress FILEs (or stdin)\n"
281//usage: "\n -c Write to stdout" 299//usage: "\n -c Write to stdout"
282//usage: "\n -f Force" 300//usage: "\n -f Force"
301//usage: "\n -k Keep input files"
283//usage: "\n -t Test file integrity" 302//usage: "\n -t Test file integrity"
284//usage: 303//usage:
285//usage:#define gunzip_example_usage 304//usage:#define gunzip_example_usage
@@ -374,7 +393,7 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
374#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS 393#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS
375 applet_long_options = gunzip_longopts; 394 applet_long_options = gunzip_longopts;
376#endif 395#endif
377 getopt32(argv, "cfvqdtn"); 396 getopt32(argv, "cfkvqdtn");
378 argv += optind; 397 argv += optind;
379 398
380 /* If called as zcat... 399 /* If called as zcat...
@@ -396,11 +415,12 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
396 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 415 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
397 */ 416 */
398//usage:#define bunzip2_trivial_usage 417//usage:#define bunzip2_trivial_usage
399//usage: "[-cf] [FILE]..." 418//usage: "[-cfk] [FILE]..."
400//usage:#define bunzip2_full_usage "\n\n" 419//usage:#define bunzip2_full_usage "\n\n"
401//usage: "Decompress FILEs (or stdin)\n" 420//usage: "Decompress FILEs (or stdin)\n"
402//usage: "\n -c Write to stdout" 421//usage: "\n -c Write to stdout"
403//usage: "\n -f Force" 422//usage: "\n -f Force"
423//usage: "\n -k Keep input files"
404//usage:#define bzcat_trivial_usage 424//usage:#define bzcat_trivial_usage
405//usage: "[FILE]..." 425//usage: "[FILE]..."
406//usage:#define bzcat_full_usage "\n\n" 426//usage:#define bzcat_full_usage "\n\n"
@@ -430,11 +450,11 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
430//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP)) 450//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
431// APPLET_ODDNAME:name main location suid_type help 451// APPLET_ODDNAME:name main location suid_type help
432//applet:IF_BZCAT(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat)) 452//applet:IF_BZCAT(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
433#if ENABLE_FEATURE_BZIP2_DECOMPRESS 453#if ENABLE_FEATURE_BZIP2_DECOMPRESS || ENABLE_BUNZIP2 || ENABLE_BZCAT
434int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 454int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
435int bunzip2_main(int argc UNUSED_PARAM, char **argv) 455int bunzip2_main(int argc UNUSED_PARAM, char **argv)
436{ 456{
437 getopt32(argv, "cfvqdt"); 457 getopt32(argv, "cfkvqdt");
438 argv += optind; 458 argv += optind;
439 if (ENABLE_BZCAT && applet_name[2] == 'c') /* bzcat */ 459 if (ENABLE_BZCAT && applet_name[2] == 'c') /* bzcat */
440 option_mask32 |= OPT_STDOUT; 460 option_mask32 |= OPT_STDOUT;
@@ -453,19 +473,21 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
453 * Licensed under GPLv2, see file LICENSE in this source tree. 473 * Licensed under GPLv2, see file LICENSE in this source tree.
454 */ 474 */
455//usage:#define unlzma_trivial_usage 475//usage:#define unlzma_trivial_usage
456//usage: "[-cf] [FILE]..." 476//usage: "[-cfk] [FILE]..."
457//usage:#define unlzma_full_usage "\n\n" 477//usage:#define unlzma_full_usage "\n\n"
458//usage: "Decompress FILE (or stdin)\n" 478//usage: "Decompress FILE (or stdin)\n"
459//usage: "\n -c Write to stdout" 479//usage: "\n -c Write to stdout"
460//usage: "\n -f Force" 480//usage: "\n -f Force"
481//usage: "\n -k Keep input files"
461//usage: 482//usage:
462//usage:#define lzma_trivial_usage 483//usage:#define lzma_trivial_usage
463//usage: "-d [-cf] [FILE]..." 484//usage: "-d [-cfk] [FILE]..."
464//usage:#define lzma_full_usage "\n\n" 485//usage:#define lzma_full_usage "\n\n"
465//usage: "Decompress FILE (or stdin)\n" 486//usage: "Decompress FILE (or stdin)\n"
466//usage: "\n -d Decompress" 487//usage: "\n -d Decompress"
467//usage: "\n -c Write to stdout" 488//usage: "\n -c Write to stdout"
468//usage: "\n -f Force" 489//usage: "\n -f Force"
490//usage: "\n -k Keep input files"
469//usage: 491//usage:
470//usage:#define lzcat_trivial_usage 492//usage:#define lzcat_trivial_usage
471//usage: "[FILE]..." 493//usage: "[FILE]..."
@@ -522,7 +544,7 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
522int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 544int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
523int unlzma_main(int argc UNUSED_PARAM, char **argv) 545int unlzma_main(int argc UNUSED_PARAM, char **argv)
524{ 546{
525 IF_LZMA(int opts =) getopt32(argv, "cfvqdt"); 547 IF_LZMA(int opts =) getopt32(argv, "cfkvqdt");
526# if ENABLE_LZMA 548# if ENABLE_LZMA
527 /* lzma without -d or -t? */ 549 /* lzma without -d or -t? */
528 if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST))) 550 if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
@@ -539,19 +561,21 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
539 561
540 562
541//usage:#define unxz_trivial_usage 563//usage:#define unxz_trivial_usage
542//usage: "[-cf] [FILE]..." 564//usage: "[-cfk] [FILE]..."
543//usage:#define unxz_full_usage "\n\n" 565//usage:#define unxz_full_usage "\n\n"
544//usage: "Decompress FILE (or stdin)\n" 566//usage: "Decompress FILE (or stdin)\n"
545//usage: "\n -c Write to stdout" 567//usage: "\n -c Write to stdout"
546//usage: "\n -f Force" 568//usage: "\n -f Force"
569//usage: "\n -k Keep input files"
547//usage: 570//usage:
548//usage:#define xz_trivial_usage 571//usage:#define xz_trivial_usage
549//usage: "-d [-cf] [FILE]..." 572//usage: "-d [-cfk] [FILE]..."
550//usage:#define xz_full_usage "\n\n" 573//usage:#define xz_full_usage "\n\n"
551//usage: "Decompress FILE (or stdin)\n" 574//usage: "Decompress FILE (or stdin)\n"
552//usage: "\n -d Decompress" 575//usage: "\n -d Decompress"
553//usage: "\n -c Write to stdout" 576//usage: "\n -c Write to stdout"
554//usage: "\n -f Force" 577//usage: "\n -f Force"
578//usage: "\n -k Keep input files"
555//usage: 579//usage:
556//usage:#define xzcat_trivial_usage 580//usage:#define xzcat_trivial_usage
557//usage: "[FILE]..." 581//usage: "[FILE]..."
@@ -588,7 +612,7 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
588int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 612int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
589int unxz_main(int argc UNUSED_PARAM, char **argv) 613int unxz_main(int argc UNUSED_PARAM, char **argv)
590{ 614{
591 IF_XZ(int opts =) getopt32(argv, "cfvqdt"); 615 IF_XZ(int opts =) getopt32(argv, "cfkvqdt");
592# if ENABLE_XZ 616# if ENABLE_XZ
593 /* xz without -d or -t? */ 617 /* xz without -d or -t? */
594 if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST))) 618 if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
diff --git a/archival/bzip2.c b/archival/bzip2.c
index 7e38e78b3..8afa43802 100644
--- a/archival/bzip2.c
+++ b/archival/bzip2.c
@@ -43,6 +43,7 @@
43//usage: ) 43//usage: )
44//usage: "\n -c Write to stdout" 44//usage: "\n -c Write to stdout"
45//usage: "\n -f Force" 45//usage: "\n -f Force"
46//usage: "\n -k Keep input files"
46 47
47#include "libbb.h" 48#include "libbb.h"
48#include "bb_archive.h" 49#include "bb_archive.h"
@@ -196,13 +197,13 @@ int bzip2_main(int argc UNUSED_PARAM, char **argv)
196 197
197 opt_complementary = "s2"; /* -s means -2 (compatibility) */ 198 opt_complementary = "s2"; /* -s means -2 (compatibility) */
198 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ 199 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
199 opt = getopt32(argv, "cfv" IF_FEATURE_BZIP2_DECOMPRESS("dt") "123456789qzs"); 200 opt = getopt32(argv, "cfkv" IF_FEATURE_BZIP2_DECOMPRESS("dt") "123456789qzs");
200#if ENABLE_FEATURE_BZIP2_DECOMPRESS /* bunzip2_main may not be visible... */ 201#if ENABLE_FEATURE_BZIP2_DECOMPRESS /* bunzip2_main may not be visible... */
201 if (opt & 0x18) // -d and/or -t 202 if (opt & 0x30) // -d and/or -t
202 return bunzip2_main(argc, argv); 203 return bunzip2_main(argc, argv);
203 opt >>= 5; 204 opt >>= 6;
204#else 205#else
205 opt >>= 3; 206 opt >>= 4;
206#endif 207#endif
207 opt = (uint8_t)opt; /* isolate bits for -1..-8 */ 208 opt = (uint8_t)opt; /* isolate bits for -1..-8 */
208 opt |= 0x100; /* if nothing else, assume -9 */ 209 opt |= 0x100; /* if nothing else, assume -9 */
@@ -213,6 +214,6 @@ int bzip2_main(int argc UNUSED_PARAM, char **argv)
213 } 214 }
214 215
215 argv += optind; 216 argv += optind;
216 option_mask32 &= 0x7; /* ignore all except -cfv */ 217 option_mask32 &= 0xf; /* ignore all except -cfkv */
217 return bbunpack(argv, compressStream, append_ext, "bz2"); 218 return bbunpack(argv, compressStream, append_ext, "bz2");
218} 219}
diff --git a/archival/dpkg.c b/archival/dpkg.c
index 1cd45eda4..da3b0864e 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -1938,10 +1938,6 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
1938 for (i = 0; i < STATUS_HASH_PRIME; i++) { 1938 for (i = 0; i < STATUS_HASH_PRIME; i++) {
1939 free(status_hashtable[i]); 1939 free(status_hashtable[i]);
1940 } 1940 }
1941
1942 free(status_hashtable);
1943 free(package_hashtable);
1944 free(name_hashtable);
1945 } 1941 }
1946 1942
1947 return EXIT_SUCCESS; 1943 return EXIT_SUCCESS;
diff --git a/archival/gzip.c b/archival/gzip.c
index e698c26cd..c895de426 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -84,7 +84,7 @@ aa: 85.1% -- replaced with aa.gz
84//kbuild:lib-$(CONFIG_GZIP) += gzip.o 84//kbuild:lib-$(CONFIG_GZIP) += gzip.o
85 85
86//usage:#define gzip_trivial_usage 86//usage:#define gzip_trivial_usage
87//usage: "[-cf" IF_FEATURE_GZIP_DECOMPRESS("dt") IF_FEATURE_GZIP_LEVELS("123456789") "] [FILE]..." 87//usage: "[-cfk" IF_FEATURE_GZIP_DECOMPRESS("dt") IF_FEATURE_GZIP_LEVELS("123456789") "] [FILE]..."
88//usage:#define gzip_full_usage "\n\n" 88//usage:#define gzip_full_usage "\n\n"
89//usage: "Compress FILEs (or stdin)\n" 89//usage: "Compress FILEs (or stdin)\n"
90//usage: IF_FEATURE_GZIP_LEVELS( 90//usage: IF_FEATURE_GZIP_LEVELS(
@@ -96,6 +96,7 @@ aa: 85.1% -- replaced with aa.gz
96//usage: ) 96//usage: )
97//usage: "\n -c Write to stdout" 97//usage: "\n -c Write to stdout"
98//usage: "\n -f Force" 98//usage: "\n -f Force"
99//usage: "\n -k Keep input files"
99//usage: 100//usage:
100//usage:#define gzip_example_usage 101//usage:#define gzip_example_usage
101//usage: "$ ls -la /tmp/busybox*\n" 102//usage: "$ ls -la /tmp/busybox*\n"
@@ -275,7 +276,7 @@ enum {
275 * input file length plus MIN_LOOKAHEAD. 276 * input file length plus MIN_LOOKAHEAD.
276 */ 277 */
277 278
278#ifndef ENABLE_FEATURE_GZIP_LEVELS 279#if !ENABLE_FEATURE_GZIP_LEVELS
279 280
280 max_chain_length = 4096, 281 max_chain_length = 4096,
281/* To speed up deflation, hash chains are never searched beyond this length. 282/* To speed up deflation, hash chains are never searched beyond this length.
@@ -314,7 +315,7 @@ enum {
314 315
315struct globals { 316struct globals {
316 317
317#ifdef ENABLE_FEATURE_GZIP_LEVELS 318#if ENABLE_FEATURE_GZIP_LEVELS
318 unsigned max_chain_length; 319 unsigned max_chain_length;
319 unsigned max_lazy_match; 320 unsigned max_lazy_match;
320 unsigned good_match; 321 unsigned good_match;
@@ -2196,7 +2197,7 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
2196#endif 2197#endif
2197{ 2198{
2198 unsigned opt; 2199 unsigned opt;
2199#ifdef ENABLE_FEATURE_GZIP_LEVELS 2200#if ENABLE_FEATURE_GZIP_LEVELS
2200 static const struct { 2201 static const struct {
2201 uint8_t good; 2202 uint8_t good;
2202 uint8_t chain_shift; 2203 uint8_t chain_shift;
@@ -2219,13 +2220,13 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
2219 applet_long_options = gzip_longopts; 2220 applet_long_options = gzip_longopts;
2220#endif 2221#endif
2221 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */ 2222 /* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
2222 opt = getopt32(argv, "cfv" IF_FEATURE_GZIP_DECOMPRESS("dt") "qn123456789"); 2223 opt = getopt32(argv, "cfkv" IF_FEATURE_GZIP_DECOMPRESS("dt") "qn123456789");
2223#if ENABLE_FEATURE_GZIP_DECOMPRESS /* gunzip_main may not be visible... */ 2224#if ENABLE_FEATURE_GZIP_DECOMPRESS /* gunzip_main may not be visible... */
2224 if (opt & 0x18) // -d and/or -t 2225 if (opt & 0x30) // -d and/or -t
2225 return gunzip_main(argc, argv); 2226 return gunzip_main(argc, argv);
2226#endif 2227#endif
2227#ifdef ENABLE_FEATURE_GZIP_LEVELS 2228#if ENABLE_FEATURE_GZIP_LEVELS
2228 opt >>= ENABLE_FEATURE_GZIP_DECOMPRESS ? 7 : 5; /* drop cfv[dt]qn bits */ 2229 opt >>= ENABLE_FEATURE_GZIP_DECOMPRESS ? 8 : 6; /* drop cfkv[dt]qn bits */
2229 if (opt == 0) 2230 if (opt == 0)
2230 opt = 1 << 6; /* default: 6 */ 2231 opt = 1 << 6; /* default: 6 */
2231 opt = ffs(opt >> 4); /* Maps -1..-4 to [0], -5 to [1] ... -9 to [5] */ 2232 opt = ffs(opt >> 4); /* Maps -1..-4 to [0], -5 to [1] ... -9 to [5] */
@@ -2234,7 +2235,7 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
2234 max_lazy_match = gzip_level_config[opt].lazy2 * 2; 2235 max_lazy_match = gzip_level_config[opt].lazy2 * 2;
2235 nice_match = gzip_level_config[opt].nice2 * 2; 2236 nice_match = gzip_level_config[opt].nice2 * 2;
2236#endif 2237#endif
2237 option_mask32 &= 0x7; /* retain only -cfv */ 2238 option_mask32 &= 0xf; /* retain only -cfkv */
2238 2239
2239 /* Allocate all global buffers (for DYN_ALLOC option) */ 2240 /* Allocate all global buffers (for DYN_ALLOC option) */
2240 ALLOC(uch, G1.l_buf, INBUFSIZ); 2241 ALLOC(uch, G1.l_buf, INBUFSIZ);
diff --git a/archival/libarchive/decompress_unxz.c b/archival/libarchive/decompress_unxz.c
index cd32cc745..350e5358a 100644
--- a/archival/libarchive/decompress_unxz.c
+++ b/archival/libarchive/decompress_unxz.c
@@ -27,11 +27,17 @@ static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
27 return ~crc32_block_endian0(~crc, buf, size, global_crc32_table); 27 return ~crc32_block_endian0(~crc, buf, size, global_crc32_table);
28} 28}
29 29
30/* We use arch-optimized unaligned accessors */ 30/* We use arch-optimized unaligned fixed-endian accessors.
31#define get_unaligned_le32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_LE32(v); }) 31 * They have been moved to libbb (proved to be useful elsewhere as well),
32#define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); }) 32 * just check that we have them defined:
33#define put_unaligned_le32(val, buf) move_to_unaligned32(buf, SWAP_LE32(val)) 33 */
34#define put_unaligned_be32(val, buf) move_to_unaligned32(buf, SWAP_BE32(val)) 34#if !defined(get_unaligned_le32) \
35 || !defined(get_unaligned_be32) \
36 || !defined(put_unaligned_le32) \
37 || !defined(put_unaligned_be32)
38# error get_unaligned_le32 accessors are not defined
39#endif
40#define get_le32(p) (*(uint32_t*)(p))
35 41
36#include "unxz/xz_dec_bcj.c" 42#include "unxz/xz_dec_bcj.c"
37#include "unxz/xz_dec_lzma2.c" 43#include "unxz/xz_dec_lzma2.c"
diff --git a/archival/lzop.c b/archival/lzop.c
index ca61add3c..6ef82b749 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -33,13 +33,13 @@
33//config: 33//config:
34//config:config UNLZOP 34//config:config UNLZOP
35//config: bool "unlzop" 35//config: bool "unlzop"
36//config: default y 36//config: default n # INCOMPAT: upstream lzop does not provide such tool
37//config: help 37//config: help
38//config: Lzop decompresion. 38//config: Lzop decompresion.
39//config: 39//config:
40//config:config LZOPCAT 40//config:config LZOPCAT
41//config: bool "lzopcat" 41//config: bool "lzopcat"
42//config: default y 42//config: default n # INCOMPAT: upstream lzop does not provide such tool
43//config: help 43//config: help
44//config: Alias to "unlzop -c". 44//config: Alias to "unlzop -c".
45//config: 45//config:
@@ -61,12 +61,14 @@
61//kbuild:lib-$(CONFIG_LZOPCAT) += lzop.o 61//kbuild:lib-$(CONFIG_LZOPCAT) += lzop.o
62 62
63//usage:#define lzop_trivial_usage 63//usage:#define lzop_trivial_usage
64//usage: "[-cfvd123456789CF] [FILE]..." 64//usage: "[-cfUvd123456789CF] [FILE]..."
65//usage:#define lzop_full_usage "\n\n" 65//usage:#define lzop_full_usage "\n\n"
66//usage: " -1..9 Compression level" 66//usage: " -1..9 Compression level"
67//usage: "\n -d Decompress" 67//usage: "\n -d Decompress"
68//usage: "\n -c Write to stdout" 68//usage: "\n -c Write to stdout"
69//usage: "\n -f Force" 69//usage: "\n -f Force"
70//usage: "\n -U Delete input files"
71///////: "\n -k Keep input files" (default, so why bother documenting?)
70//usage: "\n -v Verbose" 72//usage: "\n -v Verbose"
71//usage: "\n -F Don't store or verify checksum" 73//usage: "\n -F Don't store or verify checksum"
72//usage: "\n -C Also write checksum of compressed block" 74//usage: "\n -C Also write checksum of compressed block"
@@ -78,10 +80,12 @@
78//usage: "\n -F Don't verify checksum" 80//usage: "\n -F Don't verify checksum"
79//usage: 81//usage:
80//usage:#define unlzop_trivial_usage 82//usage:#define unlzop_trivial_usage
81//usage: "[-cfvF] [FILE]..." 83//usage: "[-cfkvF] [FILE]..."
82//usage:#define unlzop_full_usage "\n\n" 84//usage:#define unlzop_full_usage "\n\n"
83//usage: " -c Write to stdout" 85//usage: " -c Write to stdout"
84//usage: "\n -f Force" 86//usage: "\n -f Force"
87//usage: "\n -U Delete input files"
88///////: "\n -k Keep input files" (default, so why bother documenting?)
85//usage: "\n -v Verbose" 89//usage: "\n -v Verbose"
86//usage: "\n -F Don't verify checksum" 90//usage: "\n -F Don't verify checksum"
87 91
@@ -472,27 +476,33 @@ struct globals {
472//#define LZOP_VERSION_STRING "1.01" 476//#define LZOP_VERSION_STRING "1.01"
473//#define LZOP_VERSION_DATE "Apr 27th 2003" 477//#define LZOP_VERSION_DATE "Apr 27th 2003"
474 478
475#define OPTION_STRING "cfvqdt123456789CF" 479// lzop wants to be weird:
480// unlike all other compressosrs, its -k "keep" option is the default,
481// and -U is used to delete the source. We will invert the bit after getopt().
482#define OPTION_STRING "cfUvqdt123456789CFk"
476 483
477/* Note: must be kept in sync with archival/bbunzip.c */ 484/* Note: must be kept in sync with archival/bbunzip.c */
478enum { 485enum {
479 OPT_STDOUT = (1 << 0), 486 OPT_STDOUT = (1 << 0),
480 OPT_FORCE = (1 << 1), 487 OPT_FORCE = (1 << 1),
481 OPT_VERBOSE = (1 << 2), 488 OPT_KEEP = (1 << 2),
482 OPT_QUIET = (1 << 3), 489 OPT_VERBOSE = (1 << 3),
483 OPT_DECOMPRESS = (1 << 4), 490 OPT_QUIET = (1 << 4),
484 OPT_TEST = (1 << 5), 491 OPT_DECOMPRESS = (1 << 5),
485 OPT_1 = (1 << 6), 492 OPT_TEST = (1 << 6),
486 OPT_2 = (1 << 7), 493 OPT_1 = (1 << 7),
487 OPT_3 = (1 << 8), 494 OPT_2 = (1 << 8),
488 OPT_4 = (1 << 9), 495 OPT_3 = (1 << 9),
489 OPT_5 = (1 << 10), 496 OPT_4 = (1 << 10),
490 OPT_6 = (1 << 11), 497 OPT_5 = (1 << 11),
491 OPT_789 = (7 << 12), 498 OPT_6 = (1 << 12),
492 OPT_7 = (1 << 13), 499 OPT_7 = (1 << 13),
493 OPT_8 = (1 << 14), 500 OPT_8 = (1 << 14),
494 OPT_C = (1 << 15), 501 OPT_9 = (1 << 15),
495 OPT_F = (1 << 16), 502 OPT_C = (1 << 16),
503 OPT_F = (1 << 17),
504 OPT_k = (1 << 18),
505 OPT_789 = OPT_7 | OPT_8 | OPT_9
496}; 506};
497 507
498/**********************************************************************/ 508/**********************************************************************/
@@ -1125,6 +1135,13 @@ int lzop_main(int argc UNUSED_PARAM, char **argv)
1125{ 1135{
1126 getopt32(argv, OPTION_STRING); 1136 getopt32(argv, OPTION_STRING);
1127 argv += optind; 1137 argv += optind;
1138 /* -U is "anti -k", invert bit for bbunpack(): */
1139 option_mask32 ^= OPT_KEEP;
1140 /* -k disables -U (if any): */
1141 /* opt_complementary = "k-U"; - nope, only handles -Uk, not -kU */
1142 if (option_mask32 & OPT_k)
1143 option_mask32 |= OPT_KEEP;
1144
1128 /* lzopcat? */ 1145 /* lzopcat? */
1129 if (ENABLE_LZOPCAT && applet_name[4] == 'c') 1146 if (ENABLE_LZOPCAT && applet_name[4] == 'c')
1130 option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS); 1147 option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS);
diff --git a/archival/tar.c b/archival/tar.c
index be5838d0d..c11b735d5 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -1245,21 +1245,26 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1245 USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_state_t *xstate);) 1245 USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_state_t *xstate);)
1246 USE_FOR_NOMMU(const char *xformer_prog;) 1246 USE_FOR_NOMMU(const char *xformer_prog;)
1247 1247
1248 if (opt & OPT_COMPRESS) 1248 if (opt & OPT_COMPRESS) {
1249 USE_FOR_MMU(xformer = unpack_Z_stream;) 1249 USE_FOR_MMU(IF_FEATURE_SEAMLESS_Z(xformer = unpack_Z_stream;))
1250 USE_FOR_NOMMU(xformer_prog = "uncompress";) 1250 USE_FOR_NOMMU(xformer_prog = "uncompress";)
1251 if (opt & OPT_GZIP) 1251 }
1252 USE_FOR_MMU(xformer = unpack_gz_stream;) 1252 if (opt & OPT_GZIP) {
1253 USE_FOR_MMU(IF_FEATURE_SEAMLESS_GZ(xformer = unpack_gz_stream;))
1253 USE_FOR_NOMMU(xformer_prog = "gunzip";) 1254 USE_FOR_NOMMU(xformer_prog = "gunzip";)
1254 if (opt & OPT_BZIP2) 1255 }
1255 USE_FOR_MMU(xformer = unpack_bz2_stream;) 1256 if (opt & OPT_BZIP2) {
1257 USE_FOR_MMU(IF_FEATURE_SEAMLESS_BZ2(xformer = unpack_bz2_stream;))
1256 USE_FOR_NOMMU(xformer_prog = "bunzip2";) 1258 USE_FOR_NOMMU(xformer_prog = "bunzip2";)
1257 if (opt & OPT_LZMA) 1259 }
1258 USE_FOR_MMU(xformer = unpack_lzma_stream;) 1260 if (opt & OPT_LZMA) {
1261 USE_FOR_MMU(IF_FEATURE_SEAMLESS_LZMA(xformer = unpack_lzma_stream;))
1259 USE_FOR_NOMMU(xformer_prog = "unlzma";) 1262 USE_FOR_NOMMU(xformer_prog = "unlzma";)
1260 if (opt & OPT_XZ) 1263 }
1261 USE_FOR_MMU(xformer = unpack_xz_stream;) 1264 if (opt & OPT_XZ) {
1265 USE_FOR_MMU(IF_FEATURE_SEAMLESS_XZ(xformer = unpack_xz_stream;))
1262 USE_FOR_NOMMU(xformer_prog = "unxz";) 1266 USE_FOR_NOMMU(xformer_prog = "unxz";)
1267 }
1263 1268
1264 fork_transformer_with_sig(tar_handle->src_fd, xformer, xformer_prog); 1269 fork_transformer_with_sig(tar_handle->src_fd, xformer, xformer_prog);
1265 /* Can't lseek over pipes */ 1270 /* Can't lseek over pipes */
diff --git a/archival/unzip.c b/archival/unzip.c
index 986145d0f..8dfc4e678 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -349,6 +349,12 @@ static void unzip_extract(zip_header_t *zip, int dst_fd)
349 return; 349 return;
350 } 350 }
351 351
352// NB: to support symlinks, need to extract symlink target. A-la:
353// xstate.mem_output_size_max = zip->fmt.ucmpsize;
354// ...unpack...
355// if (xstate.mem_output_buf) { success, xstate.mem_output_size is the size }
356// Although archives I've seen have fmt.method == 0 for symlinks.
357
352 init_transformer_state(&xstate); 358 init_transformer_state(&xstate);
353 xstate.bytes_in = zip->fmt.cmpsize; 359 xstate.bytes_in = zip->fmt.cmpsize;
354 xstate.src_fd = zip_fd; 360 xstate.src_fd = zip_fd;
@@ -688,6 +694,20 @@ int unzip_main(int argc, char **argv)
688 zip.fmt.cmpsize = cdf.fmt.cmpsize; 694 zip.fmt.cmpsize = cdf.fmt.cmpsize;
689 zip.fmt.ucmpsize = cdf.fmt.ucmpsize; 695 zip.fmt.ucmpsize = cdf.fmt.ucmpsize;
690 } 696 }
697// Seen in some zipfiles: central directory 9 byte extra field contains
698// a subfield with ID 0x5455 and 5 data bytes, which is a Unix-style UTC mtime.
699// Local header version:
700// u16 0x5455 ("UT")
701// u16 size (1 + 4 * n)
702// u8 flags: bit 0:mtime is present, bit 1:atime is present, bit 2:ctime is present
703// u32 mtime
704// u32 atime
705// u32 ctime
706// Central header version:
707// u16 0x5455 ("UT")
708// u16 size (5 (or 1?))
709// u8 flags: bit 0:mtime is present, bit 1:atime is present, bit 2:ctime is present
710// u32 mtime (CDF does not store atime/ctime)
691# else 711# else
692 /* CDF has the same data as local header, no need to read the latter... 712 /* CDF has the same data as local header, no need to read the latter...
693 * ...not really. An archive was seen with cdf.extra_len == 6 but 713 * ...not really. An archive was seen with cdf.extra_len == 6 but
@@ -702,6 +722,7 @@ int unzip_main(int argc, char **argv)
702 if ((cdf.fmt.version_made_by >> 8) == 3) { 722 if ((cdf.fmt.version_made_by >> 8) == 3) {
703 /* This archive is created on Unix */ 723 /* This archive is created on Unix */
704 dir_mode = file_mode = (cdf.fmt.external_attributes >> 16); 724 dir_mode = file_mode = (cdf.fmt.external_attributes >> 16);
725//TODO: if (S_ISLNK(file_mode)) this is a symlink
705 } 726 }
706 } 727 }
707#endif 728#endif
diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig
index 7fbbbecc7..6ff68a092 100644
--- a/configs/TEST_nommu_defconfig
+++ b/configs/TEST_nommu_defconfig
@@ -891,7 +891,6 @@ CONFIG_HUSH_FUNCTIONS=y
891CONFIG_HUSH_LOCAL=y 891CONFIG_HUSH_LOCAL=y
892CONFIG_HUSH_EXPORT_N=y 892CONFIG_HUSH_EXPORT_N=y
893CONFIG_HUSH_RANDOM_SUPPORT=y 893CONFIG_HUSH_RANDOM_SUPPORT=y
894CONFIG_MSH=y
895CONFIG_SH_MATH_SUPPORT=y 894CONFIG_SH_MATH_SUPPORT=y
896CONFIG_SH_MATH_SUPPORT_64=y 895CONFIG_SH_MATH_SUPPORT_64=y
897CONFIG_FEATURE_SH_EXTRA_QUIET=y 896CONFIG_FEATURE_SH_EXTRA_QUIET=y
diff --git a/configs/TEST_noprintf_defconfig b/configs/TEST_noprintf_defconfig
index 3f85ee1df..4b2ef402a 100644
--- a/configs/TEST_noprintf_defconfig
+++ b/configs/TEST_noprintf_defconfig
@@ -898,7 +898,6 @@ CONFIG_FEATURE_SH_IS_NONE=y
898# CONFIG_FEATURE_BASH_IS_ASH is not set 898# CONFIG_FEATURE_BASH_IS_ASH is not set
899# CONFIG_FEATURE_BASH_IS_HUSH is not set 899# CONFIG_FEATURE_BASH_IS_HUSH is not set
900CONFIG_FEATURE_BASH_IS_NONE=y 900CONFIG_FEATURE_BASH_IS_NONE=y
901# CONFIG_MSH is not set
902# CONFIG_SH_MATH_SUPPORT is not set 901# CONFIG_SH_MATH_SUPPORT is not set
903# CONFIG_SH_MATH_SUPPORT_64 is not set 902# CONFIG_SH_MATH_SUPPORT_64 is not set
904# CONFIG_FEATURE_SH_EXTRA_QUIET is not set 903# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
diff --git a/configs/TEST_rh9_defconfig b/configs/TEST_rh9_defconfig
index 34d8e31e2..52f3e4670 100644
--- a/configs/TEST_rh9_defconfig
+++ b/configs/TEST_rh9_defconfig
@@ -905,7 +905,6 @@ CONFIG_HUSH_FUNCTIONS=y
905CONFIG_HUSH_LOCAL=y 905CONFIG_HUSH_LOCAL=y
906CONFIG_HUSH_EXPORT_N=y 906CONFIG_HUSH_EXPORT_N=y
907CONFIG_HUSH_RANDOM_SUPPORT=y 907CONFIG_HUSH_RANDOM_SUPPORT=y
908CONFIG_MSH=y
909CONFIG_SH_MATH_SUPPORT=y 908CONFIG_SH_MATH_SUPPORT=y
910CONFIG_SH_MATH_SUPPORT_64=y 909CONFIG_SH_MATH_SUPPORT_64=y
911CONFIG_FEATURE_SH_EXTRA_QUIET=y 910CONFIG_FEATURE_SH_EXTRA_QUIET=y
diff --git a/configs/android2_defconfig b/configs/android2_defconfig
index 20866c32b..9202320a4 100644
--- a/configs/android2_defconfig
+++ b/configs/android2_defconfig
@@ -952,7 +952,6 @@ CONFIG_CTTYHACK=y
952# CONFIG_HUSH_RANDOM_SUPPORT is not set 952# CONFIG_HUSH_RANDOM_SUPPORT is not set
953# CONFIG_HUSH_EXPORT_N is not set 953# CONFIG_HUSH_EXPORT_N is not set
954# CONFIG_HUSH_MODE_X is not set 954# CONFIG_HUSH_MODE_X is not set
955# CONFIG_MSH is not set
956# CONFIG_FEATURE_SH_IS_ASH is not set 955# CONFIG_FEATURE_SH_IS_ASH is not set
957# CONFIG_FEATURE_SH_IS_HUSH is not set 956# CONFIG_FEATURE_SH_IS_HUSH is not set
958CONFIG_FEATURE_SH_IS_NONE=y 957CONFIG_FEATURE_SH_IS_NONE=y
diff --git a/configs/android_502_defconfig b/configs/android_502_defconfig
index bdca9eebb..1901bdbb0 100644
--- a/configs/android_502_defconfig
+++ b/configs/android_502_defconfig
@@ -1098,7 +1098,6 @@ CONFIG_CTTYHACK=y
1098# CONFIG_HUSH_RANDOM_SUPPORT is not set 1098# CONFIG_HUSH_RANDOM_SUPPORT is not set
1099# CONFIG_HUSH_EXPORT_N is not set 1099# CONFIG_HUSH_EXPORT_N is not set
1100# CONFIG_HUSH_MODE_X is not set 1100# CONFIG_HUSH_MODE_X is not set
1101# CONFIG_MSH is not set
1102CONFIG_FEATURE_SH_IS_ASH=y 1101CONFIG_FEATURE_SH_IS_ASH=y
1103# CONFIG_FEATURE_SH_IS_HUSH is not set 1102# CONFIG_FEATURE_SH_IS_HUSH is not set
1104# CONFIG_FEATURE_SH_IS_NONE is not set 1103# CONFIG_FEATURE_SH_IS_NONE is not set
diff --git a/configs/android_defconfig b/configs/android_defconfig
index 6ef81750e..ea6e8a79e 100644
--- a/configs/android_defconfig
+++ b/configs/android_defconfig
@@ -984,7 +984,6 @@ CONFIG_CTTYHACK=y
984# CONFIG_HUSH_RANDOM_SUPPORT is not set 984# CONFIG_HUSH_RANDOM_SUPPORT is not set
985# CONFIG_HUSH_EXPORT_N is not set 985# CONFIG_HUSH_EXPORT_N is not set
986# CONFIG_HUSH_MODE_X is not set 986# CONFIG_HUSH_MODE_X is not set
987# CONFIG_MSH is not set
988# CONFIG_FEATURE_SH_IS_ASH is not set 987# CONFIG_FEATURE_SH_IS_ASH is not set
989# CONFIG_FEATURE_SH_IS_HUSH is not set 988# CONFIG_FEATURE_SH_IS_HUSH is not set
990CONFIG_FEATURE_SH_IS_NONE=y 989CONFIG_FEATURE_SH_IS_NONE=y
diff --git a/configs/android_ndk_defconfig b/configs/android_ndk_defconfig
index 35d03b42e..61871fcb1 100644
--- a/configs/android_ndk_defconfig
+++ b/configs/android_ndk_defconfig
@@ -1013,7 +1013,6 @@ CONFIG_CTTYHACK=y
1013# CONFIG_HUSH_RANDOM_SUPPORT is not set 1013# CONFIG_HUSH_RANDOM_SUPPORT is not set
1014# CONFIG_HUSH_EXPORT_N is not set 1014# CONFIG_HUSH_EXPORT_N is not set
1015# CONFIG_HUSH_MODE_X is not set 1015# CONFIG_HUSH_MODE_X is not set
1016# CONFIG_MSH is not set
1017# CONFIG_FEATURE_SH_IS_ASH is not set 1016# CONFIG_FEATURE_SH_IS_ASH is not set
1018# CONFIG_FEATURE_SH_IS_HUSH is not set 1017# CONFIG_FEATURE_SH_IS_HUSH is not set
1019CONFIG_FEATURE_SH_IS_NONE=y 1018CONFIG_FEATURE_SH_IS_NONE=y
diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig
index 6bfc973ef..54aa44470 100644
--- a/configs/cygwin_defconfig
+++ b/configs/cygwin_defconfig
@@ -955,7 +955,6 @@ CONFIG_HUSH_LOCAL=y
955CONFIG_HUSH_RANDOM_SUPPORT=y 955CONFIG_HUSH_RANDOM_SUPPORT=y
956CONFIG_HUSH_EXPORT_N=y 956CONFIG_HUSH_EXPORT_N=y
957CONFIG_HUSH_MODE_X=y 957CONFIG_HUSH_MODE_X=y
958# CONFIG_MSH is not set
959CONFIG_FEATURE_SH_IS_ASH=y 958CONFIG_FEATURE_SH_IS_ASH=y
960# CONFIG_FEATURE_SH_IS_HUSH is not set 959# CONFIG_FEATURE_SH_IS_HUSH is not set
961# CONFIG_FEATURE_SH_IS_NONE is not set 960# CONFIG_FEATURE_SH_IS_NONE is not set
diff --git a/configs/freebsd_defconfig b/configs/freebsd_defconfig
index e3d04aedc..fadbca13b 100644
--- a/configs/freebsd_defconfig
+++ b/configs/freebsd_defconfig
@@ -931,7 +931,6 @@ CONFIG_ASH=y
931# CONFIG_HUSH_RANDOM_SUPPORT is not set 931# CONFIG_HUSH_RANDOM_SUPPORT is not set
932# CONFIG_HUSH_EXPORT_N is not set 932# CONFIG_HUSH_EXPORT_N is not set
933# CONFIG_HUSH_MODE_X is not set 933# CONFIG_HUSH_MODE_X is not set
934# CONFIG_MSH is not set
935CONFIG_FEATURE_SH_IS_ASH=y 934CONFIG_FEATURE_SH_IS_ASH=y
936# CONFIG_FEATURE_SH_IS_HUSH is not set 935# CONFIG_FEATURE_SH_IS_HUSH is not set
937# CONFIG_FEATURE_SH_IS_NONE is not set 936# CONFIG_FEATURE_SH_IS_NONE is not set
diff --git a/coreutils/cat.c b/coreutils/cat.c
index 4d9147f8a..a9ba68d6b 100644
--- a/coreutils/cat.c
+++ b/coreutils/cat.c
@@ -13,6 +13,13 @@
13//config: cat is used to concatenate files and print them to the standard 13//config: cat is used to concatenate files and print them to the standard
14//config: output. Enable this option if you wish to enable the 'cat' utility. 14//config: output. Enable this option if you wish to enable the 'cat' utility.
15//config: 15//config:
16//config:config FEATURE_CATN
17//config: bool "Enable -n and -b options"
18//config: default y
19//config: depends on CAT
20//config: help
21//config: -n numbers all output lines while -b numbers nonempty output lines.
22//config:
16//config:config FEATURE_CATV 23//config:config FEATURE_CATV
17//config: bool "cat -v[etA]" 24//config: bool "cat -v[etA]"
18//config: default y 25//config: default y
@@ -27,12 +34,19 @@
27/* BB_AUDIT SUSv3 compliant */ 34/* BB_AUDIT SUSv3 compliant */
28/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */ 35/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
29 36
37//usage:#if ENABLE_FEATURE_CATN || ENABLE_FEATURE_CATV
38//usage:#define cat_trivial_usage
39//usage: "[-" IF_FEATURE_CATN("nb") IF_FEATURE_CATV("vteA") "] [FILE]..."
40//usage:#else
30//usage:#define cat_trivial_usage 41//usage:#define cat_trivial_usage
31//usage: "[-nb"IF_FEATURE_CATV("vteA")"] [FILE]..." 42//usage: "[FILE]..."
43//usage:#endif
32//usage:#define cat_full_usage "\n\n" 44//usage:#define cat_full_usage "\n\n"
33//usage: "Print FILEs to stdout\n" 45//usage: "Print FILEs to stdout\n"
46//usage: IF_FEATURE_CATN(
34//usage: "\n -n Number output lines" 47//usage: "\n -n Number output lines"
35//usage: "\n -b Number nonempty lines" 48//usage: "\n -b Number nonempty lines"
49//usage: )
36//usage: IF_FEATURE_CATV( 50//usage: IF_FEATURE_CATV(
37//usage: "\n -v Show nonprinting characters as ^x or M-x" 51//usage: "\n -v Show nonprinting characters as ^x or M-x"
38//usage: "\n -t ...and tabs as ^I" 52//usage: "\n -t ...and tabs as ^I"
@@ -84,30 +98,35 @@
84 * I agree with the argument. Unfortunately, this ship has sailed (1983...). 98 * I agree with the argument. Unfortunately, this ship has sailed (1983...).
85 * There are dozens of Linux distros and each of them has "cat" which supports -v. 99 * There are dozens of Linux distros and each of them has "cat" which supports -v.
86 * It's unrealistic for us to "reeducate" them to use our, incompatible way 100 * It's unrealistic for us to "reeducate" them to use our, incompatible way
87 * to achieve "cat -v" effect. The actuall effect would be "users pissed off 101 * to achieve "cat -v" effect. The actual effect would be "users pissed off
88 * by gratuitous incompatibility". 102 * by gratuitous incompatibility".
89 */ 103 */
90#define CATV_OPT_e (1<<0) 104#define CAT_OPT_e (1<<0)
91#define CATV_OPT_t (1<<1) 105#define CAT_OPT_t (1<<1)
92#define CATV_OPT_v (1<<2) 106#define CAT_OPT_v (1<<2)
107/* -A occupies bit (1<<3) */
108#define CAT_OPT_n ((1<<4) * ENABLE_FEATURE_CATN)
109#define CAT_OPT_b ((1<<5) * ENABLE_FEATURE_CATN)
93static int catv(unsigned opts, char **argv) 110static int catv(unsigned opts, char **argv)
94{ 111{
95 int retval = EXIT_SUCCESS; 112 int retval = EXIT_SUCCESS;
96 int fd; 113 int fd;
114#if ENABLE_FEATURE_CATN
115 unsigned lineno = 0;
116 unsigned eol_char = (opts & (CAT_OPT_n|CAT_OPT_b)) ? '\n' : 0x100;
117 unsigned skip_num_on = (opts & CAT_OPT_b) ? '\n' : 0x100;
118 bool eol_seen = 1;
119#endif
97 120
98 BUILD_BUG_ON(CATV_OPT_e != VISIBLE_ENDLINE); 121 BUILD_BUG_ON(CAT_OPT_e != VISIBLE_ENDLINE);
99 BUILD_BUG_ON(CATV_OPT_t != VISIBLE_SHOW_TABS); 122 BUILD_BUG_ON(CAT_OPT_t != VISIBLE_SHOW_TABS);
100#if 0 /* These consts match, we can just pass "opts" to visible() */ 123#if 0 /* These consts match, we can just pass "opts" to visible() */
101 if (opts & CATV_OPT_e) 124 if (opts & CAT_OPT_e)
102 flags |= VISIBLE_ENDLINE; 125 flags |= VISIBLE_ENDLINE;
103 if (opts & CATV_OPT_t) 126 if (opts & CAT_OPT_t)
104 flags |= VISIBLE_SHOW_TABS; 127 flags |= VISIBLE_SHOW_TABS;
105#endif 128#endif
106 129
107 /* Read from stdin if there's nothing else to do. */
108 if (!argv[0])
109 *--argv = (char*)"-";
110
111#define read_buf bb_common_bufsiz1 130#define read_buf bb_common_bufsiz1
112 setup_common_bufsiz(); 131 setup_common_bufsiz();
113 do { 132 do {
@@ -127,6 +146,11 @@ static int catv(unsigned opts, char **argv)
127 for (i = 0; i < res; i++) { 146 for (i = 0; i < res; i++) {
128 unsigned char c = read_buf[i]; 147 unsigned char c = read_buf[i];
129 char buf[sizeof("M-^c")]; 148 char buf[sizeof("M-^c")];
149#if ENABLE_FEATURE_CATN
150 if (eol_seen && c != skip_num_on)
151 printf("%6u ", ++lineno);
152 eol_seen = (c == eol_char);
153#endif
130 visible(c, buf, opts); 154 visible(c, buf, opts);
131 fputs(buf, stdout); 155 fputs(buf, stdout);
132 } 156 }
@@ -137,43 +161,50 @@ static int catv(unsigned opts, char **argv)
137 161
138 fflush_stdout_and_exit(retval); 162 fflush_stdout_and_exit(retval);
139} 163}
164#undef CAT_OPT_n
165#undef CAT_OPT_b
140#endif 166#endif
141 167
142int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 168int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
143int cat_main(int argc UNUSED_PARAM, char **argv) 169int cat_main(int argc UNUSED_PARAM, char **argv)
144{ 170{
145 struct number_state ns;
146 unsigned opts; 171 unsigned opts;
147 172
148 IF_FEATURE_CATV(opt_complementary = "Aetv"; /* -A == -vet */) 173 IF_FEATURE_CATV(opt_complementary = "Aetv"; /* -A == -vet */)
149 /* -u is ignored ("unbuffered") */ 174 /* -u is ignored ("unbuffered") */
150 opts = getopt32(argv, IF_FEATURE_CATV("etvA")"nbu"); 175 opts = getopt32(argv, IF_FEATURE_CATV("etvA") IF_FEATURE_CATN("nb") "u");
151 argv += optind; 176 argv += optind;
152 177
178 /* Read from stdin if there's nothing else to do. */
179 if (!argv[0])
180 *--argv = (char*)"-";
181
153#if ENABLE_FEATURE_CATV 182#if ENABLE_FEATURE_CATV
154 if (opts & 7) 183 if (opts & 7)
155 return catv(opts, argv); 184 return catv(opts, argv);
156//BUG: -v,-e,-t,-A ignore -nb
157 opts >>= 4; 185 opts >>= 4;
158#endif 186#endif
159 187
160#define CAT_OPT_n (1<<0) 188#if ENABLE_FEATURE_CATN
161#define CAT_OPT_b (1<<1) 189# define CAT_OPT_n (1<<0)
162#define CAT_OPT_u (1<<2) 190# define CAT_OPT_b (1<<1)
163 if (!(opts & (CAT_OPT_n|CAT_OPT_b))) /* no -n or -b */ 191 if (opts & (CAT_OPT_n|CAT_OPT_b)) { /* -n or -b */
164 return bb_cat(argv); 192 struct number_state ns;
193
194 ns.width = 6;
195 ns.start = 1;
196 ns.inc = 1;
197 ns.sep = "\t";
198 ns.empty_str = "\n";
199 ns.all = !(opts & CAT_OPT_b); /* -n without -b */
200 ns.nonempty = (opts & CAT_OPT_b); /* -b (with or without -n) */
201 do {
202 print_numbered_lines(&ns, *argv);
203 } while (*++argv);
204 fflush_stdout_and_exit(EXIT_SUCCESS);
205 }
206 /*opts >>= 2;*/
207#endif
165 208
166 if (!*argv) 209 return bb_cat(argv);
167 *--argv = (char*)"-";
168 ns.width = 6;
169 ns.start = 1;
170 ns.inc = 1;
171 ns.sep = "\t";
172 ns.empty_str = "\n";
173 ns.all = !(opts & CAT_OPT_b); /* -n without -b */
174 ns.nonempty = (opts & CAT_OPT_b); /* -b (with or without -n) */
175 do {
176 print_numbered_lines(&ns, *argv);
177 } while (*++argv);
178 fflush_stdout_and_exit(EXIT_SUCCESS);
179} 210}
diff --git a/coreutils/dd.c b/coreutils/dd.c
index 2fccf9d8e..b37615313 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -133,9 +133,8 @@ enum {
133 /* end of input flags */ 133 /* end of input flags */
134 FLAG_TWOBUFS = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS, 134 FLAG_TWOBUFS = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS,
135 FLAG_COUNT = 1 << 7, 135 FLAG_COUNT = 1 << 7,
136 FLAG_STATUS = 1 << 8, 136 FLAG_STATUS_NONE = 1 << 8,
137 FLAG_STATUS_NONE = 1 << 9, 137 FLAG_STATUS_NOXFER = 1 << 9,
138 FLAG_STATUS_NOXFER = 1 << 10,
139}; 138};
140 139
141static void dd_output_status(int UNUSED_PARAM cur_signal) 140static void dd_output_status(int UNUSED_PARAM cur_signal)
@@ -397,7 +396,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
397 n = index_in_strings(status_words, val); 396 n = index_in_strings(status_words, val);
398 if (n < 0) 397 if (n < 0)
399 bb_error_msg_and_die(bb_msg_invalid_arg_to, val, "status"); 398 bb_error_msg_and_die(bb_msg_invalid_arg_to, val, "status");
400 G.flags |= FLAG_STATUS << n; 399 G.flags |= FLAG_STATUS_NONE << n;
401 /*continue;*/ 400 /*continue;*/
402 } 401 }
403#endif 402#endif
diff --git a/coreutils/nl.c b/coreutils/nl.c
index 5c64923bb..dc468a90b 100644
--- a/coreutils/nl.c
+++ b/coreutils/nl.c
@@ -35,26 +35,6 @@
35 35
36#include "libbb.h" 36#include "libbb.h"
37 37
38void FAST_FUNC print_numbered_lines(struct number_state *ns, const char *filename)
39{
40 FILE *fp = fopen_or_warn_stdin(filename);
41 unsigned N = ns->start;
42 char *line;
43
44 while ((line = xmalloc_fgetline(fp)) != NULL) {
45 if (ns->all
46 || (ns->nonempty && line[0])
47 ) {
48 printf("%*u%s%s\n", ns->width, N, ns->sep, line);
49 N += ns->inc;
50 } else if (ns->empty_str)
51 fputs(ns->empty_str, stdout);
52 free(line);
53 }
54
55 fclose(fp);
56}
57
58int nl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 38int nl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
59int nl_main(int argc UNUSED_PARAM, char **argv) 39int nl_main(int argc UNUSED_PARAM, char **argv)
60{ 40{
diff --git a/coreutils/printf.c b/coreutils/printf.c
index bc22e0ee7..65bb5a935 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -305,7 +305,7 @@ static char **print_formatted(char *f, char **argv, int *conv_err)
305 } 305 }
306 break; 306 break;
307 } 307 }
308 if (strchr("-+ #", *f)) { 308 if (*f && strchr("-+ #", *f)) {
309 ++f; 309 ++f;
310 ++direc_length; 310 ++direc_length;
311 } 311 }
@@ -348,7 +348,7 @@ static char **print_formatted(char *f, char **argv, int *conv_err)
348 static const char format_chars[] ALIGN1 = "diouxXfeEgGcs"; 348 static const char format_chars[] ALIGN1 = "diouxXfeEgGcs";
349 char *p = strchr(format_chars, *f); 349 char *p = strchr(format_chars, *f);
350 /* needed - try "printf %" without it */ 350 /* needed - try "printf %" without it */
351 if (p == NULL) { 351 if (p == NULL || *f == '\0') {
352 bb_error_msg("%s: invalid format", direc_start); 352 bb_error_msg("%s: invalid format", direc_start);
353 /* causes main() to exit with error */ 353 /* causes main() to exit with error */
354 return saved_argv - 1; 354 return saved_argv - 1;
diff --git a/coreutils/shuf.c b/coreutils/shuf.c
index 9f61f2f7d..217f15c97 100644
--- a/coreutils/shuf.c
+++ b/coreutils/shuf.c
@@ -53,7 +53,7 @@ static void shuffle_lines(char **lines, unsigned numlines)
53 /* RAND_MAX can be as small as 32767 */ 53 /* RAND_MAX can be as small as 32767 */
54 if (i > RAND_MAX) 54 if (i > RAND_MAX)
55 r ^= rand() << 15; 55 r ^= rand() << 15;
56 r %= i; 56 r %= i + 1;
57 tmp = lines[i]; 57 tmp = lines[i];
58 lines[i] = lines[r]; 58 lines[i] = lines[r];
59 lines[r] = tmp; 59 lines[r] = tmp;
diff --git a/coreutils/test.c b/coreutils/test.c
index c949a3cc9..d4f93312a 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -563,26 +563,11 @@ static int binop(void)
563 /*return 1; - NOTREACHED */ 563 /*return 1; - NOTREACHED */
564} 564}
565 565
566
567static void initialize_group_array(void) 566static void initialize_group_array(void)
568{ 567{
569 int n; 568 group_array = bb_getgroups(&ngroups, NULL);
570
571 /* getgroups may be expensive, try to use it only once */
572 ngroups = 32;
573 do {
574 /* FIXME: ash tries so hard to not die on OOM,
575 * and we spoil it with just one xrealloc here */
576 /* We realloc, because test_main can be entered repeatedly by shell.
577 * Testcase (ash): 'while true; do test -x some_file; done'
578 * and watch top. (some_file must have owner != you) */
579 n = ngroups;
580 group_array = xrealloc(group_array, n * sizeof(gid_t));
581 ngroups = getgroups(n, group_array);
582 } while (ngroups > n);
583} 569}
584 570
585
586/* Return non-zero if GID is one that we have in our groups list. */ 571/* Return non-zero if GID is one that we have in our groups list. */
587//XXX: FIXME: duplicate of existing libbb function? 572//XXX: FIXME: duplicate of existing libbb function?
588// see toplevel TODO file: 573// see toplevel TODO file:
@@ -610,14 +595,10 @@ static int is_a_group_member(gid_t gid)
610/* Do the same thing access(2) does, but use the effective uid and gid, 595/* Do the same thing access(2) does, but use the effective uid and gid,
611 and don't make the mistake of telling root that any file is 596 and don't make the mistake of telling root that any file is
612 executable. */ 597 executable. */
613static int test_eaccess(char *path, int mode) 598static int test_eaccess(struct stat *st, int mode)
614{ 599{
615 struct stat st;
616 unsigned int euid = geteuid(); 600 unsigned int euid = geteuid();
617 601
618 if (stat(path, &st) < 0)
619 return -1;
620
621 if (euid == 0) { 602 if (euid == 0) {
622 /* Root can read or write any file. */ 603 /* Root can read or write any file. */
623 if (mode != X_OK) 604 if (mode != X_OK)
@@ -625,16 +606,16 @@ static int test_eaccess(char *path, int mode)
625 606
626 /* Root can execute any file that has any one of the execute 607 /* Root can execute any file that has any one of the execute
627 * bits set. */ 608 * bits set. */
628 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 609 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
629 return 0; 610 return 0;
630 } 611 }
631 612
632 if (st.st_uid == euid) /* owner */ 613 if (st->st_uid == euid) /* owner */
633 mode <<= 6; 614 mode <<= 6;
634 else if (is_a_group_member(st.st_gid)) 615 else if (is_a_group_member(st->st_gid))
635 mode <<= 3; 616 mode <<= 3;
636 617
637 if (st.st_mode & mode) 618 if (st->st_mode & mode)
638 return 0; 619 return 0;
639 620
640 return -1; 621 return -1;
@@ -682,7 +663,7 @@ static int filstat(char *nm, enum token mode)
682 i = W_OK; 663 i = W_OK;
683 if (mode == FILEX) 664 if (mode == FILEX)
684 i = X_OK; 665 i = X_OK;
685 return test_eaccess(nm, i) == 0; 666 return test_eaccess(&s, i) == 0;
686 } 667 }
687 if (is_file_type(mode)) { 668 if (is_file_type(mode)) {
688 if (mode == FILREG) 669 if (mode == FILREG)
diff --git a/coreutils/uname.c b/coreutils/uname.c
index 4d98fde25..9c6a06ebb 100644
--- a/coreutils/uname.c
+++ b/coreutils/uname.c
@@ -55,10 +55,20 @@
55//config: help 55//config: help
56//config: Sets the operating system name reported by uname -o. The 56//config: Sets the operating system name reported by uname -o. The
57//config: default is "GNU/Linux". 57//config: default is "GNU/Linux".
58//config:
59//can't use "ARCH" for this applet, all hell breaks loose in build system :)
60//config:config BBARCH
61//config: bool "arch"
62//config: default y
63//config: help
64//config: Same as uname -m.
58 65
59//applet:IF_UNAME(APPLET(uname, BB_DIR_BIN, BB_SUID_DROP)) 66//applet:IF_UNAME(APPLET(uname, BB_DIR_BIN, BB_SUID_DROP))
67// APPLET_ODDNAME:name main location suid_type help
68//applet:IF_BBARCH(APPLET_ODDNAME(arch, uname, BB_DIR_BIN, BB_SUID_DROP, arch))
60 69
61//kbuild:lib-$(CONFIG_UNAME) += uname.o 70//kbuild:lib-$(CONFIG_UNAME) += uname.o
71//kbuild:lib-$(CONFIG_BBARCH) += uname.o
62 72
63/* BB_AUDIT SUSv3 compliant */ 73/* BB_AUDIT SUSv3 compliant */
64/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */ 74/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */
@@ -81,6 +91,11 @@
81//usage: "$ uname -a\n" 91//usage: "$ uname -a\n"
82//usage: "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n" 92//usage: "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n"
83 93
94//usage:#define arch_trivial_usage
95//usage: ""
96//usage:#define arch_full_usage "\n\n"
97//usage: "Print system architecture"
98
84#include "libbb.h" 99#include "libbb.h"
85/* After libbb.h, since it needs sys/types.h on some systems */ 100/* After libbb.h, since it needs sys/types.h on some systems */
86#include <sys/utsname.h> 101#include <sys/utsname.h>
@@ -92,7 +107,8 @@ typedef struct {
92 char os[sizeof(CONFIG_UNAME_OSNAME)]; 107 char os[sizeof(CONFIG_UNAME_OSNAME)];
93} uname_info_t; 108} uname_info_t;
94 109
95static const char options[] ALIGN1 = "snrvmpioa"; 110#if ENABLE_UNAME
111#define options "snrvmpioa"
96static const unsigned short utsname_offset[] = { 112static const unsigned short utsname_offset[] = {
97 offsetof(uname_info_t, name.sysname), /* -s */ 113 offsetof(uname_info_t, name.sysname), /* -s */
98 offsetof(uname_info_t, name.nodename), /* -n */ 114 offsetof(uname_info_t, name.nodename), /* -n */
@@ -103,86 +119,97 @@ static const unsigned short utsname_offset[] = {
103 offsetof(uname_info_t, platform), /* -i */ 119 offsetof(uname_info_t, platform), /* -i */
104 offsetof(uname_info_t, os), /* -o */ 120 offsetof(uname_info_t, os), /* -o */
105}; 121};
122#endif
106 123
107int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 124int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
108int uname_main(int argc UNUSED_PARAM, char **argv) 125int uname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
109{ 126{
110#if ENABLE_LONG_OPTS
111 static const char uname_longopts[] ALIGN1 =
112 /* name, has_arg, val */
113 "all\0" No_argument "a"
114 "kernel-name\0" No_argument "s"
115 "nodename\0" No_argument "n"
116 "kernel-release\0" No_argument "r"
117 "release\0" No_argument "r"
118 "kernel-version\0" No_argument "v"
119 "machine\0" No_argument "m"
120 "processor\0" No_argument "p"
121 "hardware-platform\0" No_argument "i"
122 "operating-system\0" No_argument "o"
123 ;
124#endif
125 uname_info_t uname_info; 127 uname_info_t uname_info;
126#if defined(__sparc__) && defined(__linux__) 128 IF_UNAME(const char *unknown_str = "unknown";)
127 char *fake_sparc = getenv("FAKE_SPARC");
128#endif
129 const char *unknown_str = "unknown";
130 const char *fmt;
131 const unsigned short *delta;
132 unsigned toprint; 129 unsigned toprint;
133 130
134 IF_LONG_OPTS(applet_long_options = uname_longopts); 131 toprint = (1 << 4); /* "arch" = "uname -m" */
135 toprint = getopt32(argv, options); 132
136 133#if ENABLE_UNAME
137 if (argv[optind]) { /* coreutils-6.9 compat */ 134 if (!ENABLE_BBARCH || applet_name[0] == 'u') {
138 bb_show_usage(); 135# if ENABLE_LONG_OPTS
139 } 136 static const char uname_longopts[] ALIGN1 =
140 137 /* name, has_arg, val */
141 if (toprint & (1 << 8)) { /* -a => all opts on */ 138 "all\0" No_argument "a"
142 toprint = (1 << 8) - 1; 139 "kernel-name\0" No_argument "s"
143 unknown_str = ""; /* -a does not print unknown fields */ 140 "nodename\0" No_argument "n"
144 } 141 "kernel-release\0" No_argument "r"
145 142 "release\0" No_argument "r"
146 if (toprint == 0) { /* no opts => -s (sysname) */ 143 "kernel-version\0" No_argument "v"
147 toprint = 1; 144 "machine\0" No_argument "m"
148 } 145 "processor\0" No_argument "p"
146 "hardware-platform\0" No_argument "i"
147 "operating-system\0" No_argument "o"
148 ;
149# endif
150 IF_LONG_OPTS(applet_long_options = uname_longopts);
151 toprint = getopt32(argv, options);
152 if (argv[optind]) { /* coreutils-6.9 compat */
153 bb_show_usage();
154 }
155 if (toprint & (1 << 8)) { /* -a => all opts on */
156 toprint = (1 << 8) - 1;
157 unknown_str = ""; /* -a does not print unknown fields */
158 }
159 if (toprint == 0) { /* no opts => -s (sysname) */
160 toprint = 1;
161 }
162 } /* if "uname" */
163#endif
149 164
150 uname(&uname_info.name); /* never fails */ 165 uname(&uname_info.name); /* never fails */
151 166
152#if defined(__sparc__) && defined(__linux__) 167#if defined(__sparc__) && defined(__linux__)
153 if (fake_sparc && (fake_sparc[0] | 0x20) == 'y') { 168 {
154 strcpy(uname_info.name.machine, "sparc"); 169 char *fake_sparc = getenv("FAKE_SPARC");
155 } 170 if (fake_sparc && (fake_sparc[0] | 0x20) == 'y') {
156#endif 171 strcpy(uname_info.name.machine, "sparc");
157 strcpy(uname_info.processor, unknown_str); 172 }
158 strcpy(uname_info.platform, unknown_str);
159 strcpy(uname_info.os, CONFIG_UNAME_OSNAME);
160#if 0
161 /* Fedora does something like this */
162 strcpy(uname_info.processor, uname_info.name.machine);
163 strcpy(uname_info.platform, uname_info.name.machine);
164 if (uname_info.platform[0] == 'i'
165 && uname_info.platform[1]
166 && uname_info.platform[2] == '8'
167 && uname_info.platform[3] == '6'
168 ) {
169 uname_info.platform[1] = '3';
170 } 173 }
171#endif 174#endif
172 175 if (ENABLE_BBARCH && (!ENABLE_UNAME || applet_name[0] == 'a')) {
173 delta = utsname_offset; 176 puts(uname_info.name.machine);
174 fmt = " %s" + 1; 177 } else {
175 do { 178#if ENABLE_UNAME
176 if (toprint & 1) { 179 /* "uname" */
177 const char *p = (char *)(&uname_info) + *delta; 180 const char *fmt;
178 if (p[0]) { 181 const unsigned short *delta;
179 printf(fmt, p); 182
180 fmt = " %s"; 183 strcpy(uname_info.processor, unknown_str);
181 } 184 strcpy(uname_info.platform, unknown_str);
185 strcpy(uname_info.os, CONFIG_UNAME_OSNAME);
186# if 0
187 /* Fedora does something like this */
188 strcpy(uname_info.processor, uname_info.name.machine);
189 strcpy(uname_info.platform, uname_info.name.machine);
190 if (uname_info.platform[0] == 'i'
191 && uname_info.platform[1]
192 && uname_info.platform[2] == '8'
193 && uname_info.platform[3] == '6'
194 ) {
195 uname_info.platform[1] = '3';
182 } 196 }
183 ++delta; 197# endif
184 } while (toprint >>= 1); 198 delta = utsname_offset;
185 bb_putchar('\n'); 199 fmt = " %s" + 1;
200 do {
201 if (toprint & 1) {
202 const char *p = (char *)(&uname_info) + *delta;
203 if (p[0]) {
204 printf(fmt, p);
205 fmt = " %s";
206 }
207 }
208 ++delta;
209 } while (toprint >>= 1);
210 bb_putchar('\n');
211#endif
212 }
186 213
187 fflush_stdout_and_exit(EXIT_SUCCESS); /* coreutils-6.9 compat */ 214 fflush_stdout_and_exit(EXIT_SUCCESS); /* coreutils-6.9 compat */
188} 215}
diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c
index ddce2548b..2fe771f69 100644
--- a/coreutils/uudecode.c
+++ b/coreutils/uudecode.c
@@ -47,10 +47,16 @@ static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags U
47 line = xmalloc_fgets_str_len(src_stream, "\n", &line_len); 47 line = xmalloc_fgets_str_len(src_stream, "\n", &line_len);
48 if (!line) 48 if (!line)
49 break; 49 break;
50 /* Handle both Unix and MSDOS text, and stray trailing spaces */ 50 /* Handle both Unix and MSDOS text.
51 * Note: space should not be trimmed, some encoders use it instead of "`"
52 * for padding of last incomplete 4-char block.
53 */
51 str_len = line_len; 54 str_len = line_len;
52 while (--str_len >= 0 && isspace(line[str_len])) 55 while (--str_len >= 0
56 && (line[str_len] == '\n' || line[str_len] == '\r')
57 ) {
53 line[str_len] = '\0'; 58 line[str_len] = '\0';
59 }
54 60
55 if (strcmp(line, "end") == 0) { 61 if (strcmp(line, "end") == 0) {
56 return; /* the only non-error exit */ 62 return; /* the only non-error exit */
@@ -65,7 +71,7 @@ static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags U
65 71
66 encoded_len = line[0] * 4 / 3; 72 encoded_len = line[0] * 4 / 3;
67 /* Check that line is not too short. (we tolerate 73 /* Check that line is not too short. (we tolerate
68 * overly _long_ line to accommodate possible extra '`'). 74 * overly _long_ line to accommodate possible extra "`").
69 * Empty line case is also caught here. */ 75 * Empty line case is also caught here. */
70 if (str_len <= encoded_len) { 76 if (str_len <= encoded_len) {
71 break; /* go to bb_error_msg_and_die("short file"); */ 77 break; /* go to bb_error_msg_and_die("short file"); */
diff --git a/examples/mdev.conf.change_blockdev.sh b/examples/mdev.conf.change_blockdev.sh
index 512e43fcc..252d30f53 100755
--- a/examples/mdev.conf.change_blockdev.sh
+++ b/examples/mdev.conf.change_blockdev.sh
@@ -14,7 +14,7 @@ env | sort
14 14
15while sleep 1; test $cnt != 0; do 15while sleep 1; test $cnt != 0; do
16 echo "Trying to reread partition table on $DEVNAME ($cnt)" 16 echo "Trying to reread partition table on $DEVNAME ($cnt)"
17 : $((cnt--)) 17 cnt=$((cnt-1))
18 # If device node doesn't exist, it means the device was removed. 18 # If device node doesn't exist, it means the device was removed.
19 # Stop trying. 19 # Stop trying.
20 test -e "$DEVNAME" || { echo "$DEVNAME doesn't exist, aborting"; exit 1; } 20 test -e "$DEVNAME" || { echo "$DEVNAME doesn't exist, aborting"; exit 1; }
diff --git a/examples/var_service/dhcp_if_pinger/run b/examples/var_service/dhcp_if_pinger/run
index e0e87a16a..8aca90c1a 100755
--- a/examples/var_service/dhcp_if_pinger/run
+++ b/examples/var_service/dhcp_if_pinger/run
@@ -30,7 +30,7 @@ test x"$router" != x"" || exec env - sleep "$ping_time"
30failcnt=0 30failcnt=0
31while true; do 31while true; do
32 ping $ping_opts "$router" && exec env - sleep "$ping_time" 32 ping $ping_opts "$router" && exec env - sleep "$ping_time"
33 : $((failcnt++)) 33 failcnt=$((failcnt+1))
34 msg "Failed to ping $router, fail count:$failcnt" 34 msg "Failed to ping $router, fail count:$failcnt"
35 test $failcnt -ge $max_fail && break 35 test $failcnt -ge $max_fail && break
36 env - sleep "$retry_time" 36 env - sleep "$retry_time"
diff --git a/include/libbb.h b/include/libbb.h
index 0bc8cf29e..c58cbc250 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -801,6 +801,8 @@ ssize_t recv_from_to(int fd, void *buf, size_t len, int flags,
801 801
802uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; 802uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC;
803 803
804/* 0 if argv[0] is NULL: */
805unsigned string_array_len(char **argv) FAST_FUNC;
804void overlapping_strcpy(char *dst, const char *src) FAST_FUNC; 806void overlapping_strcpy(char *dst, const char *src) FAST_FUNC;
805char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC; 807char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC;
806char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC; 808char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC;
@@ -1057,6 +1059,15 @@ void die_if_bad_username(const char* name) FAST_FUNC;
1057#else 1059#else
1058#define die_if_bad_username(name) ((void)(name)) 1060#define die_if_bad_username(name) ((void)(name))
1059#endif 1061#endif
1062/*
1063 * Returns (-1) terminated malloced result of getgroups().
1064 * Reallocs group_array (useful for repeated calls).
1065 * ngroups is an initial size of array. It is rounded up to 32 for realloc.
1066 * ngroups is updated on return.
1067 * ngroups can be NULL: bb_getgroups(NULL, NULL) is valid usage.
1068 * Dies on errors (on Linux, only xrealloc can cause this, not internal getgroups call).
1069 */
1070gid_t *bb_getgroups(int *ngroups, gid_t *group_array) FAST_FUNC;
1060 1071
1061#if ENABLE_FEATURE_UTMP 1072#if ENABLE_FEATURE_UTMP
1062void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); 1073void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
@@ -1132,7 +1143,7 @@ int spawn_and_wait(char **argv) FAST_FUNC;
1132int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; 1143int run_nofork_applet(int applet_no, char **argv) FAST_FUNC;
1133#ifndef BUILD_INDIVIDUAL 1144#ifndef BUILD_INDIVIDUAL
1134extern int find_applet_by_name(const char *name) FAST_FUNC; 1145extern int find_applet_by_name(const char *name) FAST_FUNC;
1135extern void run_applet_no_and_exit(int a, char **argv) NORETURN FAST_FUNC; 1146extern void run_applet_no_and_exit(int a, const char *name, char **argv) NORETURN FAST_FUNC;
1136#endif 1147#endif
1137 1148
1138/* Helpers for daemonization. 1149/* Helpers for daemonization.
diff --git a/include/platform.h b/include/platform.h
index 13f818202..5ae82427a 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -264,6 +264,12 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING;
264} while (0) 264} while (0)
265#endif 265#endif
266 266
267/* Unaligned, fixed-endian accessors */
268#define get_unaligned_le32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_LE32(v); })
269#define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); })
270#define put_unaligned_le32(val, buf) move_to_unaligned32(buf, SWAP_LE32(val))
271#define put_unaligned_be32(val, buf) move_to_unaligned32(buf, SWAP_BE32(val))
272
267 273
268/* ---- Size-saving "small" ints (arch-dependent) ----------- */ 274/* ---- Size-saving "small" ints (arch-dependent) ----------- */
269 275
diff --git a/libbb/Config.src b/libbb/Config.src
index c51640305..16c79dbf0 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -3,7 +3,7 @@
3# see scripts/kbuild/config-language.txt. 3# see scripts/kbuild/config-language.txt.
4# 4#
5 5
6menu "Busybox Library Tuning" 6comment "Library Tuning"
7 7
8INSERT 8INSERT
9 9
@@ -66,7 +66,7 @@ config FEATURE_FAST_TOP
66 bool "Faster /proc scanning code (+100 bytes)" 66 bool "Faster /proc scanning code (+100 bytes)"
67 default n # all "fast or small" options default to small 67 default n # all "fast or small" options default to small
68 help 68 help
69 This option makes top (and ps) ~20% faster (or 20% less CPU hungry), 69 This option makes top and ps ~20% faster (or 20% less CPU hungry),
70 but code size is slightly bigger. 70 but code size is slightly bigger.
71 71
72config FEATURE_ETC_NETWORKS 72config FEATURE_ETC_NETWORKS
@@ -302,13 +302,17 @@ config FEATURE_VERBOSE_CP_MESSAGE
302 default n 302 default n
303 help 303 help
304 Error messages with this feature enabled: 304 Error messages with this feature enabled:
305
305 $ cp file /does_not_exist/file 306 $ cp file /does_not_exist/file
306 cp: cannot create '/does_not_exist/file': Path does not exist 307 cp: cannot create '/does_not_exist/file': Path does not exist
307 $ cp file /vmlinuz/file 308 $ cp file /vmlinuz/file
308 cp: cannot stat '/vmlinuz/file': Path has non-directory component 309 cp: cannot stat '/vmlinuz/file': Path has non-directory component
310
309 If this feature is not enabled, they will be, respectively: 311 If this feature is not enabled, they will be, respectively:
312
310 cp: cannot create '/does_not_exist/file': No such file or directory 313 cp: cannot create '/does_not_exist/file': No such file or directory
311 cp: cannot stat '/vmlinuz/file': Not a directory 314 cp: cannot stat '/vmlinuz/file': Not a directory
315
312 This will cost you ~60 bytes. 316 This will cost you ~60 bytes.
313 317
314config FEATURE_USE_SENDFILE 318config FEATURE_USE_SENDFILE
@@ -376,7 +380,4 @@ config FEATURE_HWIB
376 bool "Support infiniband HW" 380 bool "Support infiniband HW"
377 default y 381 default y
378 help 382 help
379 Support for printing infiniband addresses in 383 Support for printing infiniband addresses in network applets.
380 network applets.
381
382endmenu
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index a31a73e90..34b73afa5 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -78,6 +78,17 @@
78#endif 78#endif
79 79
80 80
81unsigned FAST_FUNC string_array_len(char **argv)
82{
83 char **start = argv;
84
85 while (*argv)
86 argv++;
87
88 return argv - start;
89}
90
91
81#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE 92#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
82static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; 93static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
83#else 94#else
@@ -675,8 +686,21 @@ static void check_suid(int applet_no)
675 if (geteuid()) 686 if (geteuid())
676 bb_error_msg_and_die("must be suid to work properly"); 687 bb_error_msg_and_die("must be suid to work properly");
677 } else if (APPLET_SUID(applet_no) == BB_SUID_DROP) { 688 } else if (APPLET_SUID(applet_no) == BB_SUID_DROP) {
678 xsetgid(rgid); /* drop all privileges */ 689 /*
679 xsetuid(ruid); 690 * Drop all privileges.
691 *
692 * Don't check for errors: in normal use, they are impossible,
693 * and in special cases, exiting is harmful. Example:
694 * 'unshare --user' when user's shell is also from busybox.
695 *
696 * 'unshare --user' creates a new user namespace without any
697 * uid mappings. Thus, busybox binary is setuid nobody:nogroup
698 * within the namespace, as that is the only user. However,
699 * since no uids are mapped, calls to setgid/setuid
700 * fail (even though they would do nothing).
701 */
702 setgid(rgid);
703 setuid(ruid);
680 } 704 }
681# if ENABLE_FEATURE_SUID_CONFIG 705# if ENABLE_FEATURE_SUID_CONFIG
682 ret: ; 706 ret: ;
@@ -919,16 +943,17 @@ static int busybox_main(char **argv)
919# endif 943# endif
920 944
921# if NUM_APPLETS > 0 945# if NUM_APPLETS > 0
922void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) 946void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv)
923{ 947{
924 int argc = 1; 948 int argc = string_array_len(argv);
925
926 while (argv[argc])
927 argc++;
928 949
929 /* Reinit some shared global data */ 950 /* Reinit some shared global data */
930 xfunc_error_retval = EXIT_FAILURE; 951 xfunc_error_retval = EXIT_FAILURE;
931 applet_name = bb_get_last_path_component_nostrip(argv[0]); 952 /*
953 * We do not use argv[0]: do not want to repeat massaging of
954 * "-/sbin/halt" -> "halt", for example.
955 */
956 applet_name = name;
932 957
933 /* Special case. POSIX says "test --help" 958 /* Special case. POSIX says "test --help"
934 * should be no different from e.g. "test --foo". 959 * should be no different from e.g. "test --foo".
@@ -972,7 +997,7 @@ static NORETURN void run_applet_and_exit(const char *name, char **argv)
972 { 997 {
973 int applet = find_applet_by_name(name); 998 int applet = find_applet_by_name(name);
974 if (applet >= 0) 999 if (applet >= 0)
975 run_applet_no_and_exit(applet, argv); 1000 run_applet_no_and_exit(applet, name, argv);
976 } 1001 }
977# endif 1002# endif
978 1003
@@ -1058,7 +1083,11 @@ int main(int argc UNUSED_PARAM, char **argv)
1058 } 1083 }
1059 /* applet_names in this case is just "applet\0\0" */ 1084 /* applet_names in this case is just "applet\0\0" */
1060 lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv)); 1085 lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
1086# if ENABLE_BUILD_LIBBUSYBOX
1087 return SINGLE_APPLET_MAIN(string_array_len(argv), argv);
1088# else
1061 return SINGLE_APPLET_MAIN(argc, argv); 1089 return SINGLE_APPLET_MAIN(argc, argv);
1090# endif
1062 1091
1063#elif !ENABLE_BUSYBOX && NUM_APPLETS == 0 1092#elif !ENABLE_BUSYBOX && NUM_APPLETS == 0
1064 1093
diff --git a/libbb/bb_getgroups.c b/libbb/bb_getgroups.c
new file mode 100644
index 000000000..59ae53738
--- /dev/null
+++ b/libbb/bb_getgroups.c
@@ -0,0 +1,47 @@
1/*
2 * Utility routines.
3 *
4 * Copyright (C) 2017 Denys Vlasenko
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
8
9//kbuild:lib-y += bb_getgroups.o
10
11#include "libbb.h"
12
13gid_t* FAST_FUNC bb_getgroups(int *ngroups, gid_t *group_array)
14{
15 int n = ngroups ? *ngroups : 0;
16
17 /* getgroups may be a bit expensive, try to use it only once */
18 if (n < 32)
19 n = 32;
20
21 for (;;) {
22// FIXME: ash tries so hard to not die on OOM (when we are called from test),
23// and we spoil it with just one xrealloc here
24 group_array = xrealloc(group_array, (n+1) * sizeof(group_array[0]));
25 n = getgroups(n, group_array);
26 /*
27 * If buffer is too small, kernel does not return new_n > n.
28 * It returns -1 and EINVAL:
29 */
30 if (n >= 0) {
31 /* Terminator for bb_getgroups(NULL, NULL) usage */
32 group_array[n] = (gid_t) -1;
33 break;
34 }
35 if (errno == EINVAL) { /* too small? */
36 /* This is the way to ask kernel how big the array is */
37 n = getgroups(0, group_array);
38 continue;
39 }
40 /* Some other error (should never happen on Linux) */
41 bb_perror_msg_and_die("getgroups");
42 }
43
44 if (ngroups)
45 *ngroups = n;
46 return group_array;
47}
diff --git a/libbb/common_bufsiz.c b/libbb/common_bufsiz.c
index 2847eb57d..f1124ba0e 100644
--- a/libbb/common_bufsiz.c
+++ b/libbb/common_bufsiz.c
@@ -19,6 +19,7 @@
19//config: 19//config:
20//config: At link time, "text" is padded to a full page. At runtime, all "text" 20//config: At link time, "text" is padded to a full page. At runtime, all "text"
21//config: pages are mapped RO and executable. 21//config: pages are mapped RO and executable.
22//config:
22//config: "Data" starts on the next page boundary, but is not padded 23//config: "Data" starts on the next page boundary, but is not padded
23//config: to a full page at the end. "Bss" starts wherever "data" ends. 24//config: to a full page at the end. "Bss" starts wherever "data" ends.
24//config: At runtime, "data" pages are mapped RW and they are file-backed 25//config: At runtime, "data" pages are mapped RW and they are file-backed
diff --git a/libbb/copyfd.c b/libbb/copyfd.c
index 7e3531903..921fe3f81 100644
--- a/libbb/copyfd.c
+++ b/libbb/copyfd.c
@@ -119,8 +119,11 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
119 } 119 }
120 out: 120 out:
121 121
122/* some environments don't have munmap(), hide it in #if */
123#if CONFIG_FEATURE_COPYBUF_KB > 4
122 if (buffer_size > 4 * 1024) 124 if (buffer_size > 4 * 1024)
123 munmap(buffer, buffer_size); 125 munmap(buffer, buffer_size);
126#endif
124 return status ? -1 : total; 127 return status ? -1 : total;
125} 128}
126 129
diff --git a/libbb/dump.c b/libbb/dump.c
index 87c1dce13..211a1ed9e 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -58,7 +58,7 @@ static NOINLINE int bb_dump_size(FS *fs)
58 const char *p; 58 const char *p;
59 int prec; 59 int prec;
60 60
61 /* figure out the data block bb_dump_size needed for each format unit */ 61 /* figure out the data block size needed for each format unit */
62 for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { 62 for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
63 if (fu->bcnt) { 63 if (fu->bcnt) {
64 cur_size += fu->bcnt * fu->reps; 64 cur_size += fu->bcnt * fu->reps;
@@ -311,20 +311,18 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
311 } 311 }
312} 312}
313 313
314static void do_skip(priv_dumper_t *dumper, const char *fname, int statok) 314static void do_skip(priv_dumper_t *dumper, const char *fname)
315{ 315{
316 struct stat sbuf; 316 struct stat sbuf;
317 317
318 if (statok) { 318 xfstat(STDIN_FILENO, &sbuf, fname);
319 xfstat(STDIN_FILENO, &sbuf, fname); 319 if (S_ISREG(sbuf.st_mode)
320 if (!(S_ISCHR(sbuf.st_mode) || S_ISBLK(sbuf.st_mode) || S_ISFIFO(sbuf.st_mode)) 320 && dumper->pub.dump_skip >= sbuf.st_size
321 && dumper->pub.dump_skip >= sbuf.st_size 321 ) {
322 ) { 322 /* If st_size is valid and pub.dump_skip >= st_size */
323 /* If bb_dump_size valid and pub.dump_skip >= size */ 323 dumper->pub.dump_skip -= sbuf.st_size;
324 dumper->pub.dump_skip -= sbuf.st_size; 324 dumper->address += sbuf.st_size;
325 dumper->address += sbuf.st_size; 325 return;
326 return;
327 }
328 } 326 }
329 if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) { 327 if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) {
330 bb_simple_perror_msg_and_die(fname); 328 bb_simple_perror_msg_and_die(fname);
@@ -336,29 +334,26 @@ static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
336 334
337static NOINLINE int next(priv_dumper_t *dumper) 335static NOINLINE int next(priv_dumper_t *dumper)
338{ 336{
339 int statok;
340
341 for (;;) { 337 for (;;) {
342 if (*dumper->argv) { 338 const char *fname = *dumper->argv;
343 dumper->next__done = statok = 1; 339
344 if (!(freopen(*dumper->argv, "r", stdin))) { 340 if (fname) {
345 bb_simple_perror_msg(*dumper->argv); 341 dumper->argv++;
346 dumper->exitval = 1; 342 if (NOT_LONE_DASH(fname)) {
347 ++dumper->argv; 343 if (!freopen(fname, "r", stdin)) {
348 continue; 344 bb_simple_perror_msg(fname);
345 dumper->exitval = 1;
346 continue;
347 }
349 } 348 }
350 } else { 349 } else {
351 if (dumper->next__done) 350 if (dumper->next__done)
352 return 0; /* no next file */ 351 return 0; /* no next file */
353 dumper->next__done = 1;
354//why stat of stdin is specially prohibited?
355 statok = 0;
356 } 352 }
353 dumper->next__done = 1;
357 if (dumper->pub.dump_skip) 354 if (dumper->pub.dump_skip)
358 do_skip(dumper, statok ? *dumper->argv : "stdin", statok); 355 do_skip(dumper, fname ? fname : "stdin");
359 if (*dumper->argv) 356 if (dumper->pub.dump_skip == 0)
360 ++dumper->argv;
361 if (!dumper->pub.dump_skip)
362 return 1; 357 return 1;
363 } 358 }
364 /* NOTREACHED */ 359 /* NOTREACHED */
@@ -670,7 +665,7 @@ int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
670 FS *tfs; 665 FS *tfs;
671 int blocksize; 666 int blocksize;
672 667
673 /* figure out the data block bb_dump_size */ 668 /* figure out the data block size */
674 blocksize = 0; 669 blocksize = 0;
675 tfs = dumper->pub.fshead; 670 tfs = dumper->pub.fshead;
676 while (tfs) { 671 while (tfs) {
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index b87b83538..80f4cc060 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -379,9 +379,7 @@ getopt32(char **argv, const char *applet_opts, ...)
379 int spec_flgs = 0; 379 int spec_flgs = 0;
380 380
381 /* skip 0: some applets cheat: they do not actually HAVE argv[0] */ 381 /* skip 0: some applets cheat: they do not actually HAVE argv[0] */
382 argc = 1; 382 argc = 1 + string_array_len(argv + 1);
383 while (argv[argc])
384 argc++;
385 383
386 va_start(p, applet_opts); 384 va_start(p, applet_opts);
387 385
diff --git a/libbb/inet_common.c b/libbb/inet_common.c
index 5b4a4a10b..04259f47b 100644
--- a/libbb/inet_common.c
+++ b/libbb/inet_common.c
@@ -11,6 +11,12 @@
11#include "libbb.h" 11#include "libbb.h"
12#include "inet_common.h" 12#include "inet_common.h"
13 13
14#if 0
15# define dbg(...) bb_error_msg(__VA_ARGS__)
16#else
17# define dbg(...) ((void)0)
18#endif
19
14int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) 20int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst)
15{ 21{
16 struct hostent *hp; 22 struct hostent *hp;
@@ -33,9 +39,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
33 } 39 }
34 /* If we expect this to be a hostname, try hostname database first */ 40 /* If we expect this to be a hostname, try hostname database first */
35 if (hostfirst) { 41 if (hostfirst) {
36#ifdef DEBUG 42 dbg("gethostbyname(%s)", name);
37 bb_error_msg("gethostbyname(%s)", name);
38#endif
39 hp = gethostbyname(name); 43 hp = gethostbyname(name);
40 if (hp) { 44 if (hp) {
41 memcpy(&s_in->sin_addr, hp->h_addr_list[0], 45 memcpy(&s_in->sin_addr, hp->h_addr_list[0],
@@ -45,9 +49,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
45 } 49 }
46#if ENABLE_FEATURE_ETC_NETWORKS 50#if ENABLE_FEATURE_ETC_NETWORKS
47 /* Try the NETWORKS database to see if this is a known network. */ 51 /* Try the NETWORKS database to see if this is a known network. */
48#ifdef DEBUG 52 dbg("getnetbyname(%s)", name);
49 bb_error_msg("getnetbyname(%s)", name);
50#endif
51 np = getnetbyname(name); 53 np = getnetbyname(name);
52 if (np) { 54 if (np) {
53 s_in->sin_addr.s_addr = htonl(np->n_net); 55 s_in->sin_addr.s_addr = htonl(np->n_net);
@@ -61,8 +63,8 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
61#ifdef DEBUG 63#ifdef DEBUG
62 res_init(); 64 res_init();
63 _res.options |= RES_DEBUG; 65 _res.options |= RES_DEBUG;
64 bb_error_msg("gethostbyname(%s)", name);
65#endif 66#endif
67 dbg("gethostbyname(%s)", name);
66 hp = gethostbyname(name); 68 hp = gethostbyname(name);
67 if (!hp) { 69 if (!hp) {
68 return -1; 70 return -1;
@@ -93,17 +95,12 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
93 smallint is_host; 95 smallint is_host;
94 96
95 if (s_in->sin_family != AF_INET) { 97 if (s_in->sin_family != AF_INET) {
96#ifdef DEBUG 98 dbg("rresolve: unsupported address family %d!", s_in->sin_family);
97 bb_error_msg("rresolve: unsupported address family %d!",
98 s_in->sin_family);
99#endif
100 errno = EAFNOSUPPORT; 99 errno = EAFNOSUPPORT;
101 return NULL; 100 return NULL;
102 } 101 }
103 nip = s_in->sin_addr.s_addr; 102 nip = s_in->sin_addr.s_addr;
104#ifdef DEBUG 103 dbg("rresolve: %08x mask:%08x num:%08x", (unsigned)nip, netmask, numeric);
105 bb_error_msg("rresolve: %08x mask:%08x num:%08x", (unsigned)nip, netmask, numeric);
106#endif
107 if (numeric & 0x0FFF) 104 if (numeric & 0x0FFF)
108 return xmalloc_sockaddr2dotted_noport((void*)s_in); 105 return xmalloc_sockaddr2dotted_noport((void*)s_in);
109 if (nip == INADDR_ANY) { 106 if (nip == INADDR_ANY) {
@@ -117,10 +114,8 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
117 pn = cache; 114 pn = cache;
118 while (pn) { 115 while (pn) {
119 if (pn->nip == nip && pn->is_host == is_host) { 116 if (pn->nip == nip && pn->is_host == is_host) {
120#ifdef DEBUG 117 dbg("rresolve: found %s %08x in cache",
121 bb_error_msg("rresolve: found %s %08x in cache",
122 (is_host ? "host" : "net"), (unsigned)nip); 118 (is_host ? "host" : "net"), (unsigned)nip);
123#endif
124 return xstrdup(pn->name); 119 return xstrdup(pn->name);
125 } 120 }
126 pn = pn->next; 121 pn = pn->next;
@@ -128,19 +123,18 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
128 123
129 name = NULL; 124 name = NULL;
130 if (is_host) { 125 if (is_host) {
131#ifdef DEBUG 126 dbg("sockaddr2host_noport(%08x)", (unsigned)nip);
132 bb_error_msg("sockaddr2host_noport(%08x)", (unsigned)nip);
133#endif
134 name = xmalloc_sockaddr2host_noport((void*)s_in); 127 name = xmalloc_sockaddr2host_noport((void*)s_in);
135 } else if (ENABLE_FEATURE_ETC_NETWORKS) { 128 }
129#if ENABLE_FEATURE_ETC_NETWORKS
130 else {
136 struct netent *np; 131 struct netent *np;
137#ifdef DEBUG 132 dbg("getnetbyaddr(%08x)", (unsigned)ntohl(nip));
138 bb_error_msg("getnetbyaddr(%08x)", (unsigned)ntohl(nip));
139#endif
140 np = getnetbyaddr(ntohl(nip), AF_INET); 133 np = getnetbyaddr(ntohl(nip), AF_INET);
141 if (np) 134 if (np)
142 name = xstrdup(np->n_name); 135 name = xstrdup(np->n_name);
143 } 136 }
137#endif
144 if (!name) 138 if (!name)
145 name = xmalloc_sockaddr2dotted_noport((void*)s_in); 139 name = xmalloc_sockaddr2dotted_noport((void*)s_in);
146 140
@@ -183,10 +177,8 @@ int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
183char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) 177char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
184{ 178{
185 if (sin6->sin6_family != AF_INET6) { 179 if (sin6->sin6_family != AF_INET6) {
186#ifdef DEBUG 180 dbg("rresolve: unsupported address family %d!",
187 bb_error_msg("rresolve: unsupported address family %d!",
188 sin6->sin6_family); 181 sin6->sin6_family);
189#endif
190 errno = EAFNOSUPPORT; 182 errno = EAFNOSUPPORT;
191 return NULL; 183 return NULL;
192 } 184 }
diff --git a/libbb/messages.c b/libbb/messages.c
index d74c237e7..3c0b921cf 100644
--- a/libbb/messages.c
+++ b/libbb/messages.c
@@ -14,12 +14,10 @@
14 14
15/* allow version to be extended, via CFLAGS */ 15/* allow version to be extended, via CFLAGS */
16#ifndef BB_EXTRA_VERSION 16#ifndef BB_EXTRA_VERSION
17#define BB_EXTRA_VERSION BB_BT 17#define BB_EXTRA_VERSION " ("AUTOCONF_TIMESTAMP")"
18#endif 18#endif
19 19
20#define BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")" 20const char bb_banner[] ALIGN1 = "BusyBox v" BB_VER BB_EXTRA_VERSION;
21
22const char bb_banner[] ALIGN1 = BANNER;
23 21
24 22
25const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory"; 23const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory";
diff --git a/libbb/print_numbered_lines.c b/libbb/print_numbered_lines.c
index 702aed1ea..9a8a51440 100644
--- a/libbb/print_numbered_lines.c
+++ b/libbb/print_numbered_lines.c
@@ -24,6 +24,7 @@ void FAST_FUNC print_numbered_lines(struct number_state *ns, const char *filenam
24 fputs(ns->empty_str, stdout); 24 fputs(ns->empty_str, stdout);
25 free(line); 25 free(line);
26 } 26 }
27 ns->start = N;
27 28
28 fclose(fp); 29 fclose(fp);
29} 30}
diff --git a/libbb/safe_write.c b/libbb/safe_write.c
index 8f7628016..aad50f5e0 100644
--- a/libbb/safe_write.c
+++ b/libbb/safe_write.c
@@ -13,9 +13,17 @@ ssize_t FAST_FUNC safe_write(int fd, const void *buf, size_t count)
13{ 13{
14 ssize_t n; 14 ssize_t n;
15 15
16 do { 16 for (;;) {
17 n = write(fd, buf, count); 17 n = write(fd, buf, count);
18 } while (n < 0 && errno == EINTR); 18 if (n >= 0 || errno != EINTR)
19 break;
20 /* Some callers set errno=0, are upset when they see EINTR.
21 * Returning EINTR is wrong since we retry write(),
22 * the "error" was transient.
23 */
24 errno = 0;
25 /* repeat the write() */
26 }
19 27
20 return n; 28 return n;
21} 29}
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index 9ab49d0a1..4b3ed5a3b 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -16,6 +16,7 @@
16 */ 16 */
17 17
18#include "busybox.h" /* uses applet tables */ 18#include "busybox.h" /* uses applet tables */
19#include "NUM_APPLETS.h"
19 20
20#if !ENABLE_PLATFORM_MINGW32 21#if !ENABLE_PLATFORM_MINGW32
21/* This does a fork/exec in one call, using vfork(). Returns PID of new child, 22/* This does a fork/exec in one call, using vfork(). Returns PID of new child,
@@ -158,7 +159,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
158int FAST_FUNC spawn_and_wait(char **argv) 159int FAST_FUNC spawn_and_wait(char **argv)
159{ 160{
160 int rc; 161 int rc;
161#if ENABLE_FEATURE_PREFER_APPLETS 162#if ENABLE_FEATURE_PREFER_APPLETS && (NUM_APPLETS > 1)
162 int a = find_applet_by_name(argv[0]); 163 int a = find_applet_by_name(argv[0]);
163 164
164 if (a >= 0) { 165 if (a >= 0) {
@@ -182,7 +183,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
182 * as of yet (and that should probably always stay true). 183 * as of yet (and that should probably always stay true).
183 */ 184 */
184 /* xfunc_error_retval and applet_name are init by: */ 185 /* xfunc_error_retval and applet_name are init by: */
185 run_applet_no_and_exit(a, argv); 186 run_applet_no_and_exit(a, argv[0], argv);
186 } 187 }
187# endif 188# endif
188# endif 189# endif
diff --git a/loginutils/add-remove-shell.c b/loginutils/add-remove-shell.c
index 922b3333d..54b62c773 100644
--- a/loginutils/add-remove-shell.c
+++ b/loginutils/add-remove-shell.c
@@ -84,7 +84,7 @@ int add_remove_shell_main(int argc UNUSED_PARAM, char **argv)
84 while ((line = xmalloc_fgetline(orig_fp)) != NULL) { 84 while ((line = xmalloc_fgetline(orig_fp)) != NULL) {
85 char **cpp = argv; 85 char **cpp = argv;
86 while (*cpp) { 86 while (*cpp) {
87 if (strcmp(*cpp, line) == 0) { 87 if (*cpp != dont_add && strcmp(*cpp, line) == 0) {
88 /* Old file has this shell name */ 88 /* Old file has this shell name */
89 if (REMOVE_SHELL) { 89 if (REMOVE_SHELL) {
90 /* we are remove-shell */ 90 /* we are remove-shell */
diff --git a/make_single_applets.sh b/make_single_applets.sh
index 8ad7a7406..329a27d32 100755
--- a/make_single_applets.sh
+++ b/make_single_applets.sh
@@ -54,18 +54,18 @@ for app; do
54 fi 54 fi
55 55
56 if ! yes '' | make oldconfig >busybox_make_${app}.log 2>&1; then 56 if ! yes '' | make oldconfig >busybox_make_${app}.log 2>&1; then
57 : $((fail++)) 57 fail=$((fail+1))
58 echo "Config error for ${app}" 58 echo "Config error for ${app}"
59 mv .config busybox_config_${app} 59 mv .config busybox_config_${app}
60 elif ! make $makeopts >>busybox_make_${app}.log 2>&1; then 60 elif ! make $makeopts >>busybox_make_${app}.log 2>&1; then
61 : $((fail++)) 61 fail=$((fail+1))
62 grep -i -e error: -e warning: busybox_make_${app}.log 62 grep -i -e error: -e warning: busybox_make_${app}.log
63 echo "Build error for ${app}" 63 echo "Build error for ${app}"
64 mv .config busybox_config_${app} 64 mv .config busybox_config_${app}
65 elif ! grep -q '^#define NUM_APPLETS 1$' include/NUM_APPLETS.h; then 65 elif ! grep -q '^#define NUM_APPLETS 1$' include/NUM_APPLETS.h; then
66 grep -i -e error: -e warning: busybox_make_${app}.log 66 grep -i -e error: -e warning: busybox_make_${app}.log
67 mv busybox busybox_${app} 67 mv busybox busybox_${app}
68 : $((fail++)) 68 fail=$((fail+1))
69 echo "NUM_APPLETS != 1 for ${app}: `cat include/NUM_APPLETS.h`" 69 echo "NUM_APPLETS != 1 for ${app}: `cat include/NUM_APPLETS.h`"
70 mv .config busybox_config_${app} 70 mv .config busybox_config_${app}
71 else 71 else
diff --git a/miscutils/beep.c b/miscutils/beep.c
index 14802b543..216f69400 100644
--- a/miscutils/beep.c
+++ b/miscutils/beep.c
@@ -16,7 +16,7 @@
16//config: 16//config:
17//config:config FEATURE_BEEP_FREQ 17//config:config FEATURE_BEEP_FREQ
18//config: int "default frequency" 18//config: int "default frequency"
19//config: range 0 2147483647 19//config: range 20 50000 # allowing 0 here breaks the build
20//config: default 4000 20//config: default 4000
21//config: depends on BEEP 21//config: depends on BEEP
22//config: help 22//config: help
diff --git a/miscutils/crond.c b/miscutils/crond.c
index 88e7b47b3..c0c8bef11 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -35,6 +35,22 @@
35//config: help 35//config: help
36//config: Command output will be sent to corresponding user via email. 36//config: Command output will be sent to corresponding user via email.
37//config: 37//config:
38//config:config FEATURE_CROND_SPECIAL_TIMES
39//config: bool "Support special times (@reboot, @daily, etc) in crontabs"
40//config: default y
41//config: depends on CROND
42//config: help
43//config: string meaning
44//config: ------ -------
45//config: @reboot Run once, at startup
46//config: @yearly Run once a year: "0 0 1 1 *"
47//config: @annually Same as @yearly: "0 0 1 1 *"
48//config: @monthly Run once a month: "0 0 1 * *"
49//config: @weekly Run once a week: "0 0 * * 0"
50//config: @daily Run once a day: "0 0 * * *"
51//config: @midnight Same as @daily: "0 0 * * *"
52//config: @hourly Run once an hour: "0 * * * *"
53//config:
38//config:config FEATURE_CROND_DIR 54//config:config FEATURE_CROND_DIR
39//config: string "crond spool directory" 55//config: string "crond spool directory"
40//config: default "/var/spool/cron" 56//config: default "/var/spool/cron"
@@ -74,6 +90,7 @@
74 90
75#define CRON_DIR CONFIG_FEATURE_CROND_DIR 91#define CRON_DIR CONFIG_FEATURE_CROND_DIR
76#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" 92#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
93#define CRON_REBOOT CONFIG_PID_FILE_PATH "/crond.reboot"
77#ifndef SENDMAIL 94#ifndef SENDMAIL
78# define SENDMAIL "sendmail" 95# define SENDMAIL "sendmail"
79#endif 96#endif
@@ -101,6 +118,8 @@ typedef struct CronLine {
101 struct CronLine *cl_next; 118 struct CronLine *cl_next;
102 char *cl_cmd; /* shell command */ 119 char *cl_cmd; /* shell command */
103 pid_t cl_pid; /* >0:running, <0:needs to be started in this minute, 0:dormant */ 120 pid_t cl_pid; /* >0:running, <0:needs to be started in this minute, 0:dormant */
121#define START_ME_REBOOT -2
122#define START_ME_NORMAL -1
104#if ENABLE_FEATURE_CROND_CALL_SENDMAIL 123#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
105 int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */ 124 int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */
106 char *cl_mailto; /* whom to mail results, may be NULL */ 125 char *cl_mailto; /* whom to mail results, may be NULL */
@@ -465,21 +484,85 @@ static void load_crontab(const char *fileName)
465//line of the crontab's owner. HOME and SHELL may be overridden by settings 484//line of the crontab's owner. HOME and SHELL may be overridden by settings
466//in the crontab; LOGNAME may not. 485//in the crontab; LOGNAME may not.
467 486
487#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
488 if (tokens[0][0] == '@') {
489 /*
490 * "@daily /a/script/to/run PARAM1 PARAM2..."
491 */
492 typedef struct SpecialEntry {
493 const char *name;
494 const char tokens[8];
495 } SpecialEntry;
496 static const SpecialEntry SpecAry[] = {
497 /* hour day month weekday */
498 { "yearly", "0\0" "1\0" "1\0" "*" },
499 { "annually", "0\0" "1\0" "1\0" "*" },
500 { "monthly", "0\0" "1\0" "*\0" "*" },
501 { "weekly", "0\0" "*\0" "*\0" "0" },
502 { "daily", "0\0" "*\0" "*\0" "*" },
503 { "midnight", "0\0" "*\0" "*\0" "*" },
504 { "hourly", "*\0" "*\0" "*\0" "*" },
505 { "reboot", "" },
506 };
507 const SpecialEntry *e = SpecAry;
508
509 if (n < 2)
510 continue;
511 for (;;) {
512 if (strcmp(e->name, tokens[0] + 1) == 0) {
513 /*
514 * tokens[1] is only the first word of command,
515 * can'r use it.
516 * find the entire command in unmodified string:
517 */
518 tokens[5] = skip_whitespace(
519 skip_non_whitespace(
520 skip_whitespace(parser->data)));
521 if (e->tokens[0]) {
522 char *et = (char*)e->tokens;
523 /* minute is "0" for all specials */
524 tokens[0] = (char*)"0";
525 tokens[1] = et;
526 tokens[2] = et + 2;
527 tokens[3] = et + 4;
528 tokens[4] = et + 6;
529 }
530 goto got_it;
531 }
532 if (!e->tokens[0])
533 break;
534 e++;
535 }
536 continue; /* bad line (unrecognized '@foo') */
537 }
538#endif
468 /* check if a minimum of tokens is specified */ 539 /* check if a minimum of tokens is specified */
469 if (n < 6) 540 if (n < 6)
470 continue; 541 continue;
542 IF_FEATURE_CROND_SPECIAL_TIMES(
543 got_it:
544 )
471 *pline = line = xzalloc(sizeof(*line)); 545 *pline = line = xzalloc(sizeof(*line));
472 /* parse date ranges */ 546#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
473 ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); 547 if (tokens[0][0] == '@') { /* "@reboot" line */
474 ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); 548 file->cf_wants_starting = 1;
475 ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); 549 line->cl_pid = START_ME_REBOOT; /* wants to start */
476 ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); 550 /* line->cl_Mins/Hrs/etc stay zero: never match any time */
477 ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); 551 } else
478 /* 552#endif
479 * fix days and dow - if one is not "*" and the other 553 {
480 * is "*", the other is set to 0, and vise-versa 554 /* parse date ranges */
481 */ 555 ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]);
482 FixDayDow(line); 556 ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]);
557 ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]);
558 ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]);
559 ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]);
560 /*
561 * fix days and dow - if one is not "*" and the other
562 * is "*", the other is set to 0, and vise-versa
563 */
564 FixDayDow(line);
565 }
483#if ENABLE_FEATURE_CROND_CALL_SENDMAIL 566#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
484 /* copy mailto (can be NULL) */ 567 /* copy mailto (can be NULL) */
485 line->cl_mailto = xstrdup(mailTo); 568 line->cl_mailto = xstrdup(mailTo);
@@ -664,7 +747,7 @@ fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail)
664 return pid; 747 return pid;
665} 748}
666 749
667static void start_one_job(const char *user, CronLine *line) 750static pid_t start_one_job(const char *user, CronLine *line)
668{ 751{
669 char mailFile[128]; 752 char mailFile[128];
670 int mailFd = -1; 753 int mailFd = -1;
@@ -698,6 +781,8 @@ static void start_one_job(const char *user, CronLine *line)
698 free(mailFile2); 781 free(mailFile2);
699 } 782 }
700 } 783 }
784
785 return line->cl_pid;
701} 786}
702 787
703/* 788/*
@@ -748,7 +833,7 @@ static void process_finished_job(const char *user, CronLine *line)
748 833
749#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ 834#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
750 835
751static void start_one_job(const char *user, CronLine *line) 836static pid_t start_one_job(const char *user, CronLine *line)
752{ 837{
753 const char *shell; 838 const char *shell;
754 struct passwd *pas; 839 struct passwd *pas;
@@ -782,6 +867,7 @@ static void start_one_job(const char *user, CronLine *line)
782 pid = 0; 867 pid = 0;
783 } 868 }
784 line->cl_pid = pid; 869 line->cl_pid = pid;
870 return pid;
785} 871}
786 872
787#define process_finished_job(user, line) ((line)->cl_pid = 0) 873#define process_finished_job(user, line) ((line)->cl_pid = 0)
@@ -825,7 +911,7 @@ static void flag_starting_jobs(time_t t1, time_t t2)
825 log8("user %s: process already running: %s", 911 log8("user %s: process already running: %s",
826 file->cf_username, line->cl_cmd); 912 file->cf_username, line->cl_cmd);
827 } else if (line->cl_pid == 0) { 913 } else if (line->cl_pid == 0) {
828 line->cl_pid = -1; 914 line->cl_pid = START_ME_NORMAL;
829 file->cf_wants_starting = 1; 915 file->cf_wants_starting = 1;
830 } 916 }
831 } 917 }
@@ -834,7 +920,20 @@ static void flag_starting_jobs(time_t t1, time_t t2)
834 } 920 }
835} 921}
836 922
837static void start_jobs(void) 923#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
924static int touch_reboot_file(void)
925{
926 int fd = open(CRON_REBOOT, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0000);
927 if (fd >= 0) {
928 close(fd);
929 return 1;
930 }
931 /* File (presumably) exists - this is not the first run after reboot */
932 return 0;
933}
934#endif
935
936static void start_jobs(int wants_start)
838{ 937{
839 CronFile *file; 938 CronFile *file;
840 CronLine *line; 939 CronLine *line;
@@ -846,11 +945,10 @@ static void start_jobs(void)
846 file->cf_wants_starting = 0; 945 file->cf_wants_starting = 0;
847 for (line = file->cf_lines; line; line = line->cl_next) { 946 for (line = file->cf_lines; line; line = line->cl_next) {
848 pid_t pid; 947 pid_t pid;
849 if (line->cl_pid >= 0) 948 if (line->cl_pid != wants_start)
850 continue; 949 continue;
851 950
852 start_one_job(file->cf_username, line); 951 pid = start_one_job(file->cf_username, line);
853 pid = line->cl_pid;
854 log8("USER %s pid %3d cmd %s", 952 log8("USER %s pid %3d cmd %s",
855 file->cf_username, (int)pid, line->cl_cmd); 953 file->cf_username, (int)pid, line->cl_cmd);
856 if (pid < 0) { 954 if (pid < 0) {
@@ -950,6 +1048,10 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
950 log8("crond (busybox "BB_VER") started, log level %d", G.log_level); 1048 log8("crond (busybox "BB_VER") started, log level %d", G.log_level);
951 rescan_crontab_dir(); 1049 rescan_crontab_dir();
952 write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); 1050 write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid");
1051#if ENABLE_FEATURE_CROND_SPECIAL_TIMES
1052 if (touch_reboot_file())
1053 start_jobs(START_ME_REBOOT); /* start @reboot entries, if any */
1054#endif
953 1055
954 /* Main loop */ 1056 /* Main loop */
955 t2 = time(NULL); 1057 t2 = time(NULL);
@@ -1002,7 +1104,7 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
1002 } else if (dt > 0) { 1104 } else if (dt > 0) {
1003 /* Usual case: time advances forward, as expected */ 1105 /* Usual case: time advances forward, as expected */
1004 flag_starting_jobs(t1, t2); 1106 flag_starting_jobs(t1, t2);
1005 start_jobs(); 1107 start_jobs(START_ME_NORMAL);
1006 sleep_time = 60; 1108 sleep_time = 60;
1007 if (check_completions() > 0) { 1109 if (check_completions() > 0) {
1008 /* some jobs are still running */ 1110 /* some jobs are still running */
diff --git a/miscutils/makedevs.c b/miscutils/makedevs.c
index 9e7ca340f..f436b08f8 100644
--- a/miscutils/makedevs.c
+++ b/miscutils/makedevs.c
@@ -208,17 +208,17 @@ int makedevs_main(int argc UNUSED_PARAM, char **argv)
208 unsigned count = 0; 208 unsigned count = 0;
209 unsigned increment = 0; 209 unsigned increment = 0;
210 unsigned start = 0; 210 unsigned start = 0;
211 char name[41];
212 char user[41]; 211 char user[41];
213 char group[41]; 212 char group[41];
214 char *full_name = name; 213 char *full_name;
214 int name_len;
215 uid_t uid; 215 uid_t uid;
216 gid_t gid; 216 gid_t gid;
217 217
218 linenum = parser->lineno; 218 linenum = parser->lineno;
219 219
220 if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", 220 if ((1 > sscanf(line, "%*s%n %c %o %40s %40s %u %u %u %u %u",
221 name, &type, &mode, user, group, 221 &name_len, &type, &mode, user, group,
222 &major, &minor, &start, &increment, &count)) 222 &major, &minor, &start, &increment, &count))
223 || ((unsigned)(major | minor | start | count | increment) > 255) 223 || ((unsigned)(major | minor | start | count | increment) > 255)
224 ) { 224 ) {
@@ -229,9 +229,11 @@ int makedevs_main(int argc UNUSED_PARAM, char **argv)
229 229
230 gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid(); 230 gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
231 uid = (*user) ? get_ug_id(user, xuname2uid) : getuid(); 231 uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
232 line[name_len] = '\0';
233 full_name = line;
232 /* We are already in the right root dir, 234 /* We are already in the right root dir,
233 * so make absolute paths relative */ 235 * so make absolute paths relative */
234 if ('/' == *full_name) 236 if ('/' == full_name[0])
235 full_name++; 237 full_name++;
236 238
237 if (type == 'd') { 239 if (type == 'd') {
@@ -260,9 +262,7 @@ int makedevs_main(int argc UNUSED_PARAM, char **argv)
260 if (chmod(full_name, mode) < 0) 262 if (chmod(full_name, mode) < 0)
261 goto chmod_fail; 263 goto chmod_fail;
262 } else { 264 } else {
263 dev_t rdev;
264 unsigned i; 265 unsigned i;
265 char *full_name_inc;
266 266
267 if (type == 'p') { 267 if (type == 'p') {
268 mode |= S_IFIFO; 268 mode |= S_IFIFO;
@@ -276,26 +276,29 @@ int makedevs_main(int argc UNUSED_PARAM, char **argv)
276 continue; 276 continue;
277 } 277 }
278 278
279 full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2); 279 if (count != 0)
280 if (count)
281 count--; 280 count--;
282 for (i = start; i <= start + count; i++) { 281 for (i = 0; i <= count; i++) {
283 sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i); 282 dev_t rdev;
284 rdev = makedev(major, minor + (i - start) * increment); 283 char *nameN = full_name;
285 if (mknod(full_name_inc, mode, rdev) != 0 284 if (count != 0)
285 nameN = xasprintf("%s%u", full_name, start + i);
286 rdev = makedev(major, minor + i * increment);
287 if (mknod(nameN, mode, rdev) != 0
286 && errno != EEXIST 288 && errno != EEXIST
287 ) { 289 ) {
288 bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc); 290 bb_perror_msg("line %d: can't create node %s", linenum, nameN);
289 ret = EXIT_FAILURE; 291 ret = EXIT_FAILURE;
290 } else if (chown(full_name_inc, uid, gid) < 0) { 292 } else if (chown(nameN, uid, gid) < 0) {
291 bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc); 293 bb_perror_msg("line %d: can't chown %s", linenum, nameN);
292 ret = EXIT_FAILURE; 294 ret = EXIT_FAILURE;
293 } else if (chmod(full_name_inc, mode) < 0) { 295 } else if (chmod(nameN, mode) < 0) {
294 bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc); 296 bb_perror_msg("line %d: can't chmod %s", linenum, nameN);
295 ret = EXIT_FAILURE; 297 ret = EXIT_FAILURE;
296 } 298 }
299 if (count != 0)
300 free(nameN);
297 } 301 }
298 free(full_name_inc);
299 } 302 }
300 } 303 }
301 if (ENABLE_FEATURE_CLEAN_UP) 304 if (ENABLE_FEATURE_CLEAN_UP)
diff --git a/miscutils/ttysize.c b/miscutils/ttysize.c
index 135ce8535..cba65b148 100644
--- a/miscutils/ttysize.c
+++ b/miscutils/ttysize.c
@@ -25,7 +25,7 @@
25//usage:#define ttysize_trivial_usage 25//usage:#define ttysize_trivial_usage
26//usage: "[w] [h]" 26//usage: "[w] [h]"
27//usage:#define ttysize_full_usage "\n\n" 27//usage:#define ttysize_full_usage "\n\n"
28//usage: "Print dimension(s) of stdin's terminal, on error return 80x25" 28//usage: "Print dimensions of stdin tty, or 80x24"
29 29
30#include "libbb.h" 30#include "libbb.h"
31 31
@@ -37,7 +37,10 @@ int ttysize_main(int argc UNUSED_PARAM, char **argv)
37 37
38 w = 80; 38 w = 80;
39 h = 24; 39 h = 24;
40 if (!ioctl(0, TIOCGWINSZ, &wsz)) { 40 if (ioctl(0, TIOCGWINSZ, &wsz) == 0
41 || ioctl(1, TIOCGWINSZ, &wsz) == 0
42 || ioctl(2, TIOCGWINSZ, &wsz) == 0
43 ) {
41 w = wsz.ws_col; 44 w = wsz.ws_col;
42 h = wsz.ws_row; 45 h = wsz.ws_row;
43 } 46 }
diff --git a/miscutils/watchdog.c b/miscutils/watchdog.c
index 07ae64e52..d379a97f4 100644
--- a/miscutils/watchdog.c
+++ b/miscutils/watchdog.c
@@ -35,27 +35,60 @@
35//usage: "\nUse 500ms to specify period in milliseconds" 35//usage: "\nUse 500ms to specify period in milliseconds"
36 36
37#include "libbb.h" 37#include "libbb.h"
38#include "linux/types.h" /* for __u32 */ 38#include <linux/types.h> /* for __u32 */
39#include "linux/watchdog.h" 39#include <linux/watchdog.h>
40
41#ifndef WDIOC_SETOPTIONS
42# define WDIOC_SETOPTIONS 0x5704
43#endif
44#ifndef WDIOC_SETTIMEOUT
45# define WDIOC_SETTIMEOUT 0x5706
46#endif
47#ifndef WDIOC_GETTIMEOUT
48# define WDIOC_GETTIMEOUT 0x5707
49#endif
50#ifndef WDIOS_ENABLECARD
51# define WDIOS_ENABLECARD 2
52#endif
40 53
41#define OPT_FOREGROUND (1 << 0) 54#define OPT_FOREGROUND (1 << 0)
42#define OPT_STIMER (1 << 1) 55#define OPT_STIMER (1 << 1)
43#define OPT_HTIMER (1 << 2) 56#define OPT_HTIMER (1 << 2)
44 57
45static void watchdog_shutdown(int sig UNUSED_PARAM) 58static void shutdown_watchdog(void)
46{ 59{
47 static const char V = 'V'; 60 static const char V = 'V';
61 write(3, &V, 1); /* Magic, see watchdog-api.txt in kernel */
62 close(3);
63}
48 64
65static void shutdown_on_signal(int sig UNUSED_PARAM)
66{
49 remove_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid"); 67 remove_pidfile(CONFIG_PID_FILE_PATH "/watchdog.pid");
50 write(3, &V, 1); /* Magic, see watchdog-api.txt in kernel */ 68 shutdown_watchdog();
51 if (ENABLE_FEATURE_CLEAN_UP)
52 close(3);
53 _exit(EXIT_SUCCESS); 69 _exit(EXIT_SUCCESS);
54} 70}
55 71
72static void watchdog_open(const char* device)
73{
74 /* Use known fd # - avoid needing global 'int fd' */
75 xmove_fd(xopen(device, O_WRONLY), 3);
76
77 /* If the watchdog driver can do something other than cause a reboot
78 * on a timeout, then it's possible this program may be starting from
79 * a state when the watchdog hadn't been previously stopped with
80 * the magic write followed by a close. In this case the driver may
81 * not start properly, so always do the proper stop first just in case.
82 */
83 shutdown_watchdog();
84
85 xmove_fd(xopen(device, O_WRONLY), 3);
86}
87
56int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 88int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
57int watchdog_main(int argc, char **argv) 89int watchdog_main(int argc UNUSED_PARAM, char **argv)
58{ 90{
91 static const int enable = WDIOS_ENABLECARD;
59 static const struct suffix_mult suffixes[] = { 92 static const struct suffix_mult suffixes[] = {
60 { "ms", 1 }, 93 { "ms", 1 },
61 { "", 1000 }, 94 { "", 1000 },
@@ -80,31 +113,22 @@ int watchdog_main(int argc, char **argv)
80 if (!(opts & OPT_FOREGROUND)) 113 if (!(opts & OPT_FOREGROUND))
81 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); 114 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
82 115
116 /* maybe bb_logenv_override(); here for LOGGING=syslog to work? */
117
83 if (opts & OPT_HTIMER) 118 if (opts & OPT_HTIMER)
84 htimer_duration = xatou_sfx(ht_arg, suffixes); 119 htimer_duration = xatou_sfx(ht_arg, suffixes);
85 stimer_duration = htimer_duration / 2; 120 stimer_duration = htimer_duration / 2;
86 if (opts & OPT_STIMER) 121 if (opts & OPT_STIMER)
87 stimer_duration = xatou_sfx(st_arg, suffixes); 122 stimer_duration = xatou_sfx(st_arg, suffixes);
88 123
89 bb_signals(BB_FATAL_SIGS, watchdog_shutdown); 124 bb_signals(BB_FATAL_SIGS, shutdown_on_signal);
90 125
91 /* Use known fd # - avoid needing global 'int fd' */ 126 watchdog_open(argv[optind]);
92 xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3);
93 127
94 /* WDIOC_SETTIMEOUT takes seconds, not milliseconds */ 128 /* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
95 htimer_duration = htimer_duration / 1000; 129 htimer_duration = htimer_duration / 1000;
96#ifndef WDIOC_SETTIMEOUT 130 ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
97# error WDIOC_SETTIMEOUT is not defined, cannot compile watchdog applet
98#else
99# if defined WDIOC_SETOPTIONS && defined WDIOS_ENABLECARD
100 {
101 static const int enable = WDIOS_ENABLECARD;
102 ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
103 }
104# endif
105 ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration); 131 ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
106#endif
107
108#if 0 132#if 0
109 ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration); 133 ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
110 printf("watchdog: SW timer is %dms, HW timer is %ds\n", 134 printf("watchdog: SW timer is %dms, HW timer is %ds\n",
diff --git a/modutils/modutils.c b/modutils/modutils.c
index dae623ee4..6f7cd9721 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -75,12 +75,6 @@ void FAST_FUNC replace(char *s, char what, char with)
75 } 75 }
76} 76}
77 77
78char* FAST_FUNC replace_underscores(char *s)
79{
80 replace(s, '-', '_');
81 return s;
82}
83
84int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) 78int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
85{ 79{
86 char *tok; 80 char *tok;
diff --git a/modutils/modutils.h b/modutils/modutils.h
index 76ce242ba..4a702e97c 100644
--- a/modutils/modutils.h
+++ b/modutils/modutils.h
@@ -48,7 +48,6 @@ module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC;
48void moddb_free(module_db *db) FAST_FUNC; 48void moddb_free(module_db *db) FAST_FUNC;
49 49
50void replace(char *s, char what, char with) FAST_FUNC; 50void replace(char *s, char what, char with) FAST_FUNC;
51char *replace_underscores(char *s) FAST_FUNC;
52int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; 51int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC;
53char *filename2modname(const char *filename, char *modname) FAST_FUNC; 52char *filename2modname(const char *filename, char *modname) FAST_FUNC;
54#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS 53#if ENABLE_FEATURE_CMDLINE_MODULE_OPTIONS
diff --git a/networking/inetd.c b/networking/inetd.c
index 01e659f13..3bf157b70 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -227,7 +227,8 @@
227 227
228#if ENABLE_FEATURE_INETD_RPC 228#if ENABLE_FEATURE_INETD_RPC
229# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) 229# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
230# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support" 230# warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
231 /* not #error, since user may be using e.g. libtirpc instead */
231# endif 232# endif
232# include <rpc/rpc.h> 233# include <rpc/rpc.h>
233# include <rpc/pmap_clnt.h> 234# include <rpc/pmap_clnt.h>
@@ -1513,8 +1514,11 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
1513 } /* for (;;) */ 1514 } /* for (;;) */
1514} 1515}
1515 1516
1516#if !BB_MMU 1517#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO \
1518 || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
1519# if !BB_MMU
1517static const char *const cat_args[] = { "cat", NULL }; 1520static const char *const cat_args[] = { "cat", NULL };
1521# endif
1518#endif 1522#endif
1519 1523
1520/* 1524/*
@@ -1525,14 +1529,14 @@ static const char *const cat_args[] = { "cat", NULL };
1525/* ARGSUSED */ 1529/* ARGSUSED */
1526static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM) 1530static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM)
1527{ 1531{
1528#if BB_MMU 1532# if BB_MMU
1529 while (1) { 1533 while (1) {
1530 ssize_t sz = safe_read(s, line, LINE_SIZE); 1534 ssize_t sz = safe_read(s, line, LINE_SIZE);
1531 if (sz <= 0) 1535 if (sz <= 0)
1532 break; 1536 break;
1533 xwrite(s, line, sz); 1537 xwrite(s, line, sz);
1534 } 1538 }
1535#else 1539# else
1536 /* We are after vfork here! */ 1540 /* We are after vfork here! */
1537 /* move network socket to stdin/stdout */ 1541 /* move network socket to stdin/stdout */
1538 xmove_fd(s, STDIN_FILENO); 1542 xmove_fd(s, STDIN_FILENO);
@@ -1542,7 +1546,7 @@ static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM)
1542 xopen(bb_dev_null, O_WRONLY); 1546 xopen(bb_dev_null, O_WRONLY);
1543 BB_EXECVP("cat", (char**)cat_args); 1547 BB_EXECVP("cat", (char**)cat_args);
1544 /* on failure we return to main, which does exit(EXIT_FAILURE) */ 1548 /* on failure we return to main, which does exit(EXIT_FAILURE) */
1545#endif 1549# endif
1546} 1550}
1547static void FAST_FUNC echo_dg(int s, servtab_t *sep) 1551static void FAST_FUNC echo_dg(int s, servtab_t *sep)
1548{ 1552{
@@ -1566,10 +1570,10 @@ static void FAST_FUNC echo_dg(int s, servtab_t *sep)
1566/* ARGSUSED */ 1570/* ARGSUSED */
1567static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM) 1571static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM)
1568{ 1572{
1569#if BB_MMU 1573# if BB_MMU
1570 while (safe_read(s, line, LINE_SIZE) > 0) 1574 while (safe_read(s, line, LINE_SIZE) > 0)
1571 continue; 1575 continue;
1572#else 1576# else
1573 /* We are after vfork here! */ 1577 /* We are after vfork here! */
1574 /* move network socket to stdin */ 1578 /* move network socket to stdin */
1575 xmove_fd(s, STDIN_FILENO); 1579 xmove_fd(s, STDIN_FILENO);
@@ -1580,7 +1584,7 @@ static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM)
1580 xdup2(STDOUT_FILENO, STDERR_FILENO); 1584 xdup2(STDOUT_FILENO, STDERR_FILENO);
1581 BB_EXECVP("cat", (char**)cat_args); 1585 BB_EXECVP("cat", (char**)cat_args);
1582 /* on failure we return to main, which does exit(EXIT_FAILURE) */ 1586 /* on failure we return to main, which does exit(EXIT_FAILURE) */
1583#endif 1587# endif
1584} 1588}
1585/* ARGSUSED */ 1589/* ARGSUSED */
1586static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM) 1590static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM)
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
index cc3443a92..6a41b8331 100644
--- a/networking/libiproute/iproute.c
+++ b/networking/libiproute/iproute.c
@@ -28,7 +28,7 @@ struct filter_t {
28 int flushe; 28 int flushe;
29 struct rtnl_handle *rth; 29 struct rtnl_handle *rth;
30 //int protocol, protocolmask; - write-only fields?! 30 //int protocol, protocolmask; - write-only fields?!
31 //int scope, scopemask; - unused 31 int scope, scopemask;
32 //int type; - read-only 32 //int type; - read-only
33 //int typemask; - unused 33 //int typemask; - unused
34 //int tos, tosmask; - unused 34 //int tos, tosmask; - unused
@@ -120,6 +120,8 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
120 return 0; 120 return 0;
121 } 121 }
122 } 122 }
123 if ((G_filter.scope ^ r->rtm_scope) & G_filter.scopemask)
124 return 0;
123 if (G_filter.rdst.family 125 if (G_filter.rdst.family
124 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len) 126 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
125 ) { 127 ) {
@@ -270,7 +272,11 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
270 printf("table %s ", rtnl_rttable_n2a(tid)); 272 printf("table %s ", rtnl_rttable_n2a(tid));
271#endif 273#endif
272 274
273 /* Todo: parse & show "proto kernel", "scope link" here */ 275 /* Todo: parse & show "proto kernel" here */
276 if (!(r->rtm_flags & RTM_F_CLONED)) {
277 if ((r->rtm_scope != RT_SCOPE_UNIVERSE) && G_filter.scopemask != -1)
278 printf("scope %s ", rtnl_rtscope_n2a(r->rtm_scope));
279 }
274 280
275 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) { 281 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
276 /* Do not use format_host(). It is our local addr 282 /* Do not use format_host(). It is our local addr
@@ -761,10 +767,11 @@ static int iproute_list_or_flush(char **argv, int flush)
761 char *id = NULL; 767 char *id = NULL;
762 char *od = NULL; 768 char *od = NULL;
763 static const char keywords[] ALIGN1 = 769 static const char keywords[] ALIGN1 =
770 /* If you add stuff here, update iproute_full_usage */
764 /* "ip route list/flush" parameters: */ 771 /* "ip route list/flush" parameters: */
765 "protocol\0" "dev\0" "oif\0" "iif\0" 772 "protocol\0" "dev\0" "oif\0" "iif\0"
766 "via\0" "table\0" "cache\0" 773 "via\0" "table\0" "cache\0"
767 "from\0" "to\0" 774 "from\0" "to\0" "scope\0"
768 /* and possible further keywords */ 775 /* and possible further keywords */
769 "all\0" 776 "all\0"
770 "root\0" 777 "root\0"
@@ -775,7 +782,7 @@ static int iproute_list_or_flush(char **argv, int flush)
775 enum { 782 enum {
776 KW_proto, KW_dev, KW_oif, KW_iif, 783 KW_proto, KW_dev, KW_oif, KW_iif,
777 KW_via, KW_table, KW_cache, 784 KW_via, KW_table, KW_cache,
778 KW_from, KW_to, 785 KW_from, KW_to, KW_scope,
779 /* */ 786 /* */
780 KW_all, 787 KW_all,
781 KW_root, 788 KW_root,
@@ -834,6 +841,17 @@ static int iproute_list_or_flush(char **argv, int flush)
834 /* The command 'ip route flush cache' is used by OpenSWAN. 841 /* The command 'ip route flush cache' is used by OpenSWAN.
835 * Assuming it's a synonym for 'ip route flush table cache' */ 842 * Assuming it's a synonym for 'ip route flush table cache' */
836 G_filter.tb = -1; 843 G_filter.tb = -1;
844 } else if (arg == KW_scope) {
845 uint32_t scope;
846 NEXT_ARG();
847 G_filter.scopemask = -1;
848 if (rtnl_rtscope_a2n(&scope, *argv)) {
849 if (strcmp(*argv, "all") != 0)
850 invarg_1_to_2(*argv, "scope");
851 scope = RT_SCOPE_NOWHERE;
852 G_filter.scopemask = 0;
853 }
854 G_filter.scope = scope;
837 } else if (arg == KW_from) { 855 } else if (arg == KW_from) {
838 NEXT_ARG(); 856 NEXT_ARG();
839 parm = index_in_substrings(keywords, *argv); 857 parm = index_in_substrings(keywords, *argv);
diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c
index 8f3f86286..1bb5e759e 100644
--- a/networking/libiproute/iprule.c
+++ b/networking/libiproute/iprule.c
@@ -17,25 +17,32 @@
17#include <netinet/ip.h> 17#include <netinet/ip.h>
18#include <arpa/inet.h> 18#include <arpa/inet.h>
19 19
20/* from <linux/fib_rules.h>: */
21#define FRA_SUPPRESS_IFGROUP 13
22#define FRA_SUPPRESS_PREFIXLEN 14
23
20#include "ip_common.h" /* #include "libbb.h" is inside */ 24#include "ip_common.h" /* #include "libbb.h" is inside */
21#include "rt_names.h" 25#include "rt_names.h"
22#include "utils.h" 26#include "utils.h"
23 27
24/* 28/* If you add stuff here, update iprule_full_usage */
25static void usage(void) __attribute__((noreturn)); 29static const char keywords[] ALIGN1 =
26 30 "from\0""to\0""preference\0""order\0""priority\0"
27static void usage(void) 31 "tos\0""fwmark\0""realms\0""table\0""lookup\0"
28{ 32 "suppress_prefixlength\0""suppress_ifgroup\0"
29 fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n"); 33 "dev\0""iif\0""nat\0""map-to\0""type\0""help\0"
30 fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n"); 34 ;
31 fprintf(stderr, " [ dev STRING ] [ pref NUMBER ]\n"); 35#define keyword_preference (keywords + sizeof("from") + sizeof("to"))
32 fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n"); 36#define keyword_fwmark (keyword_preference + sizeof("preference") + sizeof("order") + sizeof("priority") + sizeof("tos"))
33 fprintf(stderr, " [ prohibit | reject | unreachable ]\n"); 37#define keyword_realms (keyword_fwmark + sizeof("fwmark"))
34 fprintf(stderr, " [ realms [SRCREALM/]DSTREALM ]\n"); 38#define keyword_suppress_prefixlength (keyword_realms + sizeof("realms") + sizeof("table") + sizeof("lookup"))
35 fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n"); 39#define keyword_suppress_ifgroup (keyword_suppress_prefixlength + sizeof("suppress_prefixlength"))
36 exit(-1); 40enum {
37} 41 ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
38*/ 42 ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup,
43 ARG_suppress_prefixlength, ARG_suppress_ifgroup,
44 ARG_dev, ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help,
45};
39 46
40static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM, 47static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
41 struct nlmsghdr *n, void *arg UNUSED_PARAM) 48 struct nlmsghdr *n, void *arg UNUSED_PARAM)
@@ -119,6 +126,17 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
119 else if (r->rtm_table) 126 else if (r->rtm_table)
120 printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table)); 127 printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table));
121 128
129 if (tb[FRA_SUPPRESS_PREFIXLEN]) {
130 int pl = *(uint32_t*)RTA_DATA(tb[FRA_SUPPRESS_PREFIXLEN]);
131 if (pl != -1)
132 printf("%s %d ", keyword_suppress_prefixlength, pl);
133 }
134 if (tb[FRA_SUPPRESS_IFGROUP]) {
135 int grp = *(uint32_t*)RTA_DATA(tb[FRA_SUPPRESS_IFGROUP]);
136 if (grp != -1)
137 printf("%s %d ", keyword_suppress_ifgroup, grp);
138 }
139
122 if (tb[RTA_FLOW]) { 140 if (tb[RTA_FLOW]) {
123 uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]); 141 uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
124 uint32_t from = to>>16; 142 uint32_t from = to>>16;
@@ -174,15 +192,6 @@ static int iprule_list(char **argv)
174/* Return value becomes exitcode. It's okay to not return at all */ 192/* Return value becomes exitcode. It's okay to not return at all */
175static int iprule_modify(int cmd, char **argv) 193static int iprule_modify(int cmd, char **argv)
176{ 194{
177 static const char keywords[] ALIGN1 =
178 "from\0""to\0""preference\0""order\0""priority\0"
179 "tos\0""fwmark\0""realms\0""table\0""lookup\0""dev\0"
180 "iif\0""nat\0""map-to\0""type\0""help\0";
181 enum {
182 ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
183 ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup, ARG_dev,
184 ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help
185 };
186 bool table_ok = 0; 195 bool table_ok = 0;
187 struct rtnl_handle rth; 196 struct rtnl_handle rth;
188 struct { 197 struct {
@@ -232,7 +241,7 @@ static int iprule_modify(int cmd, char **argv)
232 ) { 241 ) {
233 uint32_t pref; 242 uint32_t pref;
234 NEXT_ARG(); 243 NEXT_ARG();
235 pref = get_u32(*argv, "preference"); 244 pref = get_u32(*argv, keyword_preference);
236 addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref); 245 addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
237 } else if (key == ARG_tos) { 246 } else if (key == ARG_tos) {
238 uint32_t tos; 247 uint32_t tos;
@@ -243,13 +252,13 @@ static int iprule_modify(int cmd, char **argv)
243 } else if (key == ARG_fwmark) { 252 } else if (key == ARG_fwmark) {
244 uint32_t fwmark; 253 uint32_t fwmark;
245 NEXT_ARG(); 254 NEXT_ARG();
246 fwmark = get_u32(*argv, "fwmark"); 255 fwmark = get_u32(*argv, keyword_fwmark);
247 addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark); 256 addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
248 } else if (key == ARG_realms) { 257 } else if (key == ARG_realms) {
249 uint32_t realm; 258 uint32_t realm;
250 NEXT_ARG(); 259 NEXT_ARG();
251 if (get_rt_realms(&realm, *argv)) 260 if (get_rt_realms(&realm, *argv))
252 invarg_1_to_2(*argv, "realms"); 261 invarg_1_to_2(*argv, keyword_realms);
253 addattr32(&req.n, sizeof(req), RTA_FLOW, realm); 262 addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
254 } else if (key == ARG_table || 263 } else if (key == ARG_table ||
255 key == ARG_lookup 264 key == ARG_lookup
@@ -265,6 +274,16 @@ static int iprule_modify(int cmd, char **argv)
265 addattr32(&req.n, sizeof(req), RTA_TABLE, tid); 274 addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
266 } 275 }
267 table_ok = 1; 276 table_ok = 1;
277 } else if (key == ARG_suppress_prefixlength) {
278 int prefix_length;
279 NEXT_ARG();
280 prefix_length = get_u32(*argv, keyword_suppress_prefixlength);
281 addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, prefix_length);
282 } else if (key == ARG_suppress_ifgroup) {
283 int grp;
284 NEXT_ARG();
285 grp = get_u32(*argv, keyword_suppress_ifgroup);
286 addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, grp);
268 } else if (key == ARG_dev || 287 } else if (key == ARG_dev ||
269 key == ARG_iif 288 key == ARG_iif
270 ) { 289 ) {
diff --git a/networking/tls.c b/networking/tls.c
index db518bf90..fd3cb0dba 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -1199,11 +1199,11 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni)
1199 }; 1199 };
1200 struct client_hello *record; 1200 struct client_hello *record;
1201 int len; 1201 int len;
1202 int sni_len = sni ? strnlen(sni, 127) : 0; 1202 int sni_len = sni ? strnlen(sni, 127 - 9) : 0;
1203 1203
1204 len = sizeof(*record); 1204 len = sizeof(*record);
1205 if (sni_len) 1205 if (sni_len)
1206 len += 11 + strlen(sni); 1206 len += 11 + sni_len;
1207 record = tls_get_outbuf(tls, len); 1207 record = tls_get_outbuf(tls, len);
1208 memset(record, 0, len); 1208 memset(record, 0, len);
1209 1209
diff --git a/networking/tls_aes.c b/networking/tls_aes.c
index 6992f1c90..c137442e9 100644
--- a/networking/tls_aes.c
+++ b/networking/tls_aes.c
@@ -40,10 +40,6 @@
40 */ 40 */
41#include "tls.h" 41#include "tls.h"
42 42
43/* TODO: grep for this and move to libbb */
44#define get_unaligned_be32(buf) ({ uint32_t v; move_from_unaligned32(v, buf); SWAP_BE32(v); })
45
46
47// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM 43// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
48// The numbers below can be computed dynamically trading ROM for RAM - 44// The numbers below can be computed dynamically trading ROM for RAM -
49// This can be useful in (embedded) bootloader applications, where ROM is often limited. 45// This can be useful in (embedded) bootloader applications, where ROM is often limited.
diff --git a/networking/tls_pstm.c b/networking/tls_pstm.c
index bd5bae0b7..e12e6c9d4 100644
--- a/networking/tls_pstm.c
+++ b/networking/tls_pstm.c
@@ -2127,7 +2127,7 @@ int32 pstm_invmod(psPool_t *pool, pstm_int *a, pstm_int *b, pstm_int *c)
2127{ 2127{
2128 pstm_int x, y, u, v, B, D; 2128 pstm_int x, y, u, v, B, D;
2129 int32 res; 2129 int32 res;
2130 uint16 neg, sanity; 2130 int neg, sanity; //bbox: was uint16
2131 2131
2132 /* 2. [modified] b must be odd */ 2132 /* 2. [modified] b must be odd */
2133 if (pstm_iseven (b) == 1) { 2133 if (pstm_iseven (b) == 1) {
diff --git a/networking/tls_pstm_montgomery_reduce.c b/networking/tls_pstm_montgomery_reduce.c
index dc2fe0a48..3391755e1 100644
--- a/networking/tls_pstm_montgomery_reduce.c
+++ b/networking/tls_pstm_montgomery_reduce.c
@@ -345,7 +345,7 @@ int32 pstm_montgomery_reduce(psPool_t *pool, pstm_int *a, pstm_int *m,
345{ 345{
346 pstm_digit *c, *_c, *tmpm, mu; 346 pstm_digit *c, *_c, *tmpm, mu;
347 int32 oldused, x, y; 347 int32 oldused, x, y;
348 int16 pa; 348 int pa; //bbox: was int16
349 349
350 pa = m->used; 350 pa = m->used;
351 if (pa > a->alloc) { 351 if (pa > a->alloc) {
diff --git a/networking/tls_pstm_mul_comba.c b/networking/tls_pstm_mul_comba.c
index 47cbb9618..6ba152bc1 100644
--- a/networking/tls_pstm_mul_comba.c
+++ b/networking/tls_pstm_mul_comba.c
@@ -85,7 +85,8 @@ asm( \
85 "addl %%eax,%0 \n\t" \ 85 "addl %%eax,%0 \n\t" \
86 "adcl %%edx,%1 \n\t" \ 86 "adcl %%edx,%1 \n\t" \
87 "adcl $0,%2 \n\t" \ 87 "adcl $0,%2 \n\t" \
88 :"=r"(c0), "=r"(c1), "=r"(c2): "0"(c0), "1"(c1), "2"(c2), "m"(i), "m"(j) :"%eax","%edx","%cc"); 88 :"=rm"(c0), "=rm"(c1), "=rm"(c2): "0"(c0), "1"(c1), "2"(c2), "m"(i), "m"(j) :"%eax","%edx","%cc");
89 //bbox: ^^^ replaced "=r" with "=rm": %ebx is not available on shared build
89 90
90/******************************************************************************/ 91/******************************************************************************/
91#elif defined(PSTM_X86_64) 92#elif defined(PSTM_X86_64)
@@ -228,7 +229,7 @@ asm( \
228static int32 pstm_mul_comba_gen(psPool_t *pool, pstm_int *A, pstm_int *B, 229static int32 pstm_mul_comba_gen(psPool_t *pool, pstm_int *A, pstm_int *B,
229 pstm_int *C, pstm_digit *paD, uint32 paDlen) 230 pstm_int *C, pstm_digit *paD, uint32 paDlen)
230{ 231{
231 int16 paDfail, pa; 232 int paDfail, pa; //bbox: was int16
232 int32 ix, iy, iz, tx, ty; 233 int32 ix, iy, iz, tx, ty;
233 pstm_digit c0, c1, c2, *tmpx, *tmpy, *dst; 234 pstm_digit c0, c1, c2, *tmpx, *tmpy, *dst;
234 235
diff --git a/networking/tls_pstm_sqr_comba.c b/networking/tls_pstm_sqr_comba.c
index 36cb9ea97..d5c74d2f0 100644
--- a/networking/tls_pstm_sqr_comba.c
+++ b/networking/tls_pstm_sqr_comba.c
@@ -78,7 +78,8 @@ asm( \
78 "addl %%eax,%0 \n\t" \ 78 "addl %%eax,%0 \n\t" \
79 "adcl %%edx,%1 \n\t" \ 79 "adcl %%edx,%1 \n\t" \
80 "adcl $0,%2 \n\t" \ 80 "adcl $0,%2 \n\t" \
81 :"=r"(c0), "=r"(c1), "=r"(c2): "0"(c0), "1"(c1), "2"(c2), "m"(i) :"%eax","%edx","%cc"); 81 :"=rm"(c0), "=rm"(c1), "=rm"(c2): "0"(c0), "1"(c1), "2"(c2), "m"(i) :"%eax","%edx","%cc");
82 //bbox: ^^^ replaced "=r" with "=rm": %ebx is not available on shared build
82 83
83#define SQRADD2(i, j) \ 84#define SQRADD2(i, j) \
84asm( \ 85asm( \
@@ -90,7 +91,8 @@ asm( \
90 "addl %%eax,%0 \n\t" \ 91 "addl %%eax,%0 \n\t" \
91 "adcl %%edx,%1 \n\t" \ 92 "adcl %%edx,%1 \n\t" \
92 "adcl $0,%2 \n\t" \ 93 "adcl $0,%2 \n\t" \
93 :"=r"(c0), "=r"(c1), "=r"(c2): "0"(c0), "1"(c1), "2"(c2), "m"(i), "m"(j) :"%eax","%edx","%cc"); 94 :"=rm"(c0), "=rm"(c1), "=rm"(c2): "0"(c0), "1"(c1), "2"(c2), "m"(i), "m"(j) :"%eax","%edx","%cc");
95 //bbox: ^^^ replaced "=r" with "=rm": %ebx is not available on shared build
94 96
95#define SQRADDSC(i, j) \ 97#define SQRADDSC(i, j) \
96asm( \ 98asm( \
@@ -445,7 +447,7 @@ asm( \
445static int32 pstm_sqr_comba_gen(psPool_t *pool, pstm_int *A, pstm_int *B, 447static int32 pstm_sqr_comba_gen(psPool_t *pool, pstm_int *A, pstm_int *B,
446 pstm_digit *paD, uint32 paDlen) 448 pstm_digit *paD, uint32 paDlen)
447{ 449{
448 int16 paDfail, pa; 450 int paDfail, pa; //bbox: was int16
449 int32 ix, iz; 451 int32 ix, iz;
450 pstm_digit c0, c1, c2, *dst; 452 pstm_digit c0, c1, c2, *dst;
451#ifdef PSTM_ISO 453#ifdef PSTM_ISO
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 420695a20..d3eea5def 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -14,6 +14,7 @@ const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = {
14 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 14 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
15}; 15};
16 16
17#if ENABLE_UDHCPC || ENABLE_UDHCPD
17/* Supported options are easily added here. 18/* Supported options are easily added here.
18 * See RFC2132 for more options. 19 * See RFC2132 for more options.
19 * OPTION_REQ: these options are requested by udhcpc (unless -o). 20 * OPTION_REQ: these options are requested by udhcpc (unless -o).
@@ -136,6 +137,7 @@ const char dhcp_option_strings[] ALIGN1 =
136 "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */ 137 "msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
137 "wpad" "\0" /* DHCP_WPAD */ 138 "wpad" "\0" /* DHCP_WPAD */
138 ; 139 ;
140#endif
139 141
140/* Lengths of the option types in binary form. 142/* Lengths of the option types in binary form.
141 * Used by: 143 * Used by:
@@ -190,21 +192,26 @@ static void log_option(const char *pfx, const uint8_t *opt)
190# define log_option(pfx, opt) ((void)0) 192# define log_option(pfx, opt) ((void)0)
191#endif 193#endif
192 194
193unsigned FAST_FUNC udhcp_option_idx(const char *name) 195unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings)
194{ 196{
195 int n = index_in_strings(dhcp_option_strings, name); 197 int n = index_in_strings(option_strings, name);
196 if (n >= 0) 198 if (n >= 0)
197 return n; 199 return n;
198 200
199 { 201 {
200 char buf[sizeof(dhcp_option_strings)]; 202 char *buf, *d;
201 char *d = buf; 203 const char *s;
202 const char *s = dhcp_option_strings; 204
203 while (s < dhcp_option_strings + sizeof(dhcp_option_strings) - 2) { 205 s = option_strings;
206 while (*s)
207 s += strlen(s) + 1;
208
209 d = buf = xzalloc(s - option_strings);
210 s = option_strings;
211 while (!(*s == '\0' && s[1] == '\0')) {
204 *d++ = (*s == '\0' ? ' ' : *s); 212 *d++ = (*s == '\0' ? ' ' : *s);
205 s++; 213 s++;
206 } 214 }
207 *d = '\0';
208 bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf); 215 bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf);
209 } 216 }
210} 217}
@@ -315,6 +322,7 @@ void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addo
315 optionptr[end + len] = DHCP_END; 322 optionptr[end + len] = DHCP_END;
316} 323}
317 324
325#if ENABLE_UDHCPC || ENABLE_UDHCPD
318/* Add an one to four byte option to a packet */ 326/* Add an one to four byte option to a packet */
319void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) 327void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data)
320{ 328{
@@ -338,6 +346,7 @@ void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code,
338 346
339 bb_error_msg("can't add option 0x%02x", code); 347 bb_error_msg("can't add option 0x%02x", code);
340} 348}
349#endif
341 350
342/* Find option 'code' in opt_list */ 351/* Find option 'code' in opt_list */
343struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code) 352struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code)
@@ -451,7 +460,7 @@ static NOINLINE void attach_option(
451 free(allocated); 460 free(allocated);
452} 461}
453 462
454int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg) 463int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg, const struct dhcp_optflag *optflags, const char *option_strings)
455{ 464{
456 struct option_set **opt_list = arg; 465 struct option_set **opt_list = arg;
457 char *opt, *val; 466 char *opt, *val;
@@ -478,7 +487,7 @@ int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
478 bin_optflag.code = optcode; 487 bin_optflag.code = optcode;
479 optflag = &bin_optflag; 488 optflag = &bin_optflag;
480 } else { 489 } else {
481 optflag = &dhcp_optflags[udhcp_option_idx(opt)]; 490 optflag = &optflags[udhcp_option_idx(opt, option_strings)];
482 } 491 }
483 492
484 retval = 0; 493 retval = 0;
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index ee12cf91b..a9c23a186 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -93,8 +93,10 @@ enum {
93 OPTION_BIN, 93 OPTION_BIN,
94 OPTION_STATIC_ROUTES, 94 OPTION_STATIC_ROUTES,
95 OPTION_6RD, 95 OPTION_6RD,
96#if ENABLE_FEATURE_UDHCP_RFC3397 96#if ENABLE_FEATURE_UDHCP_RFC3397 || ENABLE_FEATURE_UDHCPC6_RFC3646 || ENABLE_FEATURE_UDHCPC6_RFC4704
97 OPTION_DNS_STRING, /* RFC1035 compressed domain name list */ 97 OPTION_DNS_STRING, /* RFC1035 compressed domain name list */
98#endif
99#if ENABLE_FEATURE_UDHCP_RFC3397
98 OPTION_SIP_SERVERS, 100 OPTION_SIP_SERVERS,
99#endif 101#endif
100 102
@@ -189,17 +191,21 @@ struct option_set {
189 struct option_set *next; 191 struct option_set *next;
190}; 192};
191 193
194#if ENABLE_UDHCPC || ENABLE_UDHCPD
192extern const struct dhcp_optflag dhcp_optflags[]; 195extern const struct dhcp_optflag dhcp_optflags[];
193extern const char dhcp_option_strings[] ALIGN1; 196extern const char dhcp_option_strings[] ALIGN1;
197#endif
194extern const uint8_t dhcp_option_lengths[] ALIGN1; 198extern const uint8_t dhcp_option_lengths[] ALIGN1;
195 199
196unsigned FAST_FUNC udhcp_option_idx(const char *name); 200unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings);
197 201
198uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC; 202uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC;
199int udhcp_end_option(uint8_t *optionptr) FAST_FUNC; 203int udhcp_end_option(uint8_t *optionptr) FAST_FUNC;
200void udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt) FAST_FUNC; 204void udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt) FAST_FUNC;
205#if ENABLE_UDHCPC || ENABLE_UDHCPD
201void udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) FAST_FUNC; 206void udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) FAST_FUNC;
202#if ENABLE_FEATURE_UDHCP_RFC3397 207#endif
208#if ENABLE_FEATURE_UDHCP_RFC3397 || ENABLE_FEATURE_UDHCPC6_RFC3646 || ENABLE_FEATURE_UDHCPC6_RFC4704
203char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC; 209char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC;
204uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) FAST_FUNC; 210uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) FAST_FUNC;
205#endif 211#endif
@@ -284,9 +290,14 @@ void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC;
284/* 2nd param is "uint32_t*" */ 290/* 2nd param is "uint32_t*" */
285int FAST_FUNC udhcp_str2nip(const char *str, void *arg); 291int FAST_FUNC udhcp_str2nip(const char *str, void *arg);
286/* 2nd param is "struct option_set**" */ 292/* 2nd param is "struct option_set**" */
287int FAST_FUNC udhcp_str2optset(const char *str, void *arg); 293int FAST_FUNC udhcp_str2optset(const char *str,
294 void *arg,
295 const struct dhcp_optflag *optflags,
296 const char *option_strings);
288 297
298#if ENABLE_UDHCPC || ENABLE_UDHCPD
289void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC; 299void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC;
300#endif
290 301
291int udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) FAST_FUNC; 302int udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) FAST_FUNC;
292 303
diff --git a/networking/udhcp/d6_common.h b/networking/udhcp/d6_common.h
index fcec8c15a..310550371 100644
--- a/networking/udhcp/d6_common.h
+++ b/networking/udhcp/d6_common.h
@@ -87,8 +87,47 @@ struct d6_option {
87#define D6_OPT_IA_PD 25 87#define D6_OPT_IA_PD 25
88#define D6_OPT_IAPREFIX 26 88#define D6_OPT_IAPREFIX 26
89 89
90/* RFC 4704 "The DHCPv6 Client FQDN Option"
91 * uint16 option-code OPTION_CLIENT_FQDN (39)
92 * uint16 option-len 1 + length of domain name
93 * uint8 flags
94 * char[] domain-name partial or fully qualified domain name
95 *
96 * Flags format is |MBZ|N|O|S|
97 * The "S" bit indicates whether the server SHOULD or SHOULD NOT perform
98 * the AAAA RR (FQDN-to-address) DNS updates. A client sets the bit to
99 * 0 to indicate that the server SHOULD NOT perform the updates and 1 to
100 * indicate that the server SHOULD perform the updates. The state of
101 * the bit in the reply from the server indicates the action to be taken
102 * by the server; if it is 1, the server has taken responsibility for
103 * AAAA RR updates for the FQDN.
104 * The "O" bit indicates whether the server has overridden the client's
105 * preference for the "S" bit. A client MUST set this bit to 0. A
106 * server MUST set this bit to 1 if the "S" bit in its reply to the
107 * client does not match the "S" bit received from the client.
108 * The "N" bit indicates whether the server SHOULD NOT perform any DNS
109 * updates. A client sets this bit to 0 to request that the server
110 * SHOULD perform updates (the PTR RR and possibly the AAAA RR based on
111 * the "S" bit) or to 1 to request that the server SHOULD NOT perform
112 * any DNS updates. A server sets the "N" bit to indicate whether the
113 * server SHALL (0) or SHALL NOT (1) perform DNS updates. If the "N"
114 * bit is 1, the "S" bit MUST be 0.
115 *
116 * If a client knows only part of its name, it MAY send a name that is not
117 * fully qualified, indicating that it knows part of the name but does not
118 * necessarily know the zone in which the name is to be embedded.
119 * To send a fully qualified domain name, the Domain Name field is set
120 * to the DNS-encoded domain name including the terminating zero-length
121 * label. To send a partial name, the Domain Name field is set to the
122 * DNS-encoded domain name without the terminating zero-length label.
123 * A client MAY also leave the Domain Name field empty if it desires the
124 * server to provide a name.
125 */
90#define D6_OPT_CLIENT_FQDN 39 126#define D6_OPT_CLIENT_FQDN 39
91 127
128#define D6_OPT_TZ_POSIX 41
129#define D6_OPT_TZ_NAME 42
130
92/*** Other shared functions ***/ 131/*** Other shared functions ***/
93 132
94struct client6_data_t { 133struct client6_data_t {
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 18a104c61..f6d3fb98b 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -2,26 +2,49 @@
2/* 2/*
3 * DHCPv6 client. 3 * DHCPv6 client.
4 * 4 *
5 * 2011-11. 5 * WARNING: THIS CODE IS INCOMPLETE.
6 * WARNING: THIS CODE IS INCOMPLETE. IT IS NOWHERE NEAR
7 * TO BE READY FOR PRODUCTION USE.
8 * 6 *
9 * Copyright (C) 2011 Denys Vlasenko. 7 * Copyright (C) 2011-2017 Denys Vlasenko.
10 * 8 *
11 * Licensed under GPLv2, see file LICENSE in this source tree. 9 * Licensed under GPLv2, see file LICENSE in this source tree.
12 */ 10 */
13 11
14//config:config UDHCPC6 12//config:config UDHCPC6
15//config: bool "udhcpc6 (DHCPv6 client, NOT READY)" 13//config: bool "udhcpc6 (DHCPv6 client, EXPERIMENTAL)"
16//config: default n # not yet ready 14//config: default n # not yet ready
17//config: depends on FEATURE_IPV6 15//config: depends on FEATURE_IPV6
18//config: help 16//config: help
19//config: udhcpc6 is a DHCPv6 client 17//config: udhcpc6 is a DHCPv6 client
18//config:
19//config:config FEATURE_UDHCPC6_RFC3646
20//config: bool "Support RFC 3646 (DNS server and search list)"
21//config: default y
22//config: depends on UDHCPC6
23//config: help
24//config: List of DNS servers and domain search list can be requested with
25//config: "-O dns" and "-O search". If server gives these values,
26//config: they will be set in environment variables "dns" and "search".
27//config:
28//config:config FEATURE_UDHCPC6_RFC4704
29//config: bool "Support RFC 4704 (Client FQDN)"
30//config: default y
31//config: depends on UDHCPC6
32//config: help
33//config: You can request FQDN to be given by server using "-O fqdn".
34//config:
35//config:config FEATURE_UDHCPC6_RFC4833
36//config: bool "Support RFC 4833 (Timezones)"
37//config: default y
38//config: depends on UDHCPC6
39//config: help
40//config: You can request POSIX timezone with "-O tz" and timezone name
41//config: with "-O timezone".
20 42
21//applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP)) 43//applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP))
22 44
23//kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o socket.o signalpipe.o 45//kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o socket.o signalpipe.o
24 46//kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC3646) += domain_codec.o
47//kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC4704) += domain_codec.o
25 48
26#include <syslog.h> 49#include <syslog.h>
27/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */ 50/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
@@ -37,6 +60,34 @@
37 60
38/* "struct client_config_t client_config" is in bb_common_bufsiz1 */ 61/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
39 62
63static const struct dhcp_optflag d6_optflags[] = {
64#if ENABLE_FEATURE_UDHCPC6_RFC3646
65 { OPTION_6RD | OPTION_LIST | OPTION_REQ, D6_OPT_DNS_SERVERS },
66 { OPTION_DNS_STRING | OPTION_LIST | OPTION_REQ, D6_OPT_DOMAIN_LIST },
67#endif
68#if ENABLE_FEATURE_UDHCPC6_RFC4704
69 { OPTION_DNS_STRING, D6_OPT_CLIENT_FQDN },
70#endif
71#if ENABLE_FEATURE_UDHCPC6_RFC4833
72 { OPTION_STRING, D6_OPT_TZ_POSIX },
73 { OPTION_STRING, D6_OPT_TZ_NAME },
74#endif
75 { 0, 0 }
76};
77/* Must match d6_optflags[] order */
78static const char d6_option_strings[] ALIGN1 =
79#if ENABLE_FEATURE_UDHCPC6_RFC3646
80 "dns" "\0" /* D6_OPT_DNS_SERVERS */
81 "search" "\0" /* D6_OPT_DOMAIN_LIST */
82#endif
83#if ENABLE_FEATURE_UDHCPC6_RFC4704
84 "fqdn" "\0" /* D6_OPT_CLIENT_FQDN */
85#endif
86#if ENABLE_FEATURE_UDHCPC6_RFC4833
87 "tz" "\0" /* D6_OPT_TZ_POSIX */
88 "timezone" "\0" /* D6_OPT_TZ_NAME */
89#endif
90 "\0";
40 91
41#if ENABLE_LONG_OPTS 92#if ENABLE_LONG_OPTS
42static const char udhcpc6_longopts[] ALIGN1 = 93static const char udhcpc6_longopts[] ALIGN1 =
@@ -88,19 +139,17 @@ enum {
88 IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) 139 IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,)
89}; 140};
90 141
91static const char opt_req[] = { 142#if ENABLE_FEATURE_UDHCPC6_RFC4704
92 (D6_OPT_ORO >> 8), (D6_OPT_ORO & 0xff),
93 0, 6,
94 (D6_OPT_DNS_SERVERS >> 8), (D6_OPT_DNS_SERVERS & 0xff),
95 (D6_OPT_DOMAIN_LIST >> 8), (D6_OPT_DOMAIN_LIST & 0xff),
96 (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff)
97};
98
99static const char opt_fqdn_req[] = { 143static const char opt_fqdn_req[] = {
100 (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff), 144 (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff),
101 0, 2, 145 0, 2, /* optlen */
102 0, 0 146 0, /* flags: */
147 /* S=0: server SHOULD NOT perform AAAA RR updates */
148 /* O=0: client MUST set this bit to 0 */
149 /* N=0: server SHOULD perform updates (PTR RR only in our case, since S=0) */
150 0 /* empty DNS-encoded name */
103}; 151};
152#endif
104 153
105/*** Utility functions ***/ 154/*** Utility functions ***/
106 155
@@ -136,12 +185,6 @@ static void *d6_copy_option(uint8_t *option, uint8_t *option_end, unsigned code)
136 return xmemdup(opt, opt[3] + 4); 185 return xmemdup(opt, opt[3] + 4);
137} 186}
138 187
139static void *d6_store_blob(void *dst, const void *src, unsigned len)
140{
141 memcpy(dst, src, len);
142 return dst + len;
143}
144
145 188
146/*** Script execution code ***/ 189/*** Script execution code ***/
147 190
@@ -154,10 +197,12 @@ static char** new_env(void)
154/* put all the parameters into the environment */ 197/* put all the parameters into the environment */
155static void option_to_env(uint8_t *option, uint8_t *option_end) 198static void option_to_env(uint8_t *option, uint8_t *option_end)
156{ 199{
157 char *dlist, *ptr; 200#if ENABLE_FEATURE_UDHCPC6_RFC3646
201 int addrs, option_offset;
202#endif
158 /* "length minus 4" */ 203 /* "length minus 4" */
159 int len_m4 = option_end - option - 4; 204 int len_m4 = option_end - option - 4;
160 int olen, ooff; 205
161 while (len_m4 >= 0) { 206 while (len_m4 >= 0) {
162 uint32_t v32; 207 uint32_t v32;
163 char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; 208 char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
@@ -165,6 +210,10 @@ static void option_to_env(uint8_t *option, uint8_t *option_end)
165 if (option[0] != 0 || option[2] != 0) 210 if (option[0] != 0 || option[2] != 0)
166 break; 211 break;
167 212
213 /* Check if option-length exceeds size of option */
214 if (option[3] > len_m4)
215 break;
216
168 switch (option[1]) { 217 switch (option[1]) {
169 //case D6_OPT_CLIENTID: 218 //case D6_OPT_CLIENTID:
170 //case D6_OPT_SERVERID: 219 //case D6_OPT_SERVERID:
@@ -174,7 +223,7 @@ static void option_to_env(uint8_t *option, uint8_t *option_end)
174 break; 223 break;
175 //case D6_OPT_IA_TA: 224 //case D6_OPT_IA_TA:
176 case D6_OPT_IAADDR: 225 case D6_OPT_IAADDR:
177/* 0 1 2 3 226/* 0 1 2 3
178 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 227 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
179 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 228 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
180 * | OPTION_IAADDR | option-len | 229 * | OPTION_IAADDR | option-len |
@@ -235,51 +284,76 @@ static void option_to_env(uint8_t *option, uint8_t *option_end)
235 sprint_nip6(ipv6str, option + 4 + 4 + 1); 284 sprint_nip6(ipv6str, option + 4 + 4 + 1);
236 *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4])); 285 *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4]));
237 break; 286 break;
238 case D6_OPT_DNS_SERVERS: 287#if ENABLE_FEATURE_UDHCPC6_RFC3646
239 olen = ((option[2] << 8) | option[3]) / 16; 288 case D6_OPT_DNS_SERVERS: {
240 dlist = ptr = malloc (4 + olen * 40 - 1); 289 char *dlist;
241
242 memcpy (ptr, "dns=", 4);
243 ptr += 4;
244 ooff = 0;
245
246 while (olen--) {
247 sprint_nip6(ptr, option + 4 + ooff);
248 ptr += 39;
249 ooff += 16;
250 if (olen)
251 *ptr++ = ' ';
252 }
253 290
254 *new_env() = dlist; 291 /* Make sure payload-size is a multiple of 16 */
292 if ((option[3] & 0x0f) != 0)
293 break;
294
295 /* Get the number of addresses on the option */
296 addrs = option[3] >> 4;
297
298 /* Setup environment variable */
299 *new_env() = dlist = xmalloc(4 + addrs * 40 - 1);
300 dlist = stpcpy(dlist, "dns=");
301 option_offset = 0;
302
303 while (addrs--) {
304 sprint_nip6(dlist, option + 4 + option_offset);
305 dlist += 39;
306 option_offset += 16;
307 if (addrs)
308 *dlist++ = ' ';
309 }
255 310
256 break; 311 break;
257 case D6_OPT_DOMAIN_LIST: 312 }
313 case D6_OPT_DOMAIN_LIST: {
314 char *dlist;
315
258 dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search="); 316 dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search=");
259 if (!dlist) 317 if (!dlist)
260 break; 318 break;
261 *new_env() = dlist; 319 *new_env() = dlist;
262 break; 320 break;
263 case D6_OPT_CLIENT_FQDN: 321 }
264 // Work around broken ISC DHCPD6 322#endif
323#if ENABLE_FEATURE_UDHCPC6_RFC4704
324 case D6_OPT_CLIENT_FQDN: {
325 char *dlist;
326
327 if (option[3] == 0)
328 break;
329 /* Work around broken ISC DHCPD6.
330 * ISC DHCPD6 does not implement RFC 4704 correctly: It says the first
331 * byte of option-payload should contain flags where the bits 7-3 are
332 * reserved for future use and MUST be zero. Instead ISC DHCPD6 just
333 * writes the entire FQDN as string to option-payload. We assume a
334 * broken server here if any of the reserved bits are set.
335 */
265 if (option[4] & 0xf8) { 336 if (option[4] & 0xf8) {
266 olen = ((option[2] << 8) | option[3]); 337 *new_env() = xasprintf("fqdn=%.*s", (int)option[3], (char*)option + 4);
267 dlist = xmalloc(olen);
268//fixme:
269//- explain
270//- add len error check
271//- merge two allocs into one
272 memcpy(dlist, option + 4, olen);
273 *new_env() = xasprintf("fqdn=%s", dlist);
274 free(dlist);
275 break; 338 break;
276 } 339 }
277 dlist = dname_dec(option + 5, ((option[2] << 8) | option[3]) - 1, "fqdn="); 340 dlist = dname_dec(option + 5, (/*(option[2] << 8) |*/ option[3]) - 1, "fqdn=");
278 if (!dlist) 341 if (!dlist)
279 break; 342 break;
280 *new_env() = dlist; 343 *new_env() = dlist;
281 break; 344 break;
282 } 345 }
346#endif
347#if ENABLE_FEATURE_UDHCPC6_RFC4833
348 /* RFC 4833 Timezones */
349 case D6_OPT_TZ_POSIX:
350 *new_env() = xasprintf("tz=%.*s", (int)option[3], (char*)option + 4);
351 break;
352 case D6_OPT_TZ_NAME:
353 *new_env() = xasprintf("tz_name=%.*s", (int)option[3], (char*)option + 4);
354 break;
355#endif
356 }
283 len_m4 -= 4 + option[3]; 357 len_m4 -= 4 + option[3];
284 option += 4 + option[3]; 358 option += 4 + option[3];
285 } 359 }
@@ -346,22 +420,38 @@ static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid
346 packet->d6_msg_type = type; 420 packet->d6_msg_type = type;
347 421
348 clientid = (void*)client_config.clientid; 422 clientid = (void*)client_config.clientid;
349 return d6_store_blob(packet->d6_options, clientid, clientid->len + 2+2); 423 return mempcpy(packet->d6_options, clientid, clientid->len + 2+2);
350} 424}
351 425
352static uint8_t *add_d6_client_options(uint8_t *ptr) 426static uint8_t *add_d6_client_options(uint8_t *ptr)
353{ 427{
354 return ptr; 428 uint8_t *start = ptr;
355 //uint8_t c; 429 unsigned option;
356 //int i, end, len; 430
431 ptr += 4;
432 for (option = 1; option < 256; option++) {
433 if (client_config.opt_mask[option >> 3] & (1 << (option & 7))) {
434 ptr[0] = (option >> 8);
435 ptr[1] = option;
436 ptr += 2;
437 }
438 }
357 439
358 /* Add a "param req" option with the list of options we'd like to have 440 if ((ptr - start - 4) != 0) {
359 * from stubborn DHCP servers. Pull the data from the struct in common.c. 441 start[0] = (D6_OPT_ORO >> 8);
360 * No bounds checking because it goes towards the head of the packet. */ 442 start[1] = D6_OPT_ORO;
361 //... 443 start[2] = ((ptr - start - 4) >> 8);
444 start[3] = (ptr - start - 4);
445 } else
446 ptr = start;
362 447
448#if ENABLE_FEATURE_UDHCPC6_RFC4704
449 ptr = mempcpy(ptr, &opt_fqdn_req, sizeof(opt_fqdn_req));
450#endif
363 /* Add -x options if any */ 451 /* Add -x options if any */
364 //... 452 //...
453
454 return ptr;
365} 455}
366 456
367static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end) 457static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end)
@@ -483,11 +573,7 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip
483 iaaddr->len = 16+4+4; 573 iaaddr->len = 16+4+4;
484 memcpy(iaaddr->data, requested_ipv6, 16); 574 memcpy(iaaddr->data, requested_ipv6, 16);
485 } 575 }
486 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len); 576 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len);
487
488 /* Request additional options */
489 opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req));
490 opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req));
491 577
492 /* Add options: 578 /* Add options:
493 * "param req" option according to -O, options specified with -x 579 * "param req" option according to -O, options specified with -x
@@ -538,13 +624,9 @@ static NOINLINE int send_d6_select(uint32_t xid)
538 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid); 624 opt_ptr = init_d6_packet(&packet, D6_MSG_REQUEST, xid);
539 625
540 /* server id */ 626 /* server id */
541 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2); 627 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
542 /* IA NA (contains requested IP) */ 628 /* IA NA (contains requested IP) */
543 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); 629 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
544
545 /* Request additional options */
546 opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req));
547 opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req));
548 630
549 /* Add options: 631 /* Add options:
550 * "param req" option according to -O, options specified with -x 632 * "param req" option according to -O, options specified with -x
@@ -611,9 +693,9 @@ static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, st
611 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid); 693 opt_ptr = init_d6_packet(&packet, DHCPREQUEST, xid);
612 694
613 /* server id */ 695 /* server id */
614 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2); 696 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
615 /* IA NA (contains requested IP) */ 697 /* IA NA (contains requested IP) */
616 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); 698 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
617 699
618 /* Add options: 700 /* Add options:
619 * "param req" option according to -O, options specified with -x 701 * "param req" option according to -O, options specified with -x
@@ -640,9 +722,9 @@ static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cu
640 /* Fill in: msg type, client id */ 722 /* Fill in: msg type, client id */
641 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid()); 723 opt_ptr = init_d6_packet(&packet, D6_MSG_RELEASE, random_xid());
642 /* server id */ 724 /* server id */
643 opt_ptr = d6_store_blob(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2); 725 opt_ptr = mempcpy(opt_ptr, client6_data.server_id, client6_data.server_id->len + 2+2);
644 /* IA NA (contains our current IP) */ 726 /* IA NA (contains our current IP) */
645 opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); 727 opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
646 728
647 bb_error_msg("sending %s", "release"); 729 bb_error_msg("sending %s", "release");
648 return d6_send_kernel_packet( 730 return d6_send_kernel_packet(
@@ -1051,20 +1133,18 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1051 char *optstr = llist_pop(&list_O); 1133 char *optstr = llist_pop(&list_O);
1052 unsigned n = bb_strtou(optstr, NULL, 0); 1134 unsigned n = bb_strtou(optstr, NULL, 0);
1053 if (errno || n > 254) { 1135 if (errno || n > 254) {
1054 n = udhcp_option_idx(optstr); 1136 n = udhcp_option_idx(optstr, d6_option_strings);
1055 n = dhcp_optflags[n].code; 1137 n = d6_optflags[n].code;
1056 } 1138 }
1057 client_config.opt_mask[n >> 3] |= 1 << (n & 7); 1139 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
1058 } 1140 }
1059 if (!(opt & OPT_o)) { 1141 if (!(opt & OPT_o)) {
1060 /*
1061 unsigned i, n; 1142 unsigned i, n;
1062 for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) { 1143 for (i = 0; (n = d6_optflags[i].code) != 0; i++) {
1063 if (dhcp_optflags[i].flags & OPTION_REQ) { 1144 if (d6_optflags[i].flags & OPTION_REQ) {
1064 client_config.opt_mask[n >> 3] |= 1 << (n & 7); 1145 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
1065 } 1146 }
1066 } 1147 }
1067 */
1068 } 1148 }
1069 while (list_x) { 1149 while (list_x) {
1070 char *optstr = llist_pop(&list_x); 1150 char *optstr = llist_pop(&list_x);
@@ -1073,7 +1153,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
1073 *colon = ' '; 1153 *colon = ' ';
1074 /* now it looks similar to udhcpd's config file line: 1154 /* now it looks similar to udhcpd's config file line:
1075 * "optname optval", using the common routine: */ 1155 * "optname optval", using the common routine: */
1076 udhcp_str2optset(optstr, &client_config.options); 1156 udhcp_str2optset(optstr, &client_config.options, d6_optflags, d6_option_strings);
1077 if (colon) 1157 if (colon)
1078 *colon = ':'; /* restore it for NOMMU reexec */ 1158 *colon = ':'; /* restore it for NOMMU reexec */
1079 } 1159 }
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 6aa6731fb..1a66c610e 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -1346,7 +1346,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1346 char *optstr = llist_pop(&list_O); 1346 char *optstr = llist_pop(&list_O);
1347 unsigned n = bb_strtou(optstr, NULL, 0); 1347 unsigned n = bb_strtou(optstr, NULL, 0);
1348 if (errno || n > 254) { 1348 if (errno || n > 254) {
1349 n = udhcp_option_idx(optstr); 1349 n = udhcp_option_idx(optstr, dhcp_option_strings);
1350 n = dhcp_optflags[n].code; 1350 n = dhcp_optflags[n].code;
1351 } 1351 }
1352 client_config.opt_mask[n >> 3] |= 1 << (n & 7); 1352 client_config.opt_mask[n >> 3] |= 1 << (n & 7);
@@ -1366,7 +1366,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
1366 *colon = ' '; 1366 *colon = ' ';
1367 /* now it looks similar to udhcpd's config file line: 1367 /* now it looks similar to udhcpd's config file line:
1368 * "optname optval", using the common routine: */ 1368 * "optname optval", using the common routine: */
1369 udhcp_str2optset(optstr, &client_config.options); 1369 udhcp_str2optset(optstr, &client_config.options, dhcp_optflags, dhcp_option_strings);
1370 if (colon) 1370 if (colon)
1371 *colon = ':'; /* restore it for NOMMU reexec */ 1371 *colon = ':'; /* restore it for NOMMU reexec */
1372 } 1372 }
diff --git a/networking/udhcp/dhcpc.h b/networking/udhcp/dhcpc.h
index 9f423a5b2..7fdbc9a6c 100644
--- a/networking/udhcp/dhcpc.h
+++ b/networking/udhcp/dhcpc.h
@@ -12,6 +12,7 @@ struct client_config_t {
12 IF_FEATURE_UDHCP_PORT(uint16_t port;) 12 IF_FEATURE_UDHCP_PORT(uint16_t port;)
13 int ifindex; /* Index number of the interface to use */ 13 int ifindex; /* Index number of the interface to use */
14 uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */ 14 uint8_t opt_mask[256 / 8]; /* Bitmask of options to send (-O option) */
15// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TODO: DHCPv6 has 16-bit option numbers
15 const char *interface; /* The name of the interface to use */ 16 const char *interface; /* The name of the interface to use */
16 char *pidfile; /* Optionally store the process ID */ 17 char *pidfile; /* Optionally store the process ID */
17 const char *script; /* User script to run at dhcp events */ 18 const char *script; /* User script to run at dhcp events */
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 5eff026bc..3a5fc2db7 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -361,6 +361,10 @@ static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
361 return 1; 361 return 1;
362} 362}
363 363
364static int FAST_FUNC read_optset(const char *line, void *arg) {
365 return udhcp_str2optset(line, arg, dhcp_optflags, dhcp_option_strings);
366}
367
364struct config_keyword { 368struct config_keyword {
365 const char *keyword; 369 const char *keyword;
366 int (*handler)(const char *line, void *var) FAST_FUNC; 370 int (*handler)(const char *line, void *var) FAST_FUNC;
@@ -387,8 +391,8 @@ static const struct config_keyword keywords[] = {
387 {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"}, 391 {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"},
388 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"}, 392 {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
389 /* keywords with no defaults must be last! */ 393 /* keywords with no defaults must be last! */
390 {"option" , udhcp_str2optset, OFS(options ), ""}, 394 {"option" , read_optset , OFS(options ), ""},
391 {"opt" , udhcp_str2optset, OFS(options ), ""}, 395 {"opt" , read_optset , OFS(options ), ""},
392 {"notify_file" , read_str , OFS(notify_file ), NULL}, 396 {"notify_file" , read_str , OFS(notify_file ), NULL},
393 {"sname" , read_str , OFS(sname ), NULL}, 397 {"sname" , read_str , OFS(sname ), NULL},
394 {"boot_file" , read_str , OFS(boot_file ), NULL}, 398 {"boot_file" , read_str , OFS(boot_file ), NULL},
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c
index 0a31f2643..9e1b46d2f 100644
--- a/networking/udhcp/packet.c
+++ b/networking/udhcp/packet.c
@@ -12,6 +12,7 @@
12#include <netinet/if_ether.h> 12#include <netinet/if_ether.h>
13#include <netpacket/packet.h> 13#include <netpacket/packet.h>
14 14
15#if ENABLE_UDHCPC || ENABLE_UDHCPD
15void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type) 16void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
16{ 17{
17 memset(packet, 0, sizeof(*packet)); 18 memset(packet, 0, sizeof(*packet));
@@ -29,6 +30,7 @@ void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
29 packet->options[0] = DHCP_END; 30 packet->options[0] = DHCP_END;
30 udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type); 31 udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
31} 32}
33#endif
32 34
33#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2 35#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
34void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet) 36void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet)
diff --git a/procps/pgrep.c b/procps/pgrep.c
index e932a32bc..3d01c6cff 100644
--- a/procps/pgrep.c
+++ b/procps/pgrep.c
@@ -26,10 +26,11 @@
26//kbuild:lib-$(CONFIG_PKILL) += pgrep.o 26//kbuild:lib-$(CONFIG_PKILL) += pgrep.o
27 27
28//usage:#define pgrep_trivial_usage 28//usage:#define pgrep_trivial_usage
29//usage: "[-flnovx] [-s SID|-P PPID|PATTERN]" 29//usage: "[-flanovx] [-s SID|-P PPID|PATTERN]"
30//usage:#define pgrep_full_usage "\n\n" 30//usage:#define pgrep_full_usage "\n\n"
31//usage: "Display process(es) selected by regex PATTERN\n" 31//usage: "Display process(es) selected by regex PATTERN\n"
32//usage: "\n -l Show command name too" 32//usage: "\n -l Show command name too"
33//usage: "\n -a Show command line too"
33//usage: "\n -f Match against entire command line" 34//usage: "\n -f Match against entire command line"
34//usage: "\n -n Show the newest process only" 35//usage: "\n -n Show the newest process only"
35//usage: "\n -o Show the oldest process only" 36//usage: "\n -o Show the oldest process only"
@@ -55,13 +56,14 @@
55#include "xregex.h" 56#include "xregex.h"
56 57
57/* Idea taken from kill.c */ 58/* Idea taken from kill.c */
58#define pgrep (ENABLE_PGREP && applet_name[1] == 'g') 59#define pgrep (ENABLE_PGREP && (!ENABLE_PKILL || applet_name[1] == 'g'))
59#define pkill (ENABLE_PKILL && applet_name[1] == 'k') 60#define pkill (ENABLE_PKILL && (!ENABLE_PGREP || applet_name[1] == 'k'))
60 61
61enum { 62enum {
62 /* "vlfxons:P:" */ 63 /* "vlafxons:+P:+" */
63 OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */ 64 OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
64 OPTBIT_L, 65 OPTBIT_L,
66 OPTBIT_A,
65 OPTBIT_F, 67 OPTBIT_F,
66 OPTBIT_X, 68 OPTBIT_X,
67 OPTBIT_O, 69 OPTBIT_O,
@@ -72,6 +74,7 @@ enum {
72 74
73#define OPT_INVERT (opt & (1 << OPTBIT_V)) 75#define OPT_INVERT (opt & (1 << OPTBIT_V))
74#define OPT_LIST (opt & (1 << OPTBIT_L)) 76#define OPT_LIST (opt & (1 << OPTBIT_L))
77#define OPT_LISTFULL (opt & (1 << OPTBIT_A))
75#define OPT_FULL (opt & (1 << OPTBIT_F)) 78#define OPT_FULL (opt & (1 << OPTBIT_F))
76#define OPT_ANCHOR (opt & (1 << OPTBIT_X)) 79#define OPT_ANCHOR (opt & (1 << OPTBIT_X))
77#define OPT_FIRST (opt & (1 << OPTBIT_O)) 80#define OPT_FIRST (opt & (1 << OPTBIT_O))
@@ -82,7 +85,7 @@ enum {
82static void act(unsigned pid, char *cmd, int signo) 85static void act(unsigned pid, char *cmd, int signo)
83{ 86{
84 if (pgrep) { 87 if (pgrep) {
85 if (option_mask32 & (1 << OPTBIT_L)) /* OPT_LIST */ 88 if (option_mask32 & ((1 << OPTBIT_L)|(1 << OPTBIT_A))) /* -l or -a */
86 printf("%u %s\n", pid, cmd); 89 printf("%u %s\n", pid, cmd);
87 else 90 else
88 printf("%u\n", pid); 91 printf("%u\n", pid);
@@ -124,7 +127,7 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
124 /* Parse remaining options */ 127 /* Parse remaining options */
125 ppid2match = -1; 128 ppid2match = -1;
126 sid2match = -1; 129 sid2match = -1;
127 opt = getopt32(argv, "vlfxons:+P:+", &sid2match, &ppid2match); 130 opt = getopt32(argv, "vlafxons:+P:+", &sid2match, &ppid2match);
128 argv += optind; 131 argv += optind;
129 132
130 if (pkill && OPT_LIST) { /* -l: print the whole signal list */ 133 if (pkill && OPT_LIST) { /* -l: print the whole signal list */
@@ -152,26 +155,37 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
152 proc = NULL; 155 proc = NULL;
153 while ((proc = procps_scan(proc, scan_mask)) != NULL) { 156 while ((proc = procps_scan(proc, scan_mask)) != NULL) {
154 char *cmd; 157 char *cmd;
158 int cmdlen;
155 159
156 if (proc->pid == pid) 160 if (proc->pid == pid)
157 continue; 161 continue;
158 162
163 if (ppid2match >= 0 && ppid2match != proc->ppid)
164 continue;
165 if (sid2match >= 0 && sid2match != proc->sid)
166 continue;
167
168 cmdlen = -1;
159 cmd = proc->argv0; 169 cmd = proc->argv0;
160 if (!cmd) { 170 if (!cmd) {
161 cmd = proc->comm; 171 cmd = proc->comm;
162 } else { 172 } else {
163 int i = proc->argv_len; 173 int i = proc->argv_len;
174
175 if (!OPT_LISTFULL)
176 cmdlen = strlen(cmd); /* not -a: find first NUL */
177 /*
178 * "sleep 11" looks like "sleep""\0""11""\0" in argv0.
179 * Make sure last "\0" does not get converted to " ":
180 */
181 if (i && cmd[i-1] == '\0')
182 i--;
164 while (--i >= 0) { 183 while (--i >= 0) {
165 if ((unsigned char)cmd[i] < ' ') 184 if ((unsigned char)cmd[i] < ' ')
166 cmd[i] = ' '; 185 cmd[i] = ' ';
167 } 186 }
168 } 187 }
169 188
170 if (ppid2match >= 0 && ppid2match != proc->ppid)
171 continue;
172 if (sid2match >= 0 && sid2match != proc->sid)
173 continue;
174
175 /* NB: OPT_INVERT is always 0 or 1 */ 189 /* NB: OPT_INVERT is always 0 or 1 */
176 if (!argv[0] 190 if (!argv[0]
177 || (regexec(&re_buffer, cmd, 1, re_match, 0) == 0 /* match found */ 191 || (regexec(&re_buffer, cmd, 1, re_match, 0) == 0 /* match found */
@@ -184,6 +198,8 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
184 cmd_last = xstrdup(cmd); 198 cmd_last = xstrdup(cmd);
185 continue; 199 continue;
186 } 200 }
201 if (cmdlen >= 0)
202 cmd[cmdlen] = '\0';
187 act(proc->pid, cmd, signo); 203 act(proc->pid, cmd, signo);
188 if (OPT_FIRST) 204 if (OPT_FIRST)
189 break; 205 break;
diff --git a/procps/pstree.c b/procps/pstree.c
index f97e99639..bc9f0c927 100644
--- a/procps/pstree.c
+++ b/procps/pstree.c
@@ -34,7 +34,7 @@
34 34
35struct child; 35struct child;
36 36
37#ifdef ENABLE_FEATURE_SHOW_THREADS 37#if ENABLE_FEATURE_SHOW_THREADS
38/* For threads, we add {...} around the comm, so we need two extra bytes */ 38/* For threads, we add {...} around the comm, so we need two extra bytes */
39# define COMM_DISP_LEN (COMM_LEN + 2) 39# define COMM_DISP_LEN (COMM_LEN + 2)
40#else 40#else
diff --git a/scripts/randomtest b/scripts/randomtest
index 287f1c771..1809838a4 100755
--- a/scripts/randomtest
+++ b/scripts/randomtest
@@ -82,6 +82,9 @@ if test x"$LIBC" = x"uclibc"; then
82 | grep -v CONFIG_BLKDISCARD \ 82 | grep -v CONFIG_BLKDISCARD \
83 | grep -v CONFIG_NSENTER \ 83 | grep -v CONFIG_NSENTER \
84 | grep -v CONFIG_UNSHARE \ 84 | grep -v CONFIG_UNSHARE \
85 | grep -v CONFIG_FALLOCATE \
86 | grep -v CONFIG_UDHCPC6 \
87 | grep -v CONFIG_ASH_INTERNAL_GLOB \
85 >.config.new 88 >.config.new
86 mv .config.new .config 89 mv .config.new .config
87 echo 'CONFIG_STATIC=y' >>.config 90 echo 'CONFIG_STATIC=y' >>.config
@@ -96,6 +99,9 @@ if test x"$LIBC" = x"uclibc"; then
96 echo '# CONFIG_BLKDISCARD is not set' >>.config 99 echo '# CONFIG_BLKDISCARD is not set' >>.config
97 echo '# CONFIG_NSENTER is not set' >>.config 100 echo '# CONFIG_NSENTER is not set' >>.config
98 echo '# CONFIG_UNSHARE is not set' >>.config 101 echo '# CONFIG_UNSHARE is not set' >>.config
102 echo '# CONFIG_FALLOCATE is not set' >>.config
103 echo '# CONFIG_UDHCPC6 is not set' >>.config
104 echo 'CONFIG_ASH_INTERNAL_GLOB=y' >>.config
99fi 105fi
100 106
101# If STATIC, remove some things. 107# If STATIC, remove some things.
diff --git a/scripts/randomtest.loop b/scripts/randomtest.loop
index 710f5fd05..4d14b652f 100755
--- a/scripts/randomtest.loop
+++ b/scripts/randomtest.loop
@@ -1,7 +1,11 @@
1#!/bin/sh 1#!/bin/sh
2 2
3run_testsuite=false
3run_testsuite=true 4run_testsuite=true
4 5
6run_single_test=false
7run_single_test=true
8
5test -d "$1" || { echo "'$1' is not a directory"; exit 1; } 9test -d "$1" || { echo "'$1' is not a directory"; exit 1; }
6test -x "$1/scripts/randomtest" || { echo "No scripts/randomtest in '$1'"; exit 1; } 10test -x "$1/scripts/randomtest" || { echo "No scripts/randomtest in '$1'"; exit 1; }
7 11
@@ -40,6 +44,28 @@ while sleep 1; do
40 fi 44 fi
41 tail -n10 -- "$dir/testsuite/runtest.log" 45 tail -n10 -- "$dir/testsuite/runtest.log"
42 fi 46 fi
47 if $run_single_test; then
48 (
49 cd -- "$dir" || exit 1
50 echo "Running make_single_applets.sh in $dir..."
51
52 if grep -q '# CONFIG_FEATURE_TFTP_GET is not set' .config \
53 && grep -q '# CONFIG_FEATURE_TFTP_PUT is not set' .config \
54 ; then
55 # If both off, tftp[d] is ifdefed out and test fails.
56 # Enable one:
57 sed 's/# CONFIG_FEATURE_TFTP_GET is not set/CONFIG_FEATURE_TFTP_GET=y/' -i .config
58 fi
59
60 ./make_single_applets.sh
61 )
62 if test $? != 0; then
63 echo "Failed make_single_applets.sh in $dir"
64 exit 1 # you may comment this out...
65 let fail++
66 continue
67 fi
68 fi
43 rm -rf -- "$dir" 69 rm -rf -- "$dir"
44 let cnt++ 70 let cnt++
45done 71done
diff --git a/scripts/trylink b/scripts/trylink
index 9f288c141..ba2d265bc 100755
--- a/scripts/trylink
+++ b/scripts/trylink
@@ -91,7 +91,9 @@ fi
91 91
92START_GROUP="-Wl,--start-group" 92START_GROUP="-Wl,--start-group"
93END_GROUP="-Wl,--end-group" 93END_GROUP="-Wl,--end-group"
94INFO_OPTS="-Wl,--warn-common -Wl,-Map,$EXE.map -Wl,--verbose" 94INFO_OPTS() {
95 echo "-Wl,--warn-common -Wl,-Map,$EXE.map -Wl,--verbose"
96}
95 97
96# gold may not support --sort-common (yet) 98# gold may not support --sort-common (yet)
97SORT_COMMON="-Wl,--sort-common" 99SORT_COMMON="-Wl,--sort-common"
@@ -194,7 +196,7 @@ if ! test -f busybox_ldscript; then
194 $GC_SECTIONS \ 196 $GC_SECTIONS \
195 $START_GROUP $O_FILES $A_FILES $END_GROUP \ 197 $START_GROUP $O_FILES $A_FILES $END_GROUP \
196 $l_list \ 198 $l_list \
197 $INFO_OPTS \ 199 `INFO_OPTS` \
198 || { 200 || {
199 cat $EXE.out 201 cat $EXE.out
200 exit 1 202 exit 1
@@ -225,7 +227,7 @@ else
225 -Wl,-T,busybox_ldscript \ 227 -Wl,-T,busybox_ldscript \
226 $START_GROUP $O_FILES $A_FILES $END_GROUP \ 228 $START_GROUP $O_FILES $A_FILES $END_GROUP \
227 $l_list \ 229 $l_list \
228 $INFO_OPTS \ 230 `INFO_OPTS` \
229 || { 231 || {
230 cat $EXE.out 232 cat $EXE.out
231 exit 1 233 exit 1
@@ -244,10 +246,14 @@ if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then
244 } 246 }
245 ln -s "libbusybox.so.$BB_VER" "$sharedlib_dir"/libbusybox.so 2>/dev/null 247 ln -s "libbusybox.so.$BB_VER" "$sharedlib_dir"/libbusybox.so 2>/dev/null
246 248
249 # Yes, "ld -shared -static" is a thing. It's a shared library which is itself static.
250 LBB_STATIC=""
251 test "$CONFIG_FEATURE_LIBBUSYBOX_STATIC" = y && LBB_STATIC="-Wl,-static"
252
247 EXE="$sharedlib_dir/libbusybox.so.${BB_VER}_unstripped" 253 EXE="$sharedlib_dir/libbusybox.so.${BB_VER}_unstripped"
248 try $CC $CFLAGS $LDFLAGS \ 254 try $CC $CFLAGS $LDFLAGS \
249 -o $EXE \ 255 -o $EXE \
250 -shared -fPIC \ 256 -shared -fPIC $LBB_STATIC \
251 -Wl,--enable-new-dtags \ 257 -Wl,--enable-new-dtags \
252 -Wl,-z,combreloc \ 258 -Wl,-z,combreloc \
253 -Wl,-soname="libbusybox.so.$BB_VER" \ 259 -Wl,-soname="libbusybox.so.$BB_VER" \
@@ -256,7 +262,7 @@ if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then
256 $SORT_SECTION \ 262 $SORT_SECTION \
257 $START_GROUP $A_FILES $END_GROUP \ 263 $START_GROUP $A_FILES $END_GROUP \
258 $l_list \ 264 $l_list \
259 $INFO_OPTS \ 265 `INFO_OPTS` \
260 || { 266 || {
261 echo "Linking $EXE failed" 267 echo "Linking $EXE failed"
262 cat $EXE.out 268 cat $EXE.out
@@ -277,7 +283,7 @@ if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then
277 $START_GROUP $O_FILES $END_GROUP \ 283 $START_GROUP $O_FILES $END_GROUP \
278 -L"$sharedlib_dir" -lbusybox \ 284 -L"$sharedlib_dir" -lbusybox \
279 $l_list \ 285 $l_list \
280 $INFO_OPTS \ 286 `INFO_OPTS` \
281 || { 287 || {
282 echo "Linking $EXE failed" 288 echo "Linking $EXE failed"
283 cat $EXE.out 289 cat $EXE.out
diff --git a/shell/ash.c b/shell/ash.c
index 5debd82f2..6cc29d25e 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -2566,12 +2566,8 @@ putprompt(const char *s)
2566} 2566}
2567#endif 2567#endif
2568 2568
2569#if ENABLE_ASH_EXPAND_PRMT
2570/* expandstr() needs parsing machinery, so it is far away ahead... */ 2569/* expandstr() needs parsing machinery, so it is far away ahead... */
2571static const char *expandstr(const char *ps); 2570static const char *expandstr(const char *ps);
2572#else
2573#define expandstr(s) s
2574#endif
2575 2571
2576static void 2572static void
2577setprompt_if(smallint do_set, int whichprompt) 2573setprompt_if(smallint do_set, int whichprompt)
@@ -2596,10 +2592,10 @@ setprompt_if(smallint do_set, int whichprompt)
2596 } 2592 }
2597#if ENABLE_ASH_EXPAND_PRMT 2593#if ENABLE_ASH_EXPAND_PRMT
2598 pushstackmark(&smark, stackblocksize()); 2594 pushstackmark(&smark, stackblocksize());
2599#endif
2600 putprompt(expandstr(prompt)); 2595 putprompt(expandstr(prompt));
2601#if ENABLE_ASH_EXPAND_PRMT
2602 popstackmark(&smark); 2596 popstackmark(&smark);
2597#else
2598 putprompt(prompt);
2603#endif 2599#endif
2604} 2600}
2605 2601
@@ -4244,15 +4240,19 @@ sprint_status48(char *s, int status, int sigonly)
4244 4240
4245 col = 0; 4241 col = 0;
4246 if (!WIFEXITED(status)) { 4242 if (!WIFEXITED(status)) {
4247 if (JOBS && WIFSTOPPED(status)) 4243#if JOBS
4244 if (WIFSTOPPED(status))
4248 st = WSTOPSIG(status); 4245 st = WSTOPSIG(status);
4249 else 4246 else
4247#endif
4250 st = WTERMSIG(status); 4248 st = WTERMSIG(status);
4251 if (sigonly) { 4249 if (sigonly) {
4252 if (st == SIGINT || st == SIGPIPE) 4250 if (st == SIGINT || st == SIGPIPE)
4253 goto out; 4251 goto out;
4254 if (JOBS && WIFSTOPPED(status)) 4252#if JOBS
4253 if (WIFSTOPPED(status))
4255 goto out; 4254 goto out;
4255#endif
4256 } 4256 }
4257 st &= 0x7f; 4257 st &= 0x7f;
4258//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata 4258//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
@@ -4501,8 +4501,10 @@ dowait(int block, struct job *job)
4501 goto out; 4501 goto out;
4502 } 4502 }
4503 /* The process wasn't found in job list */ 4503 /* The process wasn't found in job list */
4504 if (JOBS && !WIFSTOPPED(status)) 4504#if JOBS
4505 if (!WIFSTOPPED(status))
4505 jobless--; 4506 jobless--;
4507#endif
4506 out: 4508 out:
4507 INT_ON; 4509 INT_ON;
4508 4510
@@ -6285,6 +6287,7 @@ rmescapes(char *str, int flag)
6285 while (*p) { 6287 while (*p) {
6286 if ((unsigned char)*p == CTLQUOTEMARK) { 6288 if ((unsigned char)*p == CTLQUOTEMARK) {
6287// Note: both inquotes and protect_against_glob only affect whether 6289// Note: both inquotes and protect_against_glob only affect whether
6290// CTLESC,<ch> gets converted to <ch> or to \<ch>
6288 inquotes = ~inquotes; 6291 inquotes = ~inquotes;
6289 p++; 6292 p++;
6290 protect_against_glob = globbing; 6293 protect_against_glob = globbing;
@@ -6297,7 +6300,33 @@ rmescapes(char *str, int flag)
6297 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)"); 6300 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)");
6298#endif 6301#endif
6299 if (protect_against_glob) { 6302 if (protect_against_glob) {
6300 *q++ = '\\'; 6303 /*
6304 * We used to trust glob() and fnmatch() to eat
6305 * superfluous escapes (\z where z has no
6306 * special meaning anyway). But this causes
6307 * bugs such as string of one greek letter rho
6308 * (unicode-encoded as two bytes "cf,81")
6309 * getting encoded as "cf,CTLESC,81"
6310 * and here, converted to "cf,\,81" -
6311 * which does not go well with some flavors
6312 * of fnmatch() in unicode locales
6313 * (for example, glibc <= 2.22).
6314 *
6315 * Lets add "\" only on the chars which need it.
6316 * Testcases for less obvious chars are shown.
6317 */
6318 if (*p == '*'
6319 || *p == '?'
6320 || *p == '['
6321 || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */
6322 || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
6323 || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
6324 || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */
6325 /* Some libc support [^negate], that's why "^" also needs love */
6326 || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */
6327 ) {
6328 *q++ = '\\';
6329 }
6301 } 6330 }
6302 } else if (*p == '\\' && !inquotes) { 6331 } else if (*p == '\\' && !inquotes) {
6303 /* naked back slash */ 6332 /* naked back slash */
@@ -6963,7 +6992,6 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6963 char *loc; 6992 char *loc;
6964 char *rmesc, *rmescend; 6993 char *rmesc, *rmescend;
6965 char *str; 6994 char *str;
6966 IF_BASH_SUBSTR(int pos, len, orig_len;)
6967 int amount, resetloc; 6995 int amount, resetloc;
6968 IF_BASH_PATTERN_SUBST(int workloc;) 6996 IF_BASH_PATTERN_SUBST(int workloc;)
6969 IF_BASH_PATTERN_SUBST(char *repl = NULL;) 6997 IF_BASH_PATTERN_SUBST(char *repl = NULL;)
@@ -6992,14 +7020,23 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
6992 /* NOTREACHED */ 7020 /* NOTREACHED */
6993 7021
6994#if BASH_SUBSTR 7022#if BASH_SUBSTR
6995 case VSSUBSTR: 7023 case VSSUBSTR: {
6996//TODO: support more general format ${v:EXPR:EXPR}, 7024 int pos, len, orig_len;
6997// where EXPR follows $(()) rules 7025 char *colon;
7026
6998 loc = str = stackblock() + strloc; 7027 loc = str = stackblock() + strloc;
7028
7029# if !ENABLE_FEATURE_SH_MATH
7030# define ash_arith number
7031# endif
6999 /* Read POS in ${var:POS:LEN} */ 7032 /* Read POS in ${var:POS:LEN} */
7000 pos = atoi(loc); /* number(loc) errors out on "1:4" */ 7033 colon = strchr(loc, ':');
7001 len = str - startp - 1; 7034 if (colon) *colon = '\0';
7035 pos = ash_arith(loc);
7036 if (colon) *colon = ':';
7002 7037
7038 /* Read LEN in ${var:POS:LEN} */
7039 len = str - startp - 1;
7003 /* *loc != '\0', guaranteed by parser */ 7040 /* *loc != '\0', guaranteed by parser */
7004 if (quotes) { 7041 if (quotes) {
7005 char *ptr; 7042 char *ptr;
@@ -7013,25 +7050,21 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7013 } 7050 }
7014 } 7051 }
7015 orig_len = len; 7052 orig_len = len;
7016
7017 if (*loc++ == ':') { 7053 if (*loc++ == ':') {
7018 /* ${var::LEN} */ 7054 /* ${var::LEN} */
7019 len = number(loc); 7055 len = ash_arith(loc);
7020 } else { 7056 } else {
7021 /* Skip POS in ${var:POS:LEN} */ 7057 /* Skip POS in ${var:POS:LEN} */
7022 len = orig_len; 7058 len = orig_len;
7023 while (*loc && *loc != ':') { 7059 while (*loc && *loc != ':') {
7024 /* TODO?
7025 * bash complains on: var=qwe; echo ${var:1a:123}
7026 if (!isdigit(*loc))
7027 ash_msg_and_raise_error(msg_illnum, str);
7028 */
7029 loc++; 7060 loc++;
7030 } 7061 }
7031 if (*loc++ == ':') { 7062 if (*loc++ == ':') {
7032 len = number(loc); 7063 len = ash_arith(loc);
7033 } 7064 }
7034 } 7065 }
7066# undef ash_arith
7067
7035 if (pos < 0) { 7068 if (pos < 0) {
7036 /* ${VAR:$((-n)):l} starts n chars from the end */ 7069 /* ${VAR:$((-n)):l} starts n chars from the end */
7037 pos = orig_len + pos; 7070 pos = orig_len + pos;
@@ -7039,12 +7072,16 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7039 if ((unsigned)pos >= orig_len) { 7072 if ((unsigned)pos >= orig_len) {
7040 /* apart from obvious ${VAR:999999:l}, 7073 /* apart from obvious ${VAR:999999:l},
7041 * covers ${VAR:$((-9999999)):l} - result is "" 7074 * covers ${VAR:$((-9999999)):l} - result is ""
7042 * (bash-compat) 7075 * (bash compat)
7043 */ 7076 */
7044 pos = 0; 7077 pos = 0;
7045 len = 0; 7078 len = 0;
7046 } 7079 }
7047 if (len > (orig_len - pos)) 7080 if (len < 0) {
7081 /* ${VAR:N:-M} sets LEN to strlen()-M */
7082 len = (orig_len - pos) + len;
7083 }
7084 if ((unsigned)len > (orig_len - pos))
7048 len = orig_len - pos; 7085 len = orig_len - pos;
7049 7086
7050 for (str = startp; pos; str++, pos--) { 7087 for (str = startp; pos; str++, pos--) {
@@ -7060,6 +7097,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7060 amount = loc - expdest; 7097 amount = loc - expdest;
7061 STADJUST(amount, expdest); 7098 STADJUST(amount, expdest);
7062 return loc; 7099 return loc;
7100 }
7063#endif /* BASH_SUBSTR */ 7101#endif /* BASH_SUBSTR */
7064 } 7102 }
7065 7103
@@ -7104,6 +7142,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
7104#if BASH_PATTERN_SUBST 7142#if BASH_PATTERN_SUBST
7105 workloc = expdest - (char *)stackblock(); 7143 workloc = expdest - (char *)stackblock();
7106 if (subtype == VSREPLACE || subtype == VSREPLACEALL) { 7144 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
7145 int len;
7107 char *idx, *end; 7146 char *idx, *end;
7108 7147
7109 if (!repl) { 7148 if (!repl) {
@@ -7970,7 +8009,9 @@ expandhere(union node *arg, int fd)
7970static int 8009static int
7971patmatch(char *pattern, const char *string) 8010patmatch(char *pattern, const char *string)
7972{ 8011{
7973 return pmatch(preglob(pattern, 0), string); 8012 char *p = preglob(pattern, 0);
8013 //bb_error_msg("fnmatch(pattern:'%s',str:'%s')", p, string);
8014 return pmatch(p, string);
7974} 8015}
7975 8016
7976/* 8017/*
@@ -8072,7 +8113,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **
8072 clearenv(); 8113 clearenv();
8073 while (*envp) 8114 while (*envp)
8074 putenv(*envp++); 8115 putenv(*envp++);
8075 run_applet_no_and_exit(applet_no, argv); 8116 run_applet_no_and_exit(applet_no, cmd, argv);
8076 } 8117 }
8077 /* re-exec ourselves with the new arguments */ 8118 /* re-exec ourselves with the new arguments */
8078 execve(bb_busybox_exec_path, argv, envp); 8119 execve(bb_busybox_exec_path, argv, envp);
@@ -12022,9 +12063,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12022 smallint dblquote; 12063 smallint dblquote;
12023 smallint oldstyle; 12064 smallint oldstyle;
12024 IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */ 12065 IF_FEATURE_SH_MATH(smallint prevsyntax;) /* syntax before arithmetic */
12025#if ENABLE_ASH_EXPAND_PRMT
12026 smallint pssyntax; /* we are expanding a prompt string */ 12066 smallint pssyntax; /* we are expanding a prompt string */
12027#endif
12028 int varnest; /* levels of variables expansion */ 12067 int varnest; /* levels of variables expansion */
12029 IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */ 12068 IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */
12030 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */ 12069 IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
@@ -12036,11 +12075,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12036 bqlist = NULL; 12075 bqlist = NULL;
12037 quotef = 0; 12076 quotef = 0;
12038 IF_FEATURE_SH_MATH(prevsyntax = 0;) 12077 IF_FEATURE_SH_MATH(prevsyntax = 0;)
12039#if ENABLE_ASH_EXPAND_PRMT
12040 pssyntax = (syntax == PSSYNTAX); 12078 pssyntax = (syntax == PSSYNTAX);
12041 if (pssyntax) 12079 if (pssyntax)
12042 syntax = DQSYNTAX; 12080 syntax = DQSYNTAX;
12043#endif
12044 dblquote = (syntax == DQSYNTAX); 12081 dblquote = (syntax == DQSYNTAX);
12045 varnest = 0; 12082 varnest = 0;
12046 IF_FEATURE_SH_MATH(arinest = 0;) 12083 IF_FEATURE_SH_MATH(arinest = 0;)
@@ -12094,12 +12131,10 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12094 } else if (c == '\n') { 12131 } else if (c == '\n') {
12095 nlprompt(); 12132 nlprompt();
12096 } else { 12133 } else {
12097#if ENABLE_ASH_EXPAND_PRMT
12098 if (c == '$' && pssyntax) { 12134 if (c == '$' && pssyntax) {
12099 USTPUTC(CTLESC, out); 12135 USTPUTC(CTLESC, out);
12100 USTPUTC('\\', out); 12136 USTPUTC('\\', out);
12101 } 12137 }
12102#endif
12103 /* Backslash is retained if we are in "str" and next char isn't special */ 12138 /* Backslash is retained if we are in "str" and next char isn't special */
12104 if (dblquote 12139 if (dblquote
12105 && c != '\\' 12140 && c != '\\'
@@ -12384,7 +12419,7 @@ parsesub: {
12384#if ENABLE_FEATURE_SH_MATH 12419#if ENABLE_FEATURE_SH_MATH
12385 PARSEARITH(); 12420 PARSEARITH();
12386#else 12421#else
12387 raise_error_syntax("you disabled math support for $((arith)) syntax"); 12422 raise_error_syntax("support for $((arith)) is disabled");
12388#endif 12423#endif
12389 } else { 12424 } else {
12390 pungetc(); 12425 pungetc();
@@ -12933,7 +12968,6 @@ parseheredoc(void)
12933/* 12968/*
12934 * called by editline -- any expansions to the prompt should be added here. 12969 * called by editline -- any expansions to the prompt should be added here.
12935 */ 12970 */
12936#if ENABLE_ASH_EXPAND_PRMT
12937static const char * 12971static const char *
12938expandstr(const char *ps) 12972expandstr(const char *ps)
12939{ 12973{
@@ -12959,7 +12993,6 @@ expandstr(const char *ps)
12959 expandarg(&n, NULL, EXP_QUOTED); 12993 expandarg(&n, NULL, EXP_QUOTED);
12960 return stackblock(); 12994 return stackblock();
12961} 12995}
12962#endif
12963 12996
12964/* 12997/*
12965 * Execute a command or commands contained in a string. 12998 * Execute a command or commands contained in a string.
@@ -13497,7 +13530,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13497 exitcode = 0; 13530 exitcode = 0;
13498 while (*ap) { 13531 while (*ap) {
13499 signo = get_signum(*ap); 13532 signo = get_signum(*ap);
13500 if (signo < 0) { 13533 if (signo < 0 || signo >= NSIG) {
13501 /* Mimic bash message exactly */ 13534 /* Mimic bash message exactly */
13502 ash_msg("%s: invalid signal specification", *ap); 13535 ash_msg("%s: invalid signal specification", *ap);
13503 exitcode = 1; 13536 exitcode = 1;
diff --git a/shell/ash_test/ash-heredoc/heredoc1.right b/shell/ash_test/ash-heredoc/heredoc1.right
index 40aa5a5fe..7fc68f3e1 100644
--- a/shell/ash_test/ash-heredoc/heredoc1.right
+++ b/shell/ash_test/ash-heredoc/heredoc1.right
@@ -1 +1,5 @@
1./heredoc1.tests: line 3: syntax error: unexpected "then" 1qwe
2asd
3123
4456
5Ok
diff --git a/shell/ash_test/ash-heredoc/heredoc1.tests b/shell/ash_test/ash-heredoc/heredoc1.tests
index a912a67c7..2eeb4726b 100755
--- a/shell/ash_test/ash-heredoc/heredoc1.tests
+++ b/shell/ash_test/ash-heredoc/heredoc1.tests
@@ -1,3 +1,9 @@
1# We used to SEGV on this: 1cat <<000; cat <<www; cat <<eee
2 2000
3<<EOF; then <W 3qwe
4asd
5www
6123
7456
8eee
9echo Ok
diff --git a/shell/ash_test/ash-heredoc/heredoc3.right b/shell/ash_test/ash-heredoc/heredoc3.right
index ce0136250..6ed517f74 100644
--- a/shell/ash_test/ash-heredoc/heredoc3.right
+++ b/shell/ash_test/ash-heredoc/heredoc3.right
@@ -1 +1,9 @@
1hello 1exit EOF-f
2"
3echo $f
4echo `echo Hello World`
5moo
6 EOF-f
7EOF-f f
8EOF-f
9Ok
diff --git a/shell/ash_test/ash-heredoc/heredoc3.tests b/shell/ash_test/ash-heredoc/heredoc3.tests
index 96c227cc1..938577a89 100755
--- a/shell/ash_test/ash-heredoc/heredoc3.tests
+++ b/shell/ash_test/ash-heredoc/heredoc3.tests
@@ -1,9 +1,12 @@
1echo hello >greeting 1f=1
2cat <<EOF && 2 cat <<- EOF-f""
3$(cat greeting) 3 exit EOF-f
4EOF 4"
5{ 5echo $f
6 echo $? 6echo `echo Hello World`
7 cat greeting 7 moo
8} >/dev/null 8 EOF-f
9rm greeting 9EOF-f f
10EOF-f
11EOF-f
12echo Ok
diff --git a/shell/ash_test/ash-heredoc/heredoc8.right b/shell/ash_test/ash-heredoc/heredoc8.right
new file mode 100644
index 000000000..af396660e
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc8.right
@@ -0,0 +1 @@
./heredoc8.tests: line 3: syntax error: unexpected "then"
diff --git a/shell/ash_test/ash-heredoc/heredoc8.tests b/shell/ash_test/ash-heredoc/heredoc8.tests
new file mode 100755
index 000000000..f7bc0737a
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc8.tests
@@ -0,0 +1,3 @@
1# ash used to SEGV on this:
2
3<<EOF; then <W
diff --git a/shell/ash_test/ash-heredoc/heredoc9.right b/shell/ash_test/ash-heredoc/heredoc9.right
new file mode 100644
index 000000000..ce0136250
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc9.right
@@ -0,0 +1 @@
hello
diff --git a/shell/ash_test/ash-heredoc/heredoc9.tests b/shell/ash_test/ash-heredoc/heredoc9.tests
new file mode 100755
index 000000000..96c227cc1
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc9.tests
@@ -0,0 +1,9 @@
1echo hello >greeting
2cat <<EOF &&
3$(cat greeting)
4EOF
5{
6 echo $?
7 cat greeting
8} >/dev/null
9rm greeting
diff --git a/shell/ash_test/ash-misc/shift1.tests b/shell/ash_test/ash-misc/shift1.tests
index 0992d9b1b..2774b35ea 100755
--- a/shell/ash_test/ash-misc/shift1.tests
+++ b/shell/ash_test/ash-misc/shift1.tests
@@ -1,5 +1,5 @@
1$THIS_SH -c 'shift; echo "$@"' 0 1 2 3 4 1$THIS_SH -c 'shift; echo "$@"' 0 1 2 3 4
2#We do abort on -1, but then we abort. bash executes echo. 2# We do complain on -1, but then we abort. bash executes echo.
3$THIS_SH -c 'shift -1; echo "$@"' 0 1 2 3 4 3$THIS_SH -c 'shift -1; echo "$@"' 0 1 2 3 4
4$THIS_SH -c 'shift 0; echo "$@"' 0 1 2 3 4 4$THIS_SH -c 'shift 0; echo "$@"' 0 1 2 3 4
5$THIS_SH -c 'shift 1; echo "$@"' 0 1 2 3 4 5$THIS_SH -c 'shift 1; echo "$@"' 0 1 2 3 4
diff --git a/shell/ash_test/ash-misc/sigint1.right b/shell/ash_test/ash-misc/sigint1.right
deleted file mode 100644
index a9094b056..000000000
--- a/shell/ash_test/ash-misc/sigint1.right
+++ /dev/null
@@ -1 +0,0 @@
1Sending SIGINT to main shell PID
diff --git a/shell/ash_test/ash-misc/sigint1.tests b/shell/ash_test/ash-misc/sigint1.tests
deleted file mode 100755
index 3d483d32a..000000000
--- a/shell/ash_test/ash-misc/sigint1.tests
+++ /dev/null
@@ -1,41 +0,0 @@
1# What should happen if non-interactive shell gets SIGINT?
2
3(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
4
5# We create a child which exits with 0 even on SIGINT
6# (The complex command is necessary only if SIGINT is generated by ^C,
7# in this testcase even bare "sleep 2" would do because
8# in the testcase we don't send SIGINT *to the child*...)
9$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
10
11# In one second, we (main shell) get SIGINT here.
12# The question is whether we should, or should not, exit.
13
14# bash will not stop here. It will execute next command(s).
15
16# The rationale for this is described here:
17# http://www.cons.org/cracauer/sigint.html
18#
19# Basically, bash will not exit on SIGINT immediately if it waits
20# for a child. It will wait for the child to exit.
21# If child exits NOT by dying on SIGINT, then bash will not exit.
22#
23# The idea is that the following script:
24# | emacs file.txt
25# | more cmds
26# User may use ^C to interrupt editor's ops like search. But then
27# emacs exits normally. User expects that script doesn't stop.
28#
29# This is a nice idea, but detecting "did process really exit
30# with SIGINT?" is racy. Consider:
31# | bash -c 'while true; do /bin/true; done'
32# When ^C is pressed while bash waits for /bin/true to exit,
33# it may happen that /bin/true exits with exitcode 0 before
34# ^C is delivered to it as SIGINT. bash will see SIGINT, then
35# it will see that child exited with 0, and bash will NOT EXIT.
36
37# Therefore we do not implement bash behavior.
38# I'd say that emacs need to put itself into a separate pgrp
39# to isolate shell from getting stray SIGINTs from ^C.
40
41echo Next command after SIGINT was executed
diff --git a/shell/ash_test/ash-misc/tickquote1.tests b/shell/ash_test/ash-misc/tickquote1.tests
index 90d5bbc9b..8416ad996 100755
--- a/shell/ash_test/ash-misc/tickquote1.tests
+++ b/shell/ash_test/ash-misc/tickquote1.tests
@@ -1 +1 @@
echo `"pwd` echo _`"pwd`_
diff --git a/shell/ash_test/ash-misc/wait6.right b/shell/ash_test/ash-misc/wait6.right
new file mode 100644
index 000000000..12decc137
--- /dev/null
+++ b/shell/ash_test/ash-misc/wait6.right
@@ -0,0 +1,2 @@
10
23
diff --git a/shell/ash_test/ash-misc/wait6.tests b/shell/ash_test/ash-misc/wait6.tests
new file mode 100755
index 000000000..c23713199
--- /dev/null
+++ b/shell/ash_test/ash-misc/wait6.tests
@@ -0,0 +1,6 @@
1# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
2# It prints 0, then 3:
3(sleep 0; exit 3) & sleep 1
4echo $?
5wait $!
6echo $?
diff --git a/shell/hush_test/hush-bugs/and_or_and_backgrounding.right b/shell/ash_test/ash-parsing/and_or_and_backgrounding.right
index 90ce63e01..90ce63e01 100644
--- a/shell/hush_test/hush-bugs/and_or_and_backgrounding.right
+++ b/shell/ash_test/ash-parsing/and_or_and_backgrounding.right
diff --git a/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests b/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests
index 05acfb863..204d44490 100755
--- a/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests
+++ b/shell/ash_test/ash-parsing/and_or_and_backgrounding.tests
@@ -1,4 +1,3 @@
1# UNFIXED BUG: hush thinks that ; && || & have the same precedence.
2# According to this doc, && || have higher precedence than ; &. 1# According to this doc, && || have higher precedence than ; &.
3# See example below. 2# See example below.
4# Precedence of ; is not a problem in practice. Precedence of & is. 3# Precedence of ; is not a problem in practice. Precedence of & is.
diff --git a/shell/ash_test/ash-quoting/quoted_punct.right b/shell/ash_test/ash-quoting/quoted_punct.right
new file mode 100644
index 000000000..ab66c3ce0
--- /dev/null
+++ b/shell/ash_test/ash-quoting/quoted_punct.right
@@ -0,0 +1,35 @@
1ok
2ok
3ok
4ok
5ok
6ok
7ok
8ok
9ok
10ok
11ok
12ok
13ok
14ok
15ok
16ok
17ok
18ok
19ok
20ok
21ok
22ok
23ok
24ok
25ok
26ok
27ok
28ok
29ok
30ok
31ok
32ok
33ok
34ok
35ok
diff --git a/shell/ash_test/ash-quoting/quoted_punct.tests b/shell/ash_test/ash-quoting/quoted_punct.tests
new file mode 100755
index 000000000..83ee40bf4
--- /dev/null
+++ b/shell/ash_test/ash-quoting/quoted_punct.tests
@@ -0,0 +1,41 @@
1# Testing glob-escaping of every ASCII punctuation char
2# Some chars have more than one test
3# 21..2f
4case '!' in [\!] ) echo ok;; *) echo 'WRONG!';; esac
5case '"' in [\"] ) echo ok;; *) echo 'WRONG"';; esac
6case '#' in [\#] ) echo ok;; *) echo 'WRONG#';; esac
7case '$' in [\$] ) echo ok;; *) echo 'WRONG$';; esac
8case '%' in [\%] ) echo ok;; *) echo 'WRONG%';; esac
9case '&' in [\&] ) echo ok;; *) echo 'WRONG&';; esac
10case "'" in [\'] ) echo ok;; *) echo "WRONG'";; esac
11case '(' in [\(] ) echo ok;; *) echo 'WRONG(';; esac
12case ')' in [\)] ) echo ok;; *) echo 'WRONG)';; esac
13case '*' in [\*] ) echo ok;; *) echo 'WRONG*';; esac
14case '+' in [\+] ) echo ok;; *) echo 'WRONG+';; esac
15case ',' in [\,] ) echo ok;; *) echo 'WRONG,';; esac
16case '-' in [\-] ) echo ok;; *) echo 'WRONG-';; esac
17case '-' in [a\-c]) echo ok;; *) echo 'WRONGa\-c';; esac
18case '.' in [\.] ) echo ok;; *) echo 'WRONG.';; esac
19case '/' in [\/] ) echo ok;; *) echo 'WRONG/';; esac
20# 3a..40
21case ':' in [\:] ) echo ok;; *) echo 'WRONG:';; esac
22case ';' in [\;] ) echo ok;; *) echo 'WRONG;';; esac
23case '<' in [\<] ) echo ok;; *) echo 'WRONG<';; esac
24case '=' in [\=] ) echo ok;; *) echo 'WRONG=';; esac
25case '>' in [\>] ) echo ok;; *) echo 'WRONG>';; esac
26case '?' in [\?] ) echo ok;; *) echo 'WRONG?';; esac
27case '@' in [\@] ) echo ok;; *) echo 'WRONG@';; esac
28# 5b..60
29case '[' in [\[] ) echo ok;; *) echo 'WRONG[';; esac
30case '\' in [\\] ) echo ok;; *) echo 'WRONG\';; esac
31case '\' in \\ ) echo ok;; *) echo 'WRONG\\';; esac
32case ']' in [\]] ) echo ok;; *) echo 'WRONG]';; esac
33case ']' in [a\]]) echo ok;; *) echo 'WRONGa]';; esac
34case '^' in [\^] ) echo ok;; *) echo 'WRONG^';; esac
35case '_' in [\_] ) echo ok;; *) echo 'WRONG_';; esac
36case '`' in [\`] ) echo ok;; *) echo 'WRONG`';; esac
37# 7b..7e
38case '{' in [\{] ) echo ok;; *) echo 'WRONG{';; esac
39case '|' in [\|] ) echo ok;; *) echo 'WRONG|';; esac
40case '}' in [\}] ) echo ok;; *) echo 'WRONG}';; esac
41case '~' in [\~] ) echo ok;; *) echo 'WRONG~';; esac
diff --git a/shell/ash_test/ash-quoting/unicode_8x_chars.right b/shell/ash_test/ash-quoting/unicode_8x_chars.right
new file mode 100644
index 000000000..7780b88b4
--- /dev/null
+++ b/shell/ash_test/ash-quoting/unicode_8x_chars.right
@@ -0,0 +1,6 @@
1ok
2ok
3ok
4ok
5ok
6ok
diff --git a/shell/ash_test/ash-quoting/unicode_8x_chars.tests b/shell/ash_test/ash-quoting/unicode_8x_chars.tests
new file mode 100755
index 000000000..1258745ec
--- /dev/null
+++ b/shell/ash_test/ash-quoting/unicode_8x_chars.tests
@@ -0,0 +1,28 @@
1# Unicode: cf 80
2case π in
3( "π" ) echo ok ;;
4( * ) echo WRONG ;;
5esac
6# Unicode: cf 81
7case ρ in
8( "ρ" ) echo ok ;;
9( * ) echo WRONG ;;
10esac
11# Unicode: cf 82
12case ς in
13( "ς" ) echo ok ;;
14( * ) echo WRONG ;;
15esac
16
17case "π" in
18( π ) echo ok ;;
19( * ) echo WRONG ;;
20esac
21case "ρ" in
22( ρ ) echo ok ;;
23( * ) echo WRONG ;;
24esac
25case "ς" in
26( ς ) echo ok ;;
27( * ) echo WRONG ;;
28esac
diff --git a/shell/ash_test/ash-redir/redir5.right b/shell/ash_test/ash-redir/redir5.right
index 9d0877765..52cce4feb 100644
--- a/shell/ash_test/ash-redir/redir5.right
+++ b/shell/ash_test/ash-redir/redir5.right
@@ -1,2 +1,4 @@
1./redir5.tests: line 2: 10: Bad file descriptor 1Backgrounded pipes shall have their stdin redirected to /dev/null
2OK 2Zero:0
3Zero:0
4Done
diff --git a/shell/ash_test/ash-redir/redir5.tests b/shell/ash_test/ash-redir/redir5.tests
index 91b0c1ff9..957f9c81f 100755
--- a/shell/ash_test/ash-redir/redir5.tests
+++ b/shell/ash_test/ash-redir/redir5.tests
@@ -1,3 +1,13 @@
1# ash uses fd 10 (usually) for reading the script 1echo "Backgrounded pipes shall have their stdin redirected to /dev/null"
2echo LOST >&10 2
3echo OK 3# 1. bash does not redirect stdin to /dev/null if it is interactive.
4# hush does it always (this is allowed by standards).
5
6# 2. Failure will result in this script hanging
7
8cat & wait; echo Zero:$?
9
10# This does not work for bash! bash bug?
11cat | cat & wait; echo Zero:$?
12
13echo Done
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd.right b/shell/ash_test/ash-redir/redir_to_bad_fd.right
new file mode 100644
index 000000000..43b8af293
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd.right
@@ -0,0 +1,2 @@
1./redir_to_bad_fd.tests: line 2: 10: Bad file descriptor
2OK
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd.tests b/shell/ash_test/ash-redir/redir_to_bad_fd.tests
new file mode 100755
index 000000000..91b0c1ff9
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd.tests
@@ -0,0 +1,3 @@
1# ash uses fd 10 (usually) for reading the script
2echo LOST >&10
3echo OK
diff --git a/shell/ash_test/ash-vars/var-utf8-length.tests b/shell/ash_test/ash-vars/var-utf8-length.tests
index d04b2cbb6..b6e87f191 100755
--- a/shell/ash_test/ash-vars/var-utf8-length.tests
+++ b/shell/ash_test/ash-vars/var-utf8-length.tests
@@ -1,2 +1,4 @@
1LANG=en_US.UTF-8
2LC_ALL=en_US.UTF-8
1X=abcdÉfghÍjklmnÓpqrstÚvwcyz 3X=abcdÉfghÍjklmnÓpqrstÚvwcyz
2echo ${#X} 4echo ${#X}
diff --git a/shell/ash_test/ash-vars/var_bash1a.tests b/shell/ash_test/ash-vars/var_bash1a.tests
index 551dd9acc..c75c2dc53 100755
--- a/shell/ash_test/ash-vars/var_bash1a.tests
+++ b/shell/ash_test/ash-vars/var_bash1a.tests
@@ -5,7 +5,7 @@ echo "parameter '${parameter}'"
5echo "varoffset2 '${parameter:${offset}}'" 5echo "varoffset2 '${parameter:${offset}}'"
6echo "varoffset-2 '${parameter:${noffset}}'" 6echo "varoffset-2 '${parameter:${noffset}}'"
7echo "literal '2' '${parameter:2}'" 7echo "literal '2' '${parameter:2}'"
8# This is not inrpreted as ${VAR:POS{:LEN}}, 8# This is not interpreted as ${VAR:POS{:LEN}},
9# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD 9# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD
10echo "literal '-2' '${parameter:-2}'" 10echo "literal '-2' '${parameter:-2}'"
11echo "literal ' -2' '${parameter: -2}'" 11echo "literal ' -2' '${parameter: -2}'"
diff --git a/shell/ash_test/ash-vars/var_bash1b.right b/shell/ash_test/ash-vars/var_bash1b.right
new file mode 100644
index 000000000..fafc0f07c
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash1b.right
@@ -0,0 +1,23 @@
1all |0123456
24: |456
34:2 |45
44:-1 |45
54:-2 |4
64:-3 |
7-4: |3456
8-4:2 |34
9-4:-1 |345
10-4:-2 |34
11-4:-3 |3
12-4:-4 |
13-4:i=2 |34
14-4:i=-2|34
15-4:i=-3|3
16-4:i=-4|
17-5: |23456
18-6: |123456
19-7: |0123456
20-8: |
21-9: |
22-9:-99 |
23Ok:0
diff --git a/shell/ash_test/ash-vars/var_bash1b.tests b/shell/ash_test/ash-vars/var_bash1b.tests
new file mode 100755
index 000000000..efbdef35c
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash1b.tests
@@ -0,0 +1,24 @@
1set -- 0123456
2 echo "all |"$1
3 echo "4: |"${1:4}
4 echo "4:2 |"${1:4:2}
5 echo "4:-1 |"${1:4:-1}
6 echo "4:-2 |"${1:4:-2}
7 echo "4:-3 |"${1:4:-3}
8 echo "-4: |"${1: -4}
9 echo "-4:2 |"${1: -4:2}
10 echo "-4:-1 |"${1: -4:-1}
11 echo "-4:-2 |"${1: -4:-2}
12 echo "-4:-3 |"${1: -4:-3}
13 echo "-4:-4 |"${1: -4:-4}
14i=2; echo "-4:i=2 |"${1: -4:i}
15i=-2; echo "-4:i=-2|"${1: -4:i}
16i=-3; echo "-4:i=-3|"${1: -4:i}
17i=-4; echo "-4:i=-4|"${1: -4:i}
18 echo "-5: |"${1: -5}
19 echo "-6: |"${1: -6}
20 echo "-7: |"${1: -7}
21 echo "-8: |"${1: -8}
22 echo "-9: |"${1: -9}
23 echo "-9:-99 |"${1: -9:-99}
24echo Ok:$?
diff --git a/shell/ash_test/ash-vars/var_bash6.right b/shell/ash_test/ash-vars/var_bash6.right
new file mode 100644
index 000000000..63fc23df8
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash6.right
@@ -0,0 +1,5 @@
1Expected Actual
2a*z : a*z
3\z : \z
4a1z a2z: a1z a2z
5z : z
diff --git a/shell/ash_test/ash-vars/var_bash6.tests b/shell/ash_test/ash-vars/var_bash6.tests
new file mode 100755
index 000000000..cf2e4f020
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_bash6.tests
@@ -0,0 +1,9 @@
1# This testcase checks globbing correctness in ${v/a/b}
2
3>a1z; >a2z;
4 echo 'Expected' 'Actual'
5v='a bz'; echo 'a*z :' "${v/a*z/a*z}"
6v='a bz'; echo '\z :' "${v/a*z/\z}"
7v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z}
8v='a bz'; echo 'z :' ${v/a*z/\z}
9rm a1z a2z
diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all
index 354cc1fcf..983e6d184 100755
--- a/shell/ash_test/run-all
+++ b/shell/ash_test/run-all
@@ -1,6 +1,6 @@
1#!/bin/sh 1#!/bin/sh
2 2
3TOPDIR=$PWD 3TOPDIR=`pwd`
4 4
5test -x ash || { 5test -x ash || {
6 echo "No ./ash - creating a link to ../../busybox" 6 echo "No ./ash - creating a link to ../../busybox"
@@ -10,67 +10,73 @@ test -x printenv || gcc -O2 -o printenv printenv.c || exit $?
10test -x recho || gcc -O2 -o recho recho.c || exit $? 10test -x recho || gcc -O2 -o recho recho.c || exit $?
11test -x zecho || gcc -O2 -o zecho zecho.c || exit $? 11test -x zecho || gcc -O2 -o zecho zecho.c || exit $?
12 12
13PATH="$PWD:$PATH" # for ash and recho/zecho/printenv 13PATH="`pwd`:$PATH" # for ash and recho/zecho/printenv
14export PATH 14export PATH
15 15
16THIS_SH="$PWD/ash" 16THIS_SH="`pwd`/ash"
17export THIS_SH 17export THIS_SH
18 18
19do_test() 19do_test()
20{ 20{
21 test -d "$1" || return 0 21 test -d "$1" || return 0
22# echo do_test "$1" 22 d=${d%/}
23 # $1 but with / replaced by # so that it can be used as filename part 23# echo Running tests in directory "$1"
24 noslash=`echo "$1" | sed 's:/:#:g'` 24 # $1 but with / replaced by # so that it can be used as filename part
25 ( 25 noslash=`echo "$1" | sed 's:/:#:g'`
26 cd "$1" || { echo "cannot cd $1!"; exit 1; } 26 (
27 for x in run-*; do 27 cd "$1" || { echo "cannot cd $1!"; exit 1; }
28 test -f "$x" || continue 28 for x in run-*; do
29 case "$x" in 29 test -f "$x" || continue
30 "$0"|run-minimal|run-gprof) ;; 30 case "$x" in
31 *.orig|*~) ;; 31 "$0"|run-minimal|run-gprof) ;;
32 #*) echo $x ; sh $x ;; 32 *.orig|*~) ;;
33 *) 33 #*) echo $x ; sh $x ;;
34 echo -n "$1/$x: " 34 *)
35 sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \ 35 echo -n "$1/$x:"
36 { echo "ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "fail"; 36 sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \
37 ;; 37 { { echo " ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo " fail"; }
38 esac 38 ;;
39 done 39 esac
40 # Many bash run-XXX scripts just do this, 40 done
41 # no point in duplication it all over the place 41 # Many bash run-XXX scripts just do this,
42 for x in *.tests; do 42 # no point in duplication it all over the place
43 test -x "$x" || continue 43 for x in *.tests; do
44 name="${x%%.tests}" 44 test -x "$x" || continue
45 test -f "$name.right" || continue 45 name="${x%%.tests}"
46 echo -n "$1/$x: " 46 test -f "$name.right" || continue
47 { 47# echo Running test: "$x"
48 "$THIS_SH" "./$x" >"$name.xx" 2>&1 48 echo -n "$1/$x:"
49 diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \ 49 {
50 && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail" 50 "$THIS_SH" "./$x" >"$name.xx" 2>&1
51 } && echo "ok" || echo "fail" 51 diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \
52 done 52 && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail"
53 ) 53 } && echo " ok" || echo " fail"
54 done
55 )
54} 56}
55 57
56# main part of this script 58# Main part of this script
57# Usage: run-all [directories] 59# Usage: run-all [directories]
58 60
61ret=0
62
59if [ $# -lt 1 ]; then 63if [ $# -lt 1 ]; then
60 # All sub directories 64 # All sub directories
61 modules=`ls -d ash-*` 65 modules=`ls -d ash-*`
62 # If you want to test ash against hush and msh testsuites 66 # If you want to test ash against hush testsuite
63 # (have to copy hush_test and msh_test dirs to current dir first): 67 # (have to copy hush_test dir to current dir first):
64 #modules=`ls -d ash-* hush_test/hush-* msh_test/msh-*` 68 #modules=`ls -d ash-* hush_test/hush-*`
65 69
66 for module in $modules; do 70 for module in $modules; do
67 do_test $module 71 do_test $module || ret=1
68 done 72 done
69else 73else
70 while [ $# -ge 1 ]; do 74 while [ $# -ge 1 ]; do
71 if [ -d $1 ]; then 75 if [ -d $1 ]; then
72 do_test $1 76 do_test $1 || ret=1
73 fi 77 fi
74 shift 78 shift
75 done 79 done
76fi 80fi
81
82exit ${ret}
diff --git a/shell/hush.c b/shell/hush.c
index 125463a56..f6da826d3 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -41,14 +41,28 @@
41 * 41 *
42 * TODOs: 42 * TODOs:
43 * grep for "TODO" and fix (some of them are easy) 43 * grep for "TODO" and fix (some of them are easy)
44 * make complex ${var%...} constructs support optional
45 * make here documents optional
44 * special variables (done: PWD, PPID, RANDOM) 46 * special variables (done: PWD, PPID, RANDOM)
47 * follow IFS rules more precisely, including update semantics
45 * tilde expansion 48 * tilde expansion
46 * aliases 49 * aliases
47 * follow IFS rules more precisely, including update semantics
48 * builtins mandated by standards we don't support: 50 * builtins mandated by standards we don't support:
49 * [un]alias, command, fc, getopts, newgrp, readonly, times 51 * [un]alias, command, fc, getopts, times:
50 * make complex ${var%...} constructs support optional 52 * command -v CMD: print "/path/to/CMD"
51 * make here documents optional 53 * prints "CMD" for builtins
54 * prints "alias ALIAS='EXPANSION'" for aliases
55 * prints nothing and sets $? to 1 if not found
56 * command -V CMD: print "CMD is /path/CMD|a shell builtin|etc"
57 * command [-p] CMD: run CMD, even if a function CMD also exists
58 * (can use this to override standalone shell as well)
59 * -p: use default $PATH
60 * command BLTIN: disables special-ness (e.g. errors do not abort)
61 * getopts: getopt() for shells
62 * times: print getrusage(SELF/CHILDREN).ru_utime/ru_stime
63 * fc -l[nr] [BEG] [END]: list range of commands in history
64 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands
65 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP
52 * 66 *
53 * Bash compat TODO: 67 * Bash compat TODO:
54 * redirection of stdout+stderr: &> and >& 68 * redirection of stdout+stderr: &> and >&
@@ -64,8 +78,13 @@
64 * The EXPR is evaluated according to ARITHMETIC EVALUATION. 78 * The EXPR is evaluated according to ARITHMETIC EVALUATION.
65 * This is exactly equivalent to let "EXPR". 79 * This is exactly equivalent to let "EXPR".
66 * $[EXPR]: synonym for $((EXPR)) 80 * $[EXPR]: synonym for $((EXPR))
81 * indirect expansion: ${!VAR}
82 * substring op on @: ${@:n:m}
67 * 83 *
68 * Won't do: 84 * Won't do:
85 * Some builtins mandated by standards:
86 * newgrp [GRP]: not a builtin in bash but a suid binary
87 * which spawns a new shell with new group ID
69 * In bash, export builtin is special, its arguments are assignments 88 * In bash, export builtin is special, its arguments are assignments
70 * and therefore expansion of them should be "one-word" expansion: 89 * and therefore expansion of them should be "one-word" expansion:
71 * $ export i=`echo 'a b'` # export has one arg: "i=a b" 90 * $ export i=`echo 'a b'` # export has one arg: "i=a b"
@@ -219,6 +238,13 @@
219//config: help 238//config: help
220//config: export -n unexports variables. It is a bash extension. 239//config: export -n unexports variables. It is a bash extension.
221//config: 240//config:
241//config:config HUSH_READONLY
242//config: bool "readonly builtin"
243//config: default y
244//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
245//config: help
246//config: Enable support for read-only variables.
247//config:
222//config:config HUSH_KILL 248//config:config HUSH_KILL
223//config: bool "kill builtin (supports kill %jobspec)" 249//config: bool "kill builtin (supports kill %jobspec)"
224//config: default y 250//config: default y
@@ -268,17 +294,9 @@
268//config: bool "memleak builtin (debugging)" 294//config: bool "memleak builtin (debugging)"
269//config: default n 295//config: default n
270//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH 296//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
271//config:
272//config:config MSH
273//config: bool "msh (deprecated: aliased to hush)"
274//config: default n
275//config: select HUSH
276//config: help
277//config: msh is deprecated and will be removed, please migrate to hush.
278 297
279//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP)) 298//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
280// APPLET_ODDNAME:name main location suid_type help 299// APPLET_ODDNAME:name main location suid_type help
281//applet:IF_MSH( APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
282//applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 300//applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
283//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush)) 301//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
284 302
@@ -293,7 +311,7 @@
293 * therefore we don't show them either. 311 * therefore we don't show them either.
294 */ 312 */
295//usage:#define hush_trivial_usage 313//usage:#define hush_trivial_usage
296//usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" 314//usage: "[-enxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
297//usage:#define hush_full_usage "\n\n" 315//usage:#define hush_full_usage "\n\n"
298//usage: "Unix shell interpreter" 316//usage: "Unix shell interpreter"
299 317
@@ -331,9 +349,9 @@
331/* Separate defines document which part of code implements what */ 349/* Separate defines document which part of code implements what */
332#define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT 350#define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT
333#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT 351#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT
334#define BASH_TEST2 ENABLE_HUSH_BASH_COMPAT
335#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT 352#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
336#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT 353#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
354#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
337 355
338 356
339/* Build knobs */ 357/* Build knobs */
@@ -410,6 +428,7 @@
410#define debug_printf_expand(...) do {} while (0) 428#define debug_printf_expand(...) do {} while (0)
411#define debug_printf_varexp(...) do {} while (0) 429#define debug_printf_varexp(...) do {} while (0)
412#define debug_printf_glob(...) do {} while (0) 430#define debug_printf_glob(...) do {} while (0)
431#define debug_printf_redir(...) do {} while (0)
413#define debug_printf_list(...) do {} while (0) 432#define debug_printf_list(...) do {} while (0)
414#define debug_printf_subst(...) do {} while (0) 433#define debug_printf_subst(...) do {} while (0)
415#define debug_printf_clean(...) do {} while (0) 434#define debug_printf_clean(...) do {} while (0)
@@ -753,6 +772,7 @@ struct function {
753static const char o_opt_strings[] ALIGN1 = 772static const char o_opt_strings[] ALIGN1 =
754 "pipefail\0" 773 "pipefail\0"
755 "noexec\0" 774 "noexec\0"
775 "errexit\0"
756#if ENABLE_HUSH_MODE_X 776#if ENABLE_HUSH_MODE_X
757 "xtrace\0" 777 "xtrace\0"
758#endif 778#endif
@@ -760,6 +780,7 @@ static const char o_opt_strings[] ALIGN1 =
760enum { 780enum {
761 OPT_O_PIPEFAIL, 781 OPT_O_PIPEFAIL,
762 OPT_O_NOEXEC, 782 OPT_O_NOEXEC,
783 OPT_O_ERREXIT,
763#if ENABLE_HUSH_MODE_X 784#if ENABLE_HUSH_MODE_X
764 OPT_O_XTRACE, 785 OPT_O_XTRACE,
765#endif 786#endif
@@ -816,6 +837,25 @@ struct globals {
816#else 837#else
817# define G_saved_tty_pgrp 0 838# define G_saved_tty_pgrp 0
818#endif 839#endif
840 /* How deeply are we in context where "set -e" is ignored */
841 int errexit_depth;
842 /* "set -e" rules (do we follow them correctly?):
843 * Exit if pipe, list, or compound command exits with a non-zero status.
844 * Shell does not exit if failed command is part of condition in
845 * if/while, part of && or || list except the last command, any command
846 * in a pipe but the last, or if the command's return value is being
847 * inverted with !. If a compound command other than a subshell returns a
848 * non-zero status because a command failed while -e was being ignored, the
849 * shell does not exit. A trap on ERR, if set, is executed before the shell
850 * exits [ERR is a bashism].
851 *
852 * If a compound command or function executes in a context where -e is
853 * ignored, none of the commands executed within are affected by the -e
854 * setting. If a compound command or function sets -e while executing in a
855 * context where -e is ignored, that setting does not have any effect until
856 * the compound command or the command containing the function call completes.
857 */
858
819 char o_opt[NUM_OPT_O]; 859 char o_opt[NUM_OPT_O];
820#if ENABLE_HUSH_MODE_X 860#if ENABLE_HUSH_MODE_X
821# define G_x_mode (G.o_opt[OPT_O_XTRACE]) 861# define G_x_mode (G.o_opt[OPT_O_XTRACE])
@@ -839,6 +879,7 @@ struct globals {
839 smallint exiting; /* used to prevent EXIT trap recursion */ 879 smallint exiting; /* used to prevent EXIT trap recursion */
840 /* These four support $?, $#, and $1 */ 880 /* These four support $?, $#, and $1 */
841 smalluint last_exitcode; 881 smalluint last_exitcode;
882 smalluint last_bg_pid_exitcode;
842#if ENABLE_HUSH_SET 883#if ENABLE_HUSH_SET
843 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ 884 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
844 smalluint global_args_malloced; 885 smalluint global_args_malloced;
@@ -929,6 +970,9 @@ static int builtin_exit(char **argv) FAST_FUNC;
929#if ENABLE_HUSH_EXPORT 970#if ENABLE_HUSH_EXPORT
930static int builtin_export(char **argv) FAST_FUNC; 971static int builtin_export(char **argv) FAST_FUNC;
931#endif 972#endif
973#if ENABLE_HUSH_READONLY
974static int builtin_readonly(char **argv) FAST_FUNC;
975#endif
932#if ENABLE_HUSH_JOB 976#if ENABLE_HUSH_JOB
933static int builtin_fg_bg(char **argv) FAST_FUNC; 977static int builtin_fg_bg(char **argv) FAST_FUNC;
934static int builtin_jobs(char **argv) FAST_FUNC; 978static int builtin_jobs(char **argv) FAST_FUNC;
@@ -1047,6 +1091,9 @@ static const struct built_in_command bltins1[] = {
1047#if ENABLE_HUSH_READ 1091#if ENABLE_HUSH_READ
1048 BLTIN("read" , builtin_read , "Input into variable"), 1092 BLTIN("read" , builtin_read , "Input into variable"),
1049#endif 1093#endif
1094#if ENABLE_HUSH_READONLY
1095 BLTIN("readonly" , builtin_readonly, "Make variables read-only"),
1096#endif
1050#if ENABLE_HUSH_FUNCTIONS 1097#if ENABLE_HUSH_FUNCTIONS
1051 BLTIN("return" , builtin_return , "Return from function"), 1098 BLTIN("return" , builtin_return , "Return from function"),
1052#endif 1099#endif
@@ -1154,6 +1201,10 @@ static const struct built_in_command bltins2[] = {
1154# define DEBUG_GLOB 0 1201# define DEBUG_GLOB 0
1155#endif 1202#endif
1156 1203
1204#ifndef debug_printf_redir
1205# define debug_printf_redir(...) (indent(), fdprintf(2, __VA_ARGS__))
1206#endif
1207
1157#ifndef debug_printf_list 1208#ifndef debug_printf_list
1158# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__)) 1209# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
1159#endif 1210#endif
@@ -1389,12 +1440,30 @@ static void free_strings(char **strings)
1389 free(strings); 1440 free(strings);
1390} 1441}
1391 1442
1443static int fcntl_F_DUPFD(int fd, int avoid_fd)
1444{
1445 int newfd;
1446 repeat:
1447 newfd = fcntl(fd, F_DUPFD, avoid_fd + 1);
1448 if (newfd < 0) {
1449 if (errno == EBUSY)
1450 goto repeat;
1451 if (errno == EINTR)
1452 goto repeat;
1453 }
1454 return newfd;
1455}
1392 1456
1393static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC) 1457static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd)
1394{ 1458{
1395 /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */ 1459 int newfd;
1396 int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10); 1460 repeat:
1461 newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, avoid_fd + 1);
1397 if (newfd < 0) { 1462 if (newfd < 0) {
1463 if (errno == EBUSY)
1464 goto repeat;
1465 if (errno == EINTR)
1466 goto repeat;
1398 /* fd was not open? */ 1467 /* fd was not open? */
1399 if (errno == EBADF) 1468 if (errno == EBADF)
1400 return fd; 1469 return fd;
@@ -1432,13 +1501,14 @@ static void fclose_and_forget(FILE *fp)
1432 } 1501 }
1433 fclose(fp); 1502 fclose(fp);
1434} 1503}
1435static int save_FILEs_on_redirect(int fd) 1504static int save_FILEs_on_redirect(int fd, int avoid_fd)
1436{ 1505{
1437 struct FILE_list *fl = G.FILE_list; 1506 struct FILE_list *fl = G.FILE_list;
1438 while (fl) { 1507 while (fl) {
1439 if (fd == fl->fd) { 1508 if (fd == fl->fd) {
1440 /* We use it only on script files, they are all CLOEXEC */ 1509 /* We use it only on script files, they are all CLOEXEC */
1441 fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC); 1510 fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC, avoid_fd);
1511 debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd);
1442 return 1; 1512 return 1;
1443 } 1513 }
1444 fl = fl->next; 1514 fl = fl->next;
@@ -1451,13 +1521,14 @@ static void restore_redirected_FILEs(void)
1451 while (fl) { 1521 while (fl) {
1452 int should_be = fileno(fl->fp); 1522 int should_be = fileno(fl->fp);
1453 if (fl->fd != should_be) { 1523 if (fl->fd != should_be) {
1524 debug_printf_redir("restoring script fd from %d to %d\n", fl->fd, should_be);
1454 xmove_fd(fl->fd, should_be); 1525 xmove_fd(fl->fd, should_be);
1455 fl->fd = should_be; 1526 fl->fd = should_be;
1456 } 1527 }
1457 fl = fl->next; 1528 fl = fl->next;
1458 } 1529 }
1459} 1530}
1460#if ENABLE_FEATURE_SH_STANDALONE 1531#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
1461static void close_all_FILE_list(void) 1532static void close_all_FILE_list(void)
1462{ 1533{
1463 struct FILE_list *fl = G.FILE_list; 1534 struct FILE_list *fl = G.FILE_list;
@@ -1486,8 +1557,6 @@ typedef struct save_arg_t {
1486 1557
1487static void save_and_replace_G_args(save_arg_t *sv, char **argv) 1558static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1488{ 1559{
1489 int n;
1490
1491 sv->sv_argv0 = argv[0]; 1560 sv->sv_argv0 = argv[0];
1492 sv->sv_g_argv = G.global_argv; 1561 sv->sv_g_argv = G.global_argv;
1493 sv->sv_g_argc = G.global_argc; 1562 sv->sv_g_argc = G.global_argc;
@@ -1497,10 +1566,7 @@ static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1497 G.global_argv = argv; 1566 G.global_argv = argv;
1498 IF_HUSH_SET(G.global_args_malloced = 0;) 1567 IF_HUSH_SET(G.global_args_malloced = 0;)
1499 1568
1500 n = 1; 1569 G.global_argc = 1 + string_array_len(argv + 1);
1501 while (*++argv)
1502 n++;
1503 G.global_argc = n;
1504} 1570}
1505 1571
1506static void restore_G_args(save_arg_t *sv, char **argv) 1572static void restore_G_args(save_arg_t *sv, char **argv)
@@ -1991,32 +2057,19 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
1991 2057
1992/* str holds "NAME=VAL" and is expected to be malloced. 2058/* str holds "NAME=VAL" and is expected to be malloced.
1993 * We take ownership of it. 2059 * We take ownership of it.
1994 * flg_export:
1995 * 0: do not change export flag
1996 * (if creating new variable, flag will be 0)
1997 * 1: set export flag and putenv the variable
1998 * -1: clear export flag and unsetenv the variable
1999 * flg_read_only is set only when we handle -R var=val
2000 */ 2060 */
2001#if !BB_MMU && ENABLE_HUSH_LOCAL 2061#define SETFLAG_EXPORT (1 << 0)
2002/* all params are used */ 2062#define SETFLAG_UNEXPORT (1 << 1)
2003#elif BB_MMU && ENABLE_HUSH_LOCAL 2063#define SETFLAG_MAKE_RO (1 << 2)
2004#define set_local_var(str, flg_export, local_lvl, flg_read_only) \ 2064#define SETFLAG_LOCAL_SHIFT 3
2005 set_local_var(str, flg_export, local_lvl) 2065static int set_local_var(char *str, unsigned flags)
2006#elif BB_MMU && !ENABLE_HUSH_LOCAL
2007#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
2008 set_local_var(str, flg_export)
2009#elif !BB_MMU && !ENABLE_HUSH_LOCAL
2010#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
2011 set_local_var(str, flg_export, flg_read_only)
2012#endif
2013static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only)
2014{ 2066{
2015 struct variable **var_pp; 2067 struct variable **var_pp;
2016 struct variable *cur; 2068 struct variable *cur;
2017 char *free_me = NULL; 2069 char *free_me = NULL;
2018 char *eq_sign; 2070 char *eq_sign;
2019 int name_len; 2071 int name_len;
2072 IF_HUSH_LOCAL(unsigned local_lvl = (flags >> SETFLAG_LOCAL_SHIFT);)
2020 2073
2021 eq_sign = strchr(str, '='); 2074 eq_sign = strchr(str, '=');
2022 if (!eq_sign) { /* not expected to ever happen? */ 2075 if (!eq_sign) { /* not expected to ever happen? */
@@ -2034,14 +2087,13 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
2034 2087
2035 /* We found an existing var with this name */ 2088 /* We found an existing var with this name */
2036 if (cur->flg_read_only) { 2089 if (cur->flg_read_only) {
2037#if !BB_MMU 2090 bb_error_msg("%s: readonly variable", str);
2038 if (!flg_read_only)
2039#endif
2040 bb_error_msg("%s: readonly variable", str);
2041 free(str); 2091 free(str);
2092//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1,
2093//but export per se succeeds (does put the var in env). We don't mimic that.
2042 return -1; 2094 return -1;
2043 } 2095 }
2044 if (flg_export == -1) { // "&& cur->flg_export" ? 2096 if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ?
2045 debug_printf_env("%s: unsetenv '%s'\n", __func__, str); 2097 debug_printf_env("%s: unsetenv '%s'\n", __func__, str);
2046 *eq_sign = '\0'; 2098 *eq_sign = '\0';
2047 unsetenv(str); 2099 unsetenv(str);
@@ -2065,7 +2117,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
2065 * z=z 2117 * z=z
2066 */ 2118 */
2067 if (cur->flg_export) 2119 if (cur->flg_export)
2068 flg_export = 1; 2120 flags |= SETFLAG_EXPORT;
2069 break; 2121 break;
2070 } 2122 }
2071#endif 2123#endif
@@ -2096,24 +2148,24 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
2096 2148
2097 /* Not found - create new variable struct */ 2149 /* Not found - create new variable struct */
2098 cur = xzalloc(sizeof(*cur)); 2150 cur = xzalloc(sizeof(*cur));
2099#if ENABLE_HUSH_LOCAL 2151 IF_HUSH_LOCAL(cur->func_nest_level = local_lvl;)
2100 cur->func_nest_level = local_lvl;
2101#endif
2102 cur->next = *var_pp; 2152 cur->next = *var_pp;
2103 *var_pp = cur; 2153 *var_pp = cur;
2104 2154
2105 set_str_and_exp: 2155 set_str_and_exp:
2106 cur->varstr = str; 2156 cur->varstr = str;
2107#if !BB_MMU
2108 cur->flg_read_only = flg_read_only;
2109#endif
2110 exp: 2157 exp:
2111 if (flg_export == 1) 2158#if !BB_MMU || ENABLE_HUSH_READONLY
2159 if (flags & SETFLAG_MAKE_RO) {
2160 cur->flg_read_only = 1;
2161 }
2162#endif
2163 if (flags & SETFLAG_EXPORT)
2112 cur->flg_export = 1; 2164 cur->flg_export = 1;
2113 if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') 2165 if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
2114 cmdedit_update_prompt(); 2166 cmdedit_update_prompt();
2115 if (cur->flg_export) { 2167 if (cur->flg_export) {
2116 if (flg_export == -1) { 2168 if (flags & SETFLAG_UNEXPORT) {
2117 cur->flg_export = 0; 2169 cur->flg_export = 0;
2118 /* unsetenv was already done */ 2170 /* unsetenv was already done */
2119 } else { 2171 } else {
@@ -2130,10 +2182,9 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
2130} 2182}
2131 2183
2132/* Used at startup and after each cd */ 2184/* Used at startup and after each cd */
2133static void set_pwd_var(int exp) 2185static void set_pwd_var(unsigned flag)
2134{ 2186{
2135 set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), 2187 set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag);
2136 /*exp:*/ exp, /*lvl:*/ 0, /*ro:*/ 0);
2137} 2188}
2138 2189
2139static int unset_local_var_len(const char *name, int name_len) 2190static int unset_local_var_len(const char *name, int name_len)
@@ -2191,7 +2242,7 @@ static void unset_vars(char **strings)
2191static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) 2242static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2192{ 2243{
2193 char *var = xasprintf("%s=%s", name, val); 2244 char *var = xasprintf("%s=%s", name, val);
2194 set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 2245 set_local_var(var, /*flag:*/ 0);
2195} 2246}
2196#endif 2247#endif
2197 2248
@@ -2234,16 +2285,32 @@ static struct variable *set_vars_and_save_old(char **strings)
2234 if (eq) { 2285 if (eq) {
2235 var_pp = get_ptr_to_local_var(*s, eq - *s); 2286 var_pp = get_ptr_to_local_var(*s, eq - *s);
2236 if (var_pp) { 2287 if (var_pp) {
2237 /* Remove variable from global linked list */
2238 var_p = *var_pp; 2288 var_p = *var_pp;
2289 if (var_p->flg_read_only) {
2290 char **p;
2291 bb_error_msg("%s: readonly variable", *s);
2292 /*
2293 * "VAR=V BLTIN" unsets VARs after BLTIN completes.
2294 * If VAR is readonly, leaving it in the list
2295 * after asssignment error (msg above)
2296 * causes doubled error message later, on unset.
2297 */
2298 debug_printf_env("removing/freeing '%s' element\n", *s);
2299 free(*s);
2300 p = s;
2301 do { *p = p[1]; p++; } while (*p);
2302 goto next;
2303 }
2304 /* Remove variable from global linked list */
2239 debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr); 2305 debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr);
2240 *var_pp = var_p->next; 2306 *var_pp = var_p->next;
2241 /* Add it to returned list */ 2307 /* Add it to returned list */
2242 var_p->next = old; 2308 var_p->next = old;
2243 old = var_p; 2309 old = var_p;
2244 } 2310 }
2245 set_local_var(*s, /*exp:*/ 1, /*lvl:*/ 0, /*ro:*/ 0); 2311 set_local_var(*s, SETFLAG_EXPORT);
2246 } 2312 }
2313 next:
2247 s++; 2314 s++;
2248 } 2315 }
2249 return old; 2316 return old;
@@ -3339,12 +3406,49 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
3339 debug_printf_parse("done_pipe entered, followup %d\n", type); 3406 debug_printf_parse("done_pipe entered, followup %d\n", type);
3340 /* Close previous command */ 3407 /* Close previous command */
3341 not_null = done_command(ctx); 3408 not_null = done_command(ctx);
3342 ctx->pipe->followup = type;
3343#if HAS_KEYWORDS 3409#if HAS_KEYWORDS
3344 ctx->pipe->pi_inverted = ctx->ctx_inverted; 3410 ctx->pipe->pi_inverted = ctx->ctx_inverted;
3345 ctx->ctx_inverted = 0; 3411 ctx->ctx_inverted = 0;
3346 ctx->pipe->res_word = ctx->ctx_res_w; 3412 ctx->pipe->res_word = ctx->ctx_res_w;
3347#endif 3413#endif
3414 if (type == PIPE_BG && ctx->list_head != ctx->pipe) {
3415 /* Necessary since && and || have precedence over &:
3416 * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2,
3417 * in a backgrounded subshell.
3418 */
3419 struct pipe *pi;
3420 struct command *command;
3421
3422 /* Is this actually this construct, all pipes end with && or ||? */
3423 pi = ctx->list_head;
3424 while (pi != ctx->pipe) {
3425 if (pi->followup != PIPE_AND && pi->followup != PIPE_OR)
3426 goto no_conv;
3427 pi = pi->next;
3428 }
3429
3430 debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n");
3431 pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */
3432 pi = xzalloc(sizeof(*pi));
3433 pi->followup = PIPE_BG;
3434 pi->num_cmds = 1;
3435 pi->cmds = xzalloc(sizeof(pi->cmds[0]));
3436 command = &pi->cmds[0];
3437 if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */
3438 command->cmd_type = CMD_NORMAL;
3439 command->group = ctx->list_head;
3440#if !BB_MMU
3441 command->group_as_string = xstrndup(
3442 ctx->as_string.data,
3443 ctx->as_string.length - 1 /* do not copy last char, "&" */
3444 );
3445#endif
3446 /* Replace all pipes in ctx with one newly created */
3447 ctx->list_head = ctx->pipe = pi;
3448 } else {
3449 no_conv:
3450 ctx->pipe->followup = type;
3451 }
3348 3452
3349 /* Without this check, even just <enter> on command line generates 3453 /* Without this check, even just <enter> on command line generates
3350 * tree of three NOPs (!). Which is harmless but annoying. 3454 * tree of three NOPs (!). Which is harmless but annoying.
@@ -3514,9 +3618,8 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
3514 if (r->flag & FLAG_START) { 3618 if (r->flag & FLAG_START) {
3515 struct parse_context *old; 3619 struct parse_context *old;
3516 3620
3517 old = xmalloc(sizeof(*old)); 3621 old = xmemdup(ctx, sizeof(*ctx));
3518 debug_printf_parse("push stack %p\n", old); 3622 debug_printf_parse("push stack %p\n", old);
3519 *old = *ctx; /* physical copy */
3520 initialize_context(ctx); 3623 initialize_context(ctx);
3521 ctx->stack = old; 3624 ctx->stack = old;
3522 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { 3625 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
@@ -4787,7 +4890,9 @@ static struct pipe *parse_stream(char **pstring,
4787 * Really, ask yourself, why 4890 * Really, ask yourself, why
4788 * "cmd && <newline>" doesn't start 4891 * "cmd && <newline>" doesn't start
4789 * cmd but waits for more input? 4892 * cmd but waits for more input?
4790 * No reason...) 4893 * The only reason is that it might be
4894 * a "cmd1 && <nl> cmd2 &" construct,
4895 * cmd1 may need to run in BG).
4791 */ 4896 */
4792 struct pipe *pi = ctx.list_head; 4897 struct pipe *pi = ctx.list_head;
4793 if (pi->num_cmds != 0 /* check #1 */ 4898 if (pi->num_cmds != 0 /* check #1 */
@@ -5146,7 +5251,7 @@ static struct pipe *parse_stream(char **pstring,
5146 * and it will match } earlier (not here). */ 5251 * and it will match } earlier (not here). */
5147 syntax_error_unexpected_ch(ch); 5252 syntax_error_unexpected_ch(ch);
5148 G.last_exitcode = 2; 5253 G.last_exitcode = 2;
5149 goto parse_error1; 5254 goto parse_error2;
5150 default: 5255 default:
5151 if (HUSH_DEBUG) 5256 if (HUSH_DEBUG)
5152 bb_error_msg_and_die("BUG: unexpected %c\n", ch); 5257 bb_error_msg_and_die("BUG: unexpected %c\n", ch);
@@ -5155,7 +5260,7 @@ static struct pipe *parse_stream(char **pstring,
5155 5260
5156 parse_error: 5261 parse_error:
5157 G.last_exitcode = 1; 5262 G.last_exitcode = 1;
5158 parse_error1: 5263 parse_error2:
5159 { 5264 {
5160 struct parse_context *pctx; 5265 struct parse_context *pctx;
5161 IF_HAS_KEYWORDS(struct parse_context *p2;) 5266 IF_HAS_KEYWORDS(struct parse_context *p2;)
@@ -5202,7 +5307,7 @@ static struct pipe *parse_stream(char **pstring,
5202/*** Execution routines ***/ 5307/*** Execution routines ***/
5203 5308
5204/* Expansion can recurse, need forward decls: */ 5309/* Expansion can recurse, need forward decls: */
5205#if !BASH_PATTERN_SUBST 5310#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
5206/* only ${var/pattern/repl} (its pattern part) needs additional mode */ 5311/* only ${var/pattern/repl} (its pattern part) needs additional mode */
5207#define expand_string_to_string(str, do_unbackslash) \ 5312#define expand_string_to_string(str, do_unbackslash) \
5208 expand_string_to_string(str) 5313 expand_string_to_string(str)
@@ -5330,6 +5435,9 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha
5330#endif 5435#endif
5331static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) 5436static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash)
5332{ 5437{
5438#if !BASH_PATTERN_SUBST
5439 const int do_unbackslash = 1;
5440#endif
5333 char *exp_str; 5441 char *exp_str;
5334 struct in_str input; 5442 struct in_str input;
5335 o_string dest = NULL_O_STRING; 5443 o_string dest = NULL_O_STRING;
@@ -5628,27 +5736,34 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5628 if (errmsg) 5736 if (errmsg)
5629 goto arith_err; 5737 goto arith_err;
5630 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); 5738 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
5631 if (len >= 0) { /* bash compat: len < 0 is illegal */ 5739 if (beg < 0) {
5632 if (beg < 0) /* bash compat */ 5740 /* negative beg counts from the end */
5633 beg = 0; 5741 beg = (arith_t)strlen(val) + beg;
5634 debug_printf_varexp("from val:'%s'\n", val); 5742 if (beg < 0) /* ${v: -999999} is "" */
5635 if (len == 0 || !val || beg >= strlen(val)) { 5743 beg = len = 0;
5744 }
5745 debug_printf_varexp("from val:'%s'\n", val);
5746 if (len < 0) {
5747 /* in bash, len=-n means strlen()-n */
5748 len = (arith_t)strlen(val) - beg + len;
5749 if (len < 0) /* bash compat */
5750 die_if_script("%s: substring expression < 0", var);
5751 }
5752 if (len <= 0 || !val || beg >= strlen(val)) {
5636 arith_err: 5753 arith_err:
5637 val = NULL;
5638 } else {
5639 /* Paranoia. What if user entered 9999999999999
5640 * which fits in arith_t but not int? */
5641 if (len >= INT_MAX)
5642 len = INT_MAX;
5643 val = to_be_freed = xstrndup(val + beg, len);
5644 }
5645 debug_printf_varexp("val:'%s'\n", val);
5646 } else
5647#endif /* HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH */
5648 {
5649 die_if_script("malformed ${%s:...}", var);
5650 val = NULL; 5754 val = NULL;
5755 } else {
5756 /* Paranoia. What if user entered 9999999999999
5757 * which fits in arith_t but not int? */
5758 if (len >= INT_MAX)
5759 len = INT_MAX;
5760 val = to_be_freed = xstrndup(val + beg, len);
5651 } 5761 }
5762 debug_printf_varexp("val:'%s'\n", val);
5763#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
5764 die_if_script("malformed ${%s:...}", var);
5765 val = NULL;
5766#endif
5652 } else { /* one of "-=+?" */ 5767 } else { /* one of "-=+?" */
5653 /* Standard-mandated substitution ops: 5768 /* Standard-mandated substitution ops:
5654 * ${var?word} - indicate error if unset 5769 * ${var?word} - indicate error if unset
@@ -5700,7 +5815,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
5700 val = NULL; 5815 val = NULL;
5701 } else { 5816 } else {
5702 char *new_var = xasprintf("%s=%s", var, val); 5817 char *new_var = xasprintf("%s=%s", var, val);
5703 set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 5818 set_local_var(new_var, /*flag:*/ 0);
5704 } 5819 }
5705 } 5820 }
5706 } 5821 }
@@ -5949,7 +6064,7 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
5949 */ 6064 */
5950static char *expand_string_to_string(const char *str, int do_unbackslash) 6065static char *expand_string_to_string(const char *str, int do_unbackslash)
5951{ 6066{
5952#if !BASH_PATTERN_SUBST 6067#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
5953 const int do_unbackslash = 1; 6068 const int do_unbackslash = 1;
5954#endif 6069#endif
5955 char *argv[2], **list; 6070 char *argv[2], **list;
@@ -5982,7 +6097,7 @@ static char *expand_string_to_string(const char *str, int do_unbackslash)
5982 return (char*)list; 6097 return (char*)list;
5983} 6098}
5984 6099
5985/* Used for "eval" builtin */ 6100/* Used for "eval" builtin and case string */
5986static char* expand_strvec_to_string(char **argv) 6101static char* expand_strvec_to_string(char **argv)
5987{ 6102{
5988 char **list; 6103 char **list;
@@ -6524,77 +6639,108 @@ static void setup_heredoc(struct redir_struct *redir)
6524 wait(NULL); /* wait till child has died */ 6639 wait(NULL); /* wait till child has died */
6525} 6640}
6526 6641
6527/* fd: redirect wants this fd to be used (e.g. 3>file). 6642struct squirrel {
6528 * Move all conflicting internally used fds, 6643 int orig_fd;
6529 * and remember them so that we can restore them later. 6644 int moved_to;
6530 */ 6645 /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */
6531static int save_fds_on_redirect(int fd, int squirrel[3]) 6646 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
6647};
6648
6649static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
6532{ 6650{
6533 if (squirrel) { 6651 int i = 0;
6534 /* Handle redirects of fds 0,1,2 */
6535 6652
6536 /* If we collide with an already moved stdio fd... */ 6653 if (sq) while (sq[i].orig_fd >= 0) {
6537 if (fd == squirrel[0]) { 6654 /* If we collide with an already moved fd... */
6538 squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD); 6655 if (fd == sq[i].moved_to) {
6539 return 1; 6656 sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd);
6540 } 6657 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to);
6541 if (fd == squirrel[1]) { 6658 if (sq[i].moved_to < 0) /* what? */
6542 squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD);
6543 return 1;
6544 }
6545 if (fd == squirrel[2]) {
6546 squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD);
6547 return 1;
6548 }
6549 /* If we are about to redirect stdio fd, and did not yet move it... */
6550 if (fd <= 2 && squirrel[fd] < 0) {
6551 /* We avoid taking stdio fds */
6552 squirrel[fd] = fcntl(fd, F_DUPFD, 10);
6553 if (squirrel[fd] < 0 && errno != EBADF)
6554 xfunc_die(); 6659 xfunc_die();
6555 return 0; /* "we did not close fd" */ 6660 return sq;
6661 }
6662 if (fd == sq[i].orig_fd) {
6663 /* Example: echo Hello >/dev/null 1>&2 */
6664 debug_printf_redir("redirect_fd %d: already moved\n", fd);
6665 return sq;
6556 } 6666 }
6667 i++;
6557 } 6668 }
6558 6669
6670 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
6671 sq[i].orig_fd = fd;
6672 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
6673 sq[i].moved_to = fcntl_F_DUPFD(fd, avoid_fd);
6674 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, sq[i].moved_to);
6675 if (sq[i].moved_to < 0 && errno != EBADF)
6676 xfunc_die();
6677 sq[i+1].orig_fd = -1; /* end marker */
6678 return sq;
6679}
6680
6681/* fd: redirect wants this fd to be used (e.g. 3>file).
6682 * Move all conflicting internally used fds,
6683 * and remember them so that we can restore them later.
6684 */
6685static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
6686{
6687 if (avoid_fd < 9) /* the important case here is that it can be -1 */
6688 avoid_fd = 9;
6689
6559#if ENABLE_HUSH_INTERACTIVE 6690#if ENABLE_HUSH_INTERACTIVE
6560 if (fd != 0 && fd == G.interactive_fd) { 6691 if (fd != 0 && fd == G.interactive_fd) {
6561 G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC); 6692 G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC, avoid_fd);
6562 return 1; 6693 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd);
6694 return 1; /* "we closed fd" */
6563 } 6695 }
6564#endif 6696#endif
6565
6566 /* Are we called from setup_redirects(squirrel==NULL)? Two cases: 6697 /* Are we called from setup_redirects(squirrel==NULL)? Two cases:
6567 * (1) Redirect in a forked child. No need to save FILEs' fds, 6698 * (1) Redirect in a forked child. No need to save FILEs' fds,
6568 * we aren't going to use them anymore, ok to trash. 6699 * we aren't going to use them anymore, ok to trash.
6569 * (2) "exec 3>FILE". Bummer. We can save FILEs' fds, 6700 * (2) "exec 3>FILE". Bummer. We can save script FILEs' fds,
6570 * but how are we doing to use them? 6701 * but how are we doing to restore them?
6571 * "fileno(fd) = new_fd" can't be done. 6702 * "fileno(fd) = new_fd" can't be done.
6572 */ 6703 */
6573 if (!squirrel) 6704 if (!sqp)
6574 return 0; 6705 return 0;
6575 6706
6576 return save_FILEs_on_redirect(fd); 6707 /* If this one of script's fds? */
6708 if (save_FILEs_on_redirect(fd, avoid_fd))
6709 return 1; /* yes. "we closed fd" */
6710
6711 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
6712 *sqp = add_squirrel(*sqp, fd, avoid_fd);
6713 return 0; /* "we did not close fd" */
6577} 6714}
6578 6715
6579static void restore_redirects(int squirrel[3]) 6716static void restore_redirects(struct squirrel *sq)
6580{ 6717{
6581 int i, fd; 6718
6582 for (i = 0; i <= 2; i++) { 6719 if (sq) {
6583 fd = squirrel[i]; 6720 int i = 0;
6584 if (fd != -1) { 6721 while (sq[i].orig_fd >= 0) {
6585 /* We simply die on error */ 6722 if (sq[i].moved_to >= 0) {
6586 xmove_fd(fd, i); 6723 /* We simply die on error */
6724 debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd);
6725 xmove_fd(sq[i].moved_to, sq[i].orig_fd);
6726 } else {
6727 /* cmd1 9>FILE; cmd2_should_see_fd9_closed */
6728 debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd);
6729 close(sq[i].orig_fd);
6730 }
6731 i++;
6587 } 6732 }
6733 free(sq);
6588 } 6734 }
6589 6735
6590 /* Moved G.interactive_fd stays on new fd, not doing anything for it */ 6736 /* If moved, G.interactive_fd stays on new fd, not restoring it */
6591 6737
6592 restore_redirected_FILEs(); 6738 restore_redirected_FILEs();
6593} 6739}
6594 6740
6595/* squirrel != NULL means we squirrel away copies of stdin, stdout, 6741/* squirrel != NULL means we squirrel away copies of stdin, stdout,
6596 * and stderr if they are redirected. */ 6742 * and stderr if they are redirected. */
6597static int setup_redirects(struct command *prog, int squirrel[]) 6743static int setup_redirects(struct command *prog, struct squirrel **sqp)
6598{ 6744{
6599 int openfd, mode; 6745 int openfd, mode;
6600 struct redir_struct *redir; 6746 struct redir_struct *redir;
@@ -6602,7 +6748,7 @@ static int setup_redirects(struct command *prog, int squirrel[])
6602 for (redir = prog->redirects; redir; redir = redir->next) { 6748 for (redir = prog->redirects; redir; redir = redir->next) {
6603 if (redir->rd_type == REDIRECT_HEREDOC2) { 6749 if (redir->rd_type == REDIRECT_HEREDOC2) {
6604 /* "rd_fd<<HERE" case */ 6750 /* "rd_fd<<HERE" case */
6605 save_fds_on_redirect(redir->rd_fd, squirrel); 6751 save_fds_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp);
6606 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ 6752 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
6607 * of the heredoc */ 6753 * of the heredoc */
6608 debug_printf_parse("set heredoc '%s'\n", 6754 debug_printf_parse("set heredoc '%s'\n",
@@ -6641,7 +6787,7 @@ static int setup_redirects(struct command *prog, int squirrel[])
6641 } 6787 }
6642 6788
6643 if (openfd != redir->rd_fd) { 6789 if (openfd != redir->rd_fd) {
6644 int closed = save_fds_on_redirect(redir->rd_fd, squirrel); 6790 int closed = save_fds_on_redirect(redir->rd_fd, /*avoid:*/ openfd, sqp);
6645 if (openfd == REDIRFD_CLOSE) { 6791 if (openfd == REDIRFD_CLOSE) {
6646 /* "rd_fd >&-" means "close me" */ 6792 /* "rd_fd >&-" means "close me" */
6647 if (!closed) { 6793 if (!closed) {
@@ -6817,13 +6963,11 @@ static void exec_function(char ***to_free,
6817 char **argv) 6963 char **argv)
6818{ 6964{
6819# if BB_MMU 6965# if BB_MMU
6820 int n = 1; 6966 int n;
6821 6967
6822 argv[0] = G.global_argv[0]; 6968 argv[0] = G.global_argv[0];
6823 G.global_argv = argv; 6969 G.global_argv = argv;
6824 while (*++argv) 6970 G.global_argc = n = 1 + string_array_len(argv + 1);
6825 n++;
6826 G.global_argc = n;
6827 /* On MMU, funcp->body is always non-NULL */ 6971 /* On MMU, funcp->body is always non-NULL */
6828 n = run_list(funcp->body); 6972 n = run_list(funcp->body);
6829 fflush_all(); 6973 fflush_all();
@@ -7071,7 +7215,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7071 /* Do not leak open fds from opened script files etc */ 7215 /* Do not leak open fds from opened script files etc */
7072 close_all_FILE_list(); 7216 close_all_FILE_list();
7073 debug_printf_exec("running applet '%s'\n", argv[0]); 7217 debug_printf_exec("running applet '%s'\n", argv[0]);
7074 run_applet_no_and_exit(a, argv); 7218 run_applet_no_and_exit(a, argv[0], argv);
7075 } 7219 }
7076# endif 7220# endif
7077 /* Re-exec ourselves */ 7221 /* Re-exec ourselves */
@@ -7168,24 +7312,54 @@ static const char *get_cmdtext(struct pipe *pi)
7168 return pi->cmdtext; 7312 return pi->cmdtext;
7169} 7313}
7170 7314
7171static void insert_bg_job(struct pipe *pi) 7315static void remove_job_from_table(struct pipe *pi)
7316{
7317 struct pipe *prev_pipe;
7318
7319 if (pi == G.job_list) {
7320 G.job_list = pi->next;
7321 } else {
7322 prev_pipe = G.job_list;
7323 while (prev_pipe->next != pi)
7324 prev_pipe = prev_pipe->next;
7325 prev_pipe->next = pi->next;
7326 }
7327 G.last_jobid = 0;
7328 if (G.job_list)
7329 G.last_jobid = G.job_list->jobid;
7330}
7331
7332static void delete_finished_job(struct pipe *pi)
7333{
7334 remove_job_from_table(pi);
7335 free_pipe(pi);
7336}
7337
7338static void clean_up_last_dead_job(void)
7339{
7340 if (G.job_list && !G.job_list->alive_cmds)
7341 delete_finished_job(G.job_list);
7342}
7343
7344static void insert_job_into_table(struct pipe *pi)
7172{ 7345{
7173 struct pipe *job, **jobp; 7346 struct pipe *job, **jobp;
7174 int i; 7347 int i;
7175 7348
7176 /* Linear search for the ID of the job to use */ 7349 clean_up_last_dead_job();
7177 pi->jobid = 1;
7178 for (job = G.job_list; job; job = job->next)
7179 if (job->jobid >= pi->jobid)
7180 pi->jobid = job->jobid + 1;
7181 7350
7182 /* Add job to the list of running jobs */ 7351 /* Find the end of the list, and find next job ID to use */
7352 i = 0;
7183 jobp = &G.job_list; 7353 jobp = &G.job_list;
7184 while ((job = *jobp) != NULL) 7354 while ((job = *jobp) != NULL) {
7355 if (job->jobid > i)
7356 i = job->jobid;
7185 jobp = &job->next; 7357 jobp = &job->next;
7186 job = *jobp = xmalloc(sizeof(*job)); 7358 }
7359 pi->jobid = i + 1;
7187 7360
7188 *job = *pi; /* physical copy */ 7361 /* Create a new job struct at the end */
7362 job = *jobp = xmemdup(pi, sizeof(*pi));
7189 job->next = NULL; 7363 job->next = NULL;
7190 job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); 7364 job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
7191 /* Cannot copy entire pi->cmds[] vector! This causes double frees */ 7365 /* Cannot copy entire pi->cmds[] vector! This causes double frees */
@@ -7199,31 +7373,6 @@ static void insert_bg_job(struct pipe *pi)
7199 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext); 7373 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
7200 G.last_jobid = job->jobid; 7374 G.last_jobid = job->jobid;
7201} 7375}
7202
7203static void remove_bg_job(struct pipe *pi)
7204{
7205 struct pipe *prev_pipe;
7206
7207 if (pi == G.job_list) {
7208 G.job_list = pi->next;
7209 } else {
7210 prev_pipe = G.job_list;
7211 while (prev_pipe->next != pi)
7212 prev_pipe = prev_pipe->next;
7213 prev_pipe->next = pi->next;
7214 }
7215 if (G.job_list)
7216 G.last_jobid = G.job_list->jobid;
7217 else
7218 G.last_jobid = 0;
7219}
7220
7221/* Remove a backgrounded job */
7222static void delete_finished_bg_job(struct pipe *pi)
7223{
7224 remove_bg_job(pi);
7225 free_pipe(pi);
7226}
7227#endif /* JOB */ 7376#endif /* JOB */
7228 7377
7229static int job_exited_or_stopped(struct pipe *pi) 7378static int job_exited_or_stopped(struct pipe *pi)
@@ -7310,7 +7459,7 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
7310 if (G_interactive_fd) { 7459 if (G_interactive_fd) {
7311#if ENABLE_HUSH_JOB 7460#if ENABLE_HUSH_JOB
7312 if (fg_pipe->alive_cmds != 0) 7461 if (fg_pipe->alive_cmds != 0)
7313 insert_bg_job(fg_pipe); 7462 insert_job_into_table(fg_pipe);
7314#endif 7463#endif
7315 return rcode; 7464 return rcode;
7316 } 7465 }
@@ -7339,16 +7488,31 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
7339 found_pi_and_prognum: 7488 found_pi_and_prognum:
7340 if (dead) { 7489 if (dead) {
7341 /* child exited */ 7490 /* child exited */
7342 pi->cmds[i].pid = 0; 7491 int rcode = WEXITSTATUS(status);
7343 pi->cmds[i].cmd_exitcode = WEXITSTATUS(status);
7344 if (WIFSIGNALED(status)) 7492 if (WIFSIGNALED(status))
7345 pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status); 7493 rcode = 128 + WTERMSIG(status);
7494 pi->cmds[i].cmd_exitcode = rcode;
7495 if (G.last_bg_pid == pi->cmds[i].pid)
7496 G.last_bg_pid_exitcode = rcode;
7497 pi->cmds[i].pid = 0;
7346 pi->alive_cmds--; 7498 pi->alive_cmds--;
7347 if (!pi->alive_cmds) { 7499 if (!pi->alive_cmds) {
7348 if (G_interactive_fd) 7500 if (G_interactive_fd) {
7349 printf(JOB_STATUS_FORMAT, pi->jobid, 7501 printf(JOB_STATUS_FORMAT, pi->jobid,
7350 "Done", pi->cmdtext); 7502 "Done", pi->cmdtext);
7351 delete_finished_bg_job(pi); 7503 delete_finished_job(pi);
7504 } else {
7505/*
7506 * bash deletes finished jobs from job table only in interactive mode,
7507 * after "jobs" cmd, or if pid of a new process matches one of the old ones
7508 * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source).
7509 * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash.
7510 * We only retain one "dead" job, if it's the single job on the list.
7511 * This covers most of real-world scenarios where this is useful.
7512 */
7513 if (pi != G.job_list)
7514 delete_finished_job(pi);
7515 }
7352 } 7516 }
7353 } else { 7517 } else {
7354 /* child stopped */ 7518 /* child stopped */
@@ -7505,14 +7669,14 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
7505static int redirect_and_varexp_helper(char ***new_env_p, 7669static int redirect_and_varexp_helper(char ***new_env_p,
7506 struct variable **old_vars_p, 7670 struct variable **old_vars_p,
7507 struct command *command, 7671 struct command *command,
7508 int squirrel[3], 7672 struct squirrel **sqp,
7509 char **argv_expanded) 7673 char **argv_expanded)
7510{ 7674{
7511 /* setup_redirects acts on file descriptors, not FILEs. 7675 /* setup_redirects acts on file descriptors, not FILEs.
7512 * This is perfect for work that comes after exec(). 7676 * This is perfect for work that comes after exec().
7513 * Is it really safe for inline use? Experimentally, 7677 * Is it really safe for inline use? Experimentally,
7514 * things seem to work. */ 7678 * things seem to work. */
7515 int rcode = setup_redirects(command, squirrel); 7679 int rcode = setup_redirects(command, sqp);
7516 if (rcode == 0) { 7680 if (rcode == 0) {
7517 char **new_env = expand_assignments(command->argv, command->assignment_cnt); 7681 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
7518 *new_env_p = new_env; 7682 *new_env_p = new_env;
@@ -7532,8 +7696,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7532 struct command *command; 7696 struct command *command;
7533 char **argv_expanded; 7697 char **argv_expanded;
7534 char **argv; 7698 char **argv;
7535 /* it is not always needed, but we aim to smaller code */ 7699 struct squirrel *squirrel = NULL;
7536 int squirrel[] = { -1, -1, -1 };
7537 int rcode; 7700 int rcode;
7538 7701
7539 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); 7702 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
@@ -7590,7 +7753,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7590 /* { list } */ 7753 /* { list } */
7591 debug_printf("non-subshell group\n"); 7754 debug_printf("non-subshell group\n");
7592 rcode = 1; /* exitcode if redir failed */ 7755 rcode = 1; /* exitcode if redir failed */
7593 if (setup_redirects(command, squirrel) == 0) { 7756 if (setup_redirects(command, &squirrel) == 0) {
7594 debug_printf_exec(": run_list\n"); 7757 debug_printf_exec(": run_list\n");
7595 rcode = run_list(command->group) & 0xff; 7758 rcode = run_list(command->group) & 0xff;
7596 } 7759 }
@@ -7617,15 +7780,15 @@ static NOINLINE int run_pipe(struct pipe *pi)
7617 /* Ensure redirects take effect (that is, create files). 7780 /* Ensure redirects take effect (that is, create files).
7618 * Try "a=t >file" */ 7781 * Try "a=t >file" */
7619#if 0 /* A few cases in testsuite fail with this code. FIXME */ 7782#if 0 /* A few cases in testsuite fail with this code. FIXME */
7620 rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, squirrel, /*argv_expanded:*/ NULL); 7783 rcode = redirect_and_varexp_helper(&new_env, /*old_vars:*/ NULL, command, &squirrel, /*argv_expanded:*/ NULL);
7621 /* Set shell variables */ 7784 /* Set shell variables */
7622 if (new_env) { 7785 if (new_env) {
7623 argv = new_env; 7786 argv = new_env;
7624 while (*argv) { 7787 while (*argv) {
7625 set_local_var(*argv, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 7788 if (set_local_var(*argv, /*flag:*/ 0)) {
7626 /* Do we need to flag set_local_var() errors? 7789 /* assignment to readonly var / putenv error? */
7627 * "assignment to readonly var" and "putenv error" 7790 rcode = 1;
7628 */ 7791 }
7629 argv++; 7792 argv++;
7630 } 7793 }
7631 } 7794 }
@@ -7639,7 +7802,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7639 7802
7640#else /* Older, bigger, but more correct code */ 7803#else /* Older, bigger, but more correct code */
7641 7804
7642 rcode = setup_redirects(command, squirrel); 7805 rcode = setup_redirects(command, &squirrel);
7643 restore_redirects(squirrel); 7806 restore_redirects(squirrel);
7644 /* Set shell variables */ 7807 /* Set shell variables */
7645 if (G_x_mode) 7808 if (G_x_mode)
@@ -7650,10 +7813,10 @@ static NOINLINE int run_pipe(struct pipe *pi)
7650 fprintf(stderr, " %s", p); 7813 fprintf(stderr, " %s", p);
7651 debug_printf_exec("set shell var:'%s'->'%s'\n", 7814 debug_printf_exec("set shell var:'%s'->'%s'\n",
7652 *argv, p); 7815 *argv, p);
7653 set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 7816 if (set_local_var(p, /*flag:*/ 0)) {
7654 /* Do we need to flag set_local_var() errors? 7817 /* assignment to readonly var / putenv error? */
7655 * "assignment to readonly var" and "putenv error" 7818 rcode = 1;
7656 */ 7819 }
7657 argv++; 7820 argv++;
7658 } 7821 }
7659 if (G_x_mode) 7822 if (G_x_mode)
@@ -7702,7 +7865,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7702 goto clean_up_and_ret1; 7865 goto clean_up_and_ret1;
7703 } 7866 }
7704 } 7867 }
7705 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); 7868 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded);
7706 if (rcode == 0) { 7869 if (rcode == 0) {
7707 if (!funcp) { 7870 if (!funcp) {
7708 debug_printf_exec(": builtin '%s' '%s'...\n", 7871 debug_printf_exec(": builtin '%s' '%s'...\n",
@@ -7743,7 +7906,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
7743 if (ENABLE_FEATURE_SH_NOFORK) { 7906 if (ENABLE_FEATURE_SH_NOFORK) {
7744 int n = find_applet_by_name(argv_expanded[0]); 7907 int n = find_applet_by_name(argv_expanded[0]);
7745 if (n >= 0 && APPLET_IS_NOFORK(n)) { 7908 if (n >= 0 && APPLET_IS_NOFORK(n)) {
7746 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, squirrel, argv_expanded); 7909 rcode = redirect_and_varexp_helper(&new_env, &old_vars, command, &squirrel, argv_expanded);
7747 if (rcode == 0) { 7910 if (rcode == 0) {
7748 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 7911 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
7749 argv_expanded[0], argv_expanded[1]); 7912 argv_expanded[0], argv_expanded[1]);
@@ -7968,6 +8131,7 @@ static int run_list(struct pipe *pi)
7968 /* Go through list of pipes, (maybe) executing them. */ 8131 /* Go through list of pipes, (maybe) executing them. */
7969 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { 8132 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
7970 int r; 8133 int r;
8134 int sv_errexit_depth;
7971 8135
7972 if (G.flag_SIGINT) 8136 if (G.flag_SIGINT)
7973 break; 8137 break;
@@ -7977,6 +8141,13 @@ static int run_list(struct pipe *pi)
7977 IF_HAS_KEYWORDS(rword = pi->res_word;) 8141 IF_HAS_KEYWORDS(rword = pi->res_word;)
7978 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n", 8142 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
7979 rword, cond_code, last_rword); 8143 rword, cond_code, last_rword);
8144
8145 sv_errexit_depth = G.errexit_depth;
8146 if (IF_HAS_KEYWORDS(rword == RES_IF || rword == RES_ELIF ||)
8147 pi->followup != PIPE_SEQ
8148 ) {
8149 G.errexit_depth++;
8150 }
7980#if ENABLE_HUSH_LOOPS 8151#if ENABLE_HUSH_LOOPS
7981 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) 8152 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
7982 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */ 8153 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
@@ -8054,7 +8225,7 @@ static int run_list(struct pipe *pi)
8054 } 8225 }
8055 /* Insert next value from for_lcur */ 8226 /* Insert next value from for_lcur */
8056 /* note: *for_lcur already has quotes removed, $var expanded, etc */ 8227 /* note: *for_lcur already has quotes removed, $var expanded, etc */
8057 set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); 8228 set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*flag:*/ 0);
8058 continue; 8229 continue;
8059 } 8230 }
8060 if (rword == RES_IN) { 8231 if (rword == RES_IN) {
@@ -8068,6 +8239,7 @@ static int run_list(struct pipe *pi)
8068 if (rword == RES_CASE) { 8239 if (rword == RES_CASE) {
8069 debug_printf_exec("CASE cond_code:%d\n", cond_code); 8240 debug_printf_exec("CASE cond_code:%d\n", cond_code);
8070 case_word = expand_strvec_to_string(pi->cmds->argv); 8241 case_word = expand_strvec_to_string(pi->cmds->argv);
8242 unbackslash(case_word);
8071 continue; 8243 continue;
8072 } 8244 }
8073 if (rword == RES_MATCH) { 8245 if (rword == RES_MATCH) {
@@ -8079,9 +8251,10 @@ static int run_list(struct pipe *pi)
8079 /* all prev words didn't match, does this one match? */ 8251 /* all prev words didn't match, does this one match? */
8080 argv = pi->cmds->argv; 8252 argv = pi->cmds->argv;
8081 while (*argv) { 8253 while (*argv) {
8082 char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 1); 8254 char *pattern = expand_string_to_string(*argv, /*unbackslash:*/ 0);
8083 /* TODO: which FNM_xxx flags to use? */ 8255 /* TODO: which FNM_xxx flags to use? */
8084 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); 8256 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
8257 debug_printf_exec("fnmatch(pattern:'%s',str:'%s'):%d\n", pattern, case_word, cond_code);
8085 free(pattern); 8258 free(pattern);
8086 if (cond_code == 0) { /* match! we will execute this branch */ 8259 if (cond_code == 0) { /* match! we will execute this branch */
8087 free(case_word); 8260 free(case_word);
@@ -8162,10 +8335,11 @@ static int run_list(struct pipe *pi)
8162 * I'm NOT treating inner &'s as jobs */ 8335 * I'm NOT treating inner &'s as jobs */
8163#if ENABLE_HUSH_JOB 8336#if ENABLE_HUSH_JOB
8164 if (G.run_list_level == 1) 8337 if (G.run_list_level == 1)
8165 insert_bg_job(pi); 8338 insert_job_into_table(pi);
8166#endif 8339#endif
8167 /* Last command's pid goes to $! */ 8340 /* Last command's pid goes to $! */
8168 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; 8341 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
8342 G.last_bg_pid_exitcode = 0;
8169 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); 8343 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
8170/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ 8344/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */
8171 rcode = EXIT_SUCCESS; 8345 rcode = EXIT_SUCCESS;
@@ -8187,6 +8361,14 @@ static int run_list(struct pipe *pi)
8187 check_and_run_traps(); 8361 check_and_run_traps();
8188 } 8362 }
8189 8363
8364 /* Handle "set -e" */
8365 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
8366 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
8367 if (G.errexit_depth == 0)
8368 hush_exit(rcode);
8369 }
8370 G.errexit_depth = sv_errexit_depth;
8371
8190 /* Analyze how result affects subsequent commands */ 8372 /* Analyze how result affects subsequent commands */
8191#if ENABLE_HUSH_IF 8373#if ENABLE_HUSH_IF
8192 if (rword == RES_IF || rword == RES_ELIF) 8374 if (rword == RES_IF || rword == RES_ELIF)
@@ -8366,6 +8548,9 @@ static int set_mode(int state, char mode, const char *o_opt)
8366 G.o_opt[idx] = state; 8548 G.o_opt[idx] = state;
8367 break; 8549 break;
8368 } 8550 }
8551 case 'e':
8552 G.o_opt[OPT_O_ERREXIT] = state;
8553 break;
8369 default: 8554 default:
8370 return EXIT_FAILURE; 8555 return EXIT_FAILURE;
8371 } 8556 }
@@ -8425,7 +8610,7 @@ int hush_main(int argc, char **argv)
8425 putenv(shell_ver->varstr); 8610 putenv(shell_ver->varstr);
8426 8611
8427 /* Export PWD */ 8612 /* Export PWD */
8428 set_pwd_var(/*exp:*/ 1); 8613 set_pwd_var(SETFLAG_EXPORT);
8429 8614
8430#if BASH_HOSTNAME_VAR 8615#if BASH_HOSTNAME_VAR
8431 /* Set (but not export) HOSTNAME unless already set */ 8616 /* Set (but not export) HOSTNAME unless already set */
@@ -8492,7 +8677,7 @@ int hush_main(int argc, char **argv)
8492 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0; 8677 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
8493 builtin_argc = 0; 8678 builtin_argc = 0;
8494 while (1) { 8679 while (1) {
8495 opt = getopt(argc, argv, "+c:xinsl" 8680 opt = getopt(argc, argv, "+c:exinsl"
8496#if !BB_MMU 8681#if !BB_MMU
8497 "<:$:R:V:" 8682 "<:$:R:V:"
8498# if ENABLE_HUSH_FUNCTIONS 8683# if ENABLE_HUSH_FUNCTIONS
@@ -8575,8 +8760,9 @@ int hush_main(int argc, char **argv)
8575 optarg++; 8760 optarg++;
8576 empty_trap_mask = bb_strtoull(optarg, &optarg, 16); 8761 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
8577 if (empty_trap_mask != 0) { 8762 if (empty_trap_mask != 0) {
8578 int sig; 8763 IF_HUSH_TRAP(int sig;)
8579 install_special_sighandlers(); 8764 install_special_sighandlers();
8765# if ENABLE_HUSH_TRAP
8580 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); 8766 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
8581 for (sig = 1; sig < NSIG; sig++) { 8767 for (sig = 1; sig < NSIG; sig++) {
8582 if (empty_trap_mask & (1LL << sig)) { 8768 if (empty_trap_mask & (1LL << sig)) {
@@ -8584,6 +8770,7 @@ int hush_main(int argc, char **argv)
8584 install_sighandler(sig, SIG_IGN); 8770 install_sighandler(sig, SIG_IGN);
8585 } 8771 }
8586 } 8772 }
8773# endif
8587 } 8774 }
8588# if ENABLE_HUSH_LOOPS 8775# if ENABLE_HUSH_LOOPS
8589 optarg++; 8776 optarg++;
@@ -8593,7 +8780,7 @@ int hush_main(int argc, char **argv)
8593 } 8780 }
8594 case 'R': 8781 case 'R':
8595 case 'V': 8782 case 'V':
8596 set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R'); 8783 set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0);
8597 break; 8784 break;
8598# if ENABLE_HUSH_FUNCTIONS 8785# if ENABLE_HUSH_FUNCTIONS
8599 case 'F': { 8786 case 'F': {
@@ -8608,6 +8795,7 @@ int hush_main(int argc, char **argv)
8608#endif 8795#endif
8609 case 'n': 8796 case 'n':
8610 case 'x': 8797 case 'x':
8798 case 'e':
8611 if (set_mode(1, opt, NULL) == 0) /* no error */ 8799 if (set_mode(1, opt, NULL) == 0) /* no error */
8612 break; 8800 break;
8613 default: 8801 default:
@@ -8694,7 +8882,7 @@ int hush_main(int argc, char **argv)
8694 G_saved_tty_pgrp = 0; 8882 G_saved_tty_pgrp = 0;
8695 8883
8696 /* try to dup stdin to high fd#, >= 255 */ 8884 /* try to dup stdin to high fd#, >= 255 */
8697 G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); 8885 G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254);
8698 if (G_interactive_fd < 0) { 8886 if (G_interactive_fd < 0) {
8699 /* try to dup to any fd */ 8887 /* try to dup to any fd */
8700 G_interactive_fd = dup(STDIN_FILENO); 8888 G_interactive_fd = dup(STDIN_FILENO);
@@ -8767,7 +8955,7 @@ int hush_main(int argc, char **argv)
8767#elif ENABLE_HUSH_INTERACTIVE 8955#elif ENABLE_HUSH_INTERACTIVE
8768 /* No job control compiled in, only prompt/line editing */ 8956 /* No job control compiled in, only prompt/line editing */
8769 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 8957 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
8770 G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); 8958 G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254);
8771 if (G_interactive_fd < 0) { 8959 if (G_interactive_fd < 0) {
8772 /* try to dup to any fd */ 8960 /* try to dup to any fd */
8773 G_interactive_fd = dup(STDIN_FILENO); 8961 G_interactive_fd = dup(STDIN_FILENO);
@@ -8806,16 +8994,6 @@ int hush_main(int argc, char **argv)
8806} 8994}
8807 8995
8808 8996
8809#if ENABLE_MSH
8810int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
8811int msh_main(int argc, char **argv)
8812{
8813 bb_error_msg("msh is deprecated, please use hush instead");
8814 return hush_main(argc, argv);
8815}
8816#endif
8817
8818
8819/* 8997/*
8820 * Built-ins 8998 * Built-ins
8821 */ 8999 */
@@ -8827,12 +9005,8 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
8827#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL 9005#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
8828static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) 9006static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
8829{ 9007{
8830 int argc = 0; 9008 int argc = string_array_len(argv);
8831 while (*argv) { 9009 return applet_main_func(argc, argv);
8832 argc++;
8833 argv++;
8834 }
8835 return applet_main_func(argc, argv - argc);
8836} 9010}
8837#endif 9011#endif
8838#if ENABLE_HUSH_TEST || BASH_TEST2 9012#if ENABLE_HUSH_TEST || BASH_TEST2
@@ -8909,7 +9083,7 @@ static int FAST_FUNC builtin_cd(char **argv)
8909 * Note: do not enforce exporting. If PWD was unset or unexported, 9083 * Note: do not enforce exporting. If PWD was unset or unexported,
8910 * set it again, but do not export. bash does the same. 9084 * set it again, but do not export. bash does the same.
8911 */ 9085 */
8912 set_pwd_var(/*exp:*/ 0); 9086 set_pwd_var(/*flag:*/ 0);
8913 return EXIT_SUCCESS; 9087 return EXIT_SUCCESS;
8914} 9088}
8915 9089
@@ -9147,12 +9321,8 @@ static void print_escaped(const char *s)
9147} 9321}
9148#endif 9322#endif
9149 9323
9150#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL 9324#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY
9151# if !ENABLE_HUSH_LOCAL 9325static int helper_export_local(char **argv, unsigned flags)
9152#define helper_export_local(argv, exp, lvl) \
9153 helper_export_local(argv, exp)
9154# endif
9155static void helper_export_local(char **argv, int exp, int lvl)
9156{ 9326{
9157 do { 9327 do {
9158 char *name = *argv; 9328 char *name = *argv;
@@ -9166,7 +9336,7 @@ static void helper_export_local(char **argv, int exp, int lvl)
9166 vpp = get_ptr_to_local_var(name, name_end - name); 9336 vpp = get_ptr_to_local_var(name, name_end - name);
9167 var = vpp ? *vpp : NULL; 9337 var = vpp ? *vpp : NULL;
9168 9338
9169 if (exp == -1) { /* unexporting? */ 9339 if (flags & SETFLAG_UNEXPORT) {
9170 /* export -n NAME (without =VALUE) */ 9340 /* export -n NAME (without =VALUE) */
9171 if (var) { 9341 if (var) {
9172 var->flg_export = 0; 9342 var->flg_export = 0;
@@ -9175,7 +9345,7 @@ static void helper_export_local(char **argv, int exp, int lvl)
9175 } /* else: export -n NOT_EXISTING_VAR: no-op */ 9345 } /* else: export -n NOT_EXISTING_VAR: no-op */
9176 continue; 9346 continue;
9177 } 9347 }
9178 if (exp == 1) { /* exporting? */ 9348 if (flags & SETFLAG_EXPORT) {
9179 /* export NAME (without =VALUE) */ 9349 /* export NAME (without =VALUE) */
9180 if (var) { 9350 if (var) {
9181 var->flg_export = 1; 9351 var->flg_export = 1;
@@ -9184,28 +9354,45 @@ static void helper_export_local(char **argv, int exp, int lvl)
9184 continue; 9354 continue;
9185 } 9355 }
9186 } 9356 }
9357 if (flags & SETFLAG_MAKE_RO) {
9358 /* readonly NAME (without =VALUE) */
9359 if (var) {
9360 var->flg_read_only = 1;
9361 continue;
9362 }
9363 }
9187# if ENABLE_HUSH_LOCAL 9364# if ENABLE_HUSH_LOCAL
9188 if (exp == 0 /* local? */ 9365 /* Is this "local" bltin? */
9189 && var && var->func_nest_level == lvl 9366 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) {
9190 ) { 9367 unsigned lvl = flags >> SETFLAG_LOCAL_SHIFT;
9191 /* "local x=abc; ...; local x" - ignore second local decl */ 9368 if (var && var->func_nest_level == lvl) {
9192 continue; 9369 /* "local x=abc; ...; local x" - ignore second local decl */
9370 continue;
9371 }
9193 } 9372 }
9194# endif 9373# endif
9195 /* Exporting non-existing variable. 9374 /* Exporting non-existing variable.
9196 * bash does not put it in environment, 9375 * bash does not put it in environment,
9197 * but remembers that it is exported, 9376 * but remembers that it is exported,
9198 * and does put it in env when it is set later. 9377 * and does put it in env when it is set later.
9199 * We just set it to "" and export. */ 9378 * We just set it to "" and export.
9379 */
9200 /* Or, it's "local NAME" (without =VALUE). 9380 /* Or, it's "local NAME" (without =VALUE).
9201 * bash sets the value to "". */ 9381 * bash sets the value to "".
9382 */
9383 /* Or, it's "readonly NAME" (without =VALUE).
9384 * bash remembers NAME and disallows its creation
9385 * in the future.
9386 */
9202 name = xasprintf("%s=", name); 9387 name = xasprintf("%s=", name);
9203 } else { 9388 } else {
9204 /* (Un)exporting/making local NAME=VALUE */ 9389 /* (Un)exporting/making local NAME=VALUE */
9205 name = xstrdup(name); 9390 name = xstrdup(name);
9206 } 9391 }
9207 set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); 9392 if (set_local_var(name, flags))
9393 return EXIT_FAILURE;
9208 } while (*++argv); 9394 } while (*++argv);
9395 return EXIT_SUCCESS;
9209} 9396}
9210#endif 9397#endif
9211 9398
@@ -9251,9 +9438,7 @@ static int FAST_FUNC builtin_export(char **argv)
9251 return EXIT_SUCCESS; 9438 return EXIT_SUCCESS;
9252 } 9439 }
9253 9440
9254 helper_export_local(argv, (opt_unexport ? -1 : 1), 0); 9441 return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT);
9255
9256 return EXIT_SUCCESS;
9257} 9442}
9258#endif 9443#endif
9259 9444
@@ -9264,8 +9449,29 @@ static int FAST_FUNC builtin_local(char **argv)
9264 bb_error_msg("%s: not in a function", argv[0]); 9449 bb_error_msg("%s: not in a function", argv[0]);
9265 return EXIT_FAILURE; /* bash compat */ 9450 return EXIT_FAILURE; /* bash compat */
9266 } 9451 }
9267 helper_export_local(argv, 0, G.func_nest_level); 9452 argv++;
9268 return EXIT_SUCCESS; 9453 return helper_export_local(argv, G.func_nest_level << SETFLAG_LOCAL_SHIFT);
9454}
9455#endif
9456
9457#if ENABLE_HUSH_READONLY
9458static int FAST_FUNC builtin_readonly(char **argv)
9459{
9460 argv++;
9461 if (*argv == NULL) {
9462 /* bash: readonly [-p]: list all readonly VARs
9463 * (-p has no effect in bash)
9464 */
9465 struct variable *e;
9466 for (e = G.top_var; e; e = e->next) {
9467 if (e->flg_read_only) {
9468//TODO: quote value: readonly VAR='VAL'
9469 printf("readonly %s\n", e->varstr);
9470 }
9471 }
9472 return EXIT_SUCCESS;
9473 }
9474 return helper_export_local(argv, SETFLAG_MAKE_RO);
9269} 9475}
9270#endif 9476#endif
9271 9477
@@ -9379,10 +9585,7 @@ static int FAST_FUNC builtin_set(char **argv)
9379 /* This realloc's G.global_argv */ 9585 /* This realloc's G.global_argv */
9380 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1); 9586 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
9381 9587
9382 n = 1; 9588 G.global_argc = 1 + string_array_len(pp + 1);
9383 while (*++pp)
9384 n++;
9385 G.global_argc = n;
9386 9589
9387 return EXIT_SUCCESS; 9590 return EXIT_SUCCESS;
9388 9591
@@ -9398,7 +9601,18 @@ static int FAST_FUNC builtin_shift(char **argv)
9398 int n = 1; 9601 int n = 1;
9399 argv = skip_dash_dash(argv); 9602 argv = skip_dash_dash(argv);
9400 if (argv[0]) { 9603 if (argv[0]) {
9401 n = atoi(argv[0]); 9604 n = bb_strtou(argv[0], NULL, 10);
9605 if (errno || n < 0) {
9606 /* shared string with ash.c */
9607 bb_error_msg("Illegal number: %s", argv[0]);
9608 /*
9609 * ash aborts in this case.
9610 * bash prints error message and set $? to 1.
9611 * Interestingly, for "shift 99999" bash does not
9612 * print error message, but does set $? to 1
9613 * (and does no shifting at all).
9614 */
9615 }
9402 } 9616 }
9403 if (n >= 0 && n < G.global_argc) { 9617 if (n >= 0 && n < G.global_argc) {
9404 if (G_global_args_malloced) { 9618 if (G_global_args_malloced) {
@@ -9511,7 +9725,7 @@ static int FAST_FUNC builtin_trap(char **argv)
9511 if (sig < 0 || sig >= NSIG) { 9725 if (sig < 0 || sig >= NSIG) {
9512 ret = EXIT_FAILURE; 9726 ret = EXIT_FAILURE;
9513 /* Mimic bash message exactly */ 9727 /* Mimic bash message exactly */
9514 bb_perror_msg("trap: %s: invalid signal specification", argv[-1]); 9728 bb_error_msg("trap: %s: invalid signal specification", argv[-1]);
9515 continue; 9729 continue;
9516 } 9730 }
9517 9731
@@ -9604,6 +9818,9 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
9604 9818
9605 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); 9819 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
9606 } 9820 }
9821
9822 clean_up_last_dead_job();
9823
9607 return EXIT_SUCCESS; 9824 return EXIT_SUCCESS;
9608} 9825}
9609 9826
@@ -9648,14 +9865,14 @@ static int FAST_FUNC builtin_fg_bg(char **argv)
9648 i = kill(- pi->pgrp, SIGCONT); 9865 i = kill(- pi->pgrp, SIGCONT);
9649 if (i < 0) { 9866 if (i < 0) {
9650 if (errno == ESRCH) { 9867 if (errno == ESRCH) {
9651 delete_finished_bg_job(pi); 9868 delete_finished_job(pi);
9652 return EXIT_SUCCESS; 9869 return EXIT_SUCCESS;
9653 } 9870 }
9654 bb_perror_msg("kill (SIGCONT)"); 9871 bb_perror_msg("kill (SIGCONT)");
9655 } 9872 }
9656 9873
9657 if (argv[0][0] == 'f') { 9874 if (argv[0][0] == 'f') {
9658 remove_bg_job(pi); 9875 remove_job_from_table(pi); /* FG job shouldn't be in job table */
9659 return checkjobs_and_fg_shell(pi); 9876 return checkjobs_and_fg_shell(pi);
9660 } 9877 }
9661 return EXIT_SUCCESS; 9878 return EXIT_SUCCESS;
@@ -9847,8 +10064,12 @@ static int FAST_FUNC builtin_wait(char **argv)
9847 wait_pipe = parse_jobspec(*argv); 10064 wait_pipe = parse_jobspec(*argv);
9848 if (wait_pipe) { 10065 if (wait_pipe) {
9849 ret = job_exited_or_stopped(wait_pipe); 10066 ret = job_exited_or_stopped(wait_pipe);
9850 if (ret < 0) 10067 if (ret < 0) {
9851 ret = wait_for_child_or_signal(wait_pipe, 0); 10068 ret = wait_for_child_or_signal(wait_pipe, 0);
10069 } else {
10070 /* waiting on "last dead job" removes it */
10071 clean_up_last_dead_job();
10072 }
9852 } 10073 }
9853 /* else: parse_jobspec() already emitted error msg */ 10074 /* else: parse_jobspec() already emitted error msg */
9854 continue; 10075 continue;
@@ -9864,14 +10085,15 @@ static int FAST_FUNC builtin_wait(char **argv)
9864 ret = waitpid(pid, &status, WNOHANG); 10085 ret = waitpid(pid, &status, WNOHANG);
9865 if (ret < 0) { 10086 if (ret < 0) {
9866 /* No */ 10087 /* No */
10088 ret = 127;
9867 if (errno == ECHILD) { 10089 if (errno == ECHILD) {
9868 if (G.last_bg_pid > 0 && pid == G.last_bg_pid) { 10090 if (pid == G.last_bg_pid) {
9869 /* "wait $!" but last bg task has already exited. Try: 10091 /* "wait $!" but last bg task has already exited. Try:
9870 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $? 10092 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
9871 * In bash it prints exitcode 0, then 3. 10093 * In bash it prints exitcode 0, then 3.
9872 * In dash, it is 127. 10094 * In dash, it is 127.
9873 */ 10095 */
9874 /* ret = G.last_bg_pid_exitstatus - FIXME */ 10096 ret = G.last_bg_pid_exitcode;
9875 } else { 10097 } else {
9876 /* Example: "wait 1". mimic bash message */ 10098 /* Example: "wait 1". mimic bash message */
9877 bb_error_msg("wait: pid %d is not a child of this shell", (int)pid); 10099 bb_error_msg("wait: pid %d is not a child of this shell", (int)pid);
@@ -9880,7 +10102,6 @@ static int FAST_FUNC builtin_wait(char **argv)
9880 /* ??? */ 10102 /* ??? */
9881 bb_perror_msg("wait %s", *argv); 10103 bb_perror_msg("wait %s", *argv);
9882 } 10104 }
9883 ret = 127;
9884 continue; /* bash checks all argv[] */ 10105 continue; /* bash checks all argv[] */
9885 } 10106 }
9886 if (ret == 0) { 10107 if (ret == 0) {
diff --git a/shell/hush_test/hush-heredoc/heredoc8.right b/shell/hush_test/hush-heredoc/heredoc8.right
new file mode 100644
index 000000000..558858f47
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc8.right
@@ -0,0 +1 @@
hush: syntax error at 'then'
diff --git a/shell/hush_test/hush-heredoc/heredoc8.tests b/shell/hush_test/hush-heredoc/heredoc8.tests
new file mode 100755
index 000000000..f7bc0737a
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc8.tests
@@ -0,0 +1,3 @@
1# ash used to SEGV on this:
2
3<<EOF; then <W
diff --git a/shell/hush_test/hush-misc/errexit1.right b/shell/hush_test/hush-misc/errexit1.right
new file mode 100644
index 000000000..d86bac9de
--- /dev/null
+++ b/shell/hush_test/hush-misc/errexit1.right
@@ -0,0 +1 @@
OK
diff --git a/shell/hush_test/hush-misc/errexit1.tests b/shell/hush_test/hush-misc/errexit1.tests
new file mode 100755
index 000000000..7b4a15634
--- /dev/null
+++ b/shell/hush_test/hush-misc/errexit1.tests
@@ -0,0 +1,5 @@
1set -e
2(true)
3echo OK
4(false)
5echo FAIL
diff --git a/shell/hush_test/hush-misc/shift1.right b/shell/hush_test/hush-misc/shift1.right
new file mode 100644
index 000000000..e3ab61392
--- /dev/null
+++ b/shell/hush_test/hush-misc/shift1.right
@@ -0,0 +1,10 @@
12 3 4
2hush: Illegal number: -1
31 2 3 4
41 2 3 4
52 3 4
63 4
74
8
91 2 3 4
101 2 3 4
diff --git a/shell/hush_test/hush-misc/shift1.tests b/shell/hush_test/hush-misc/shift1.tests
new file mode 100755
index 000000000..f2a264751
--- /dev/null
+++ b/shell/hush_test/hush-misc/shift1.tests
@@ -0,0 +1,10 @@
1$THIS_SH -c 'shift; echo "$@"' 0 1 2 3 4
2#We complain on -1 and continue.
3$THIS_SH -c 'shift -1; echo "$@"' 0 1 2 3 4
4$THIS_SH -c 'shift 0; echo "$@"' 0 1 2 3 4
5$THIS_SH -c 'shift 1; echo "$@"' 0 1 2 3 4
6$THIS_SH -c 'shift 2; echo "$@"' 0 1 2 3 4
7$THIS_SH -c 'shift 3; echo "$@"' 0 1 2 3 4
8$THIS_SH -c 'shift 4; echo "$@"' 0 1 2 3 4
9$THIS_SH -c 'shift 5; echo "$@"' 0 1 2 3 4
10$THIS_SH -c 'shift 6; echo "$@"' 0 1 2 3 4
diff --git a/shell/hush_test/hush-misc/sigint1.right b/shell/hush_test/hush-misc/sigint1.right
deleted file mode 100644
index a9094b056..000000000
--- a/shell/hush_test/hush-misc/sigint1.right
+++ /dev/null
@@ -1 +0,0 @@
1Sending SIGINT to main shell PID
diff --git a/shell/hush_test/hush-misc/sigint1.tests b/shell/hush_test/hush-misc/sigint1.tests
deleted file mode 100755
index 3d483d32a..000000000
--- a/shell/hush_test/hush-misc/sigint1.tests
+++ /dev/null
@@ -1,41 +0,0 @@
1# What should happen if non-interactive shell gets SIGINT?
2
3(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
4
5# We create a child which exits with 0 even on SIGINT
6# (The complex command is necessary only if SIGINT is generated by ^C,
7# in this testcase even bare "sleep 2" would do because
8# in the testcase we don't send SIGINT *to the child*...)
9$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
10
11# In one second, we (main shell) get SIGINT here.
12# The question is whether we should, or should not, exit.
13
14# bash will not stop here. It will execute next command(s).
15
16# The rationale for this is described here:
17# http://www.cons.org/cracauer/sigint.html
18#
19# Basically, bash will not exit on SIGINT immediately if it waits
20# for a child. It will wait for the child to exit.
21# If child exits NOT by dying on SIGINT, then bash will not exit.
22#
23# The idea is that the following script:
24# | emacs file.txt
25# | more cmds
26# User may use ^C to interrupt editor's ops like search. But then
27# emacs exits normally. User expects that script doesn't stop.
28#
29# This is a nice idea, but detecting "did process really exit
30# with SIGINT?" is racy. Consider:
31# | bash -c 'while true; do /bin/true; done'
32# When ^C is pressed while bash waits for /bin/true to exit,
33# it may happen that /bin/true exits with exitcode 0 before
34# ^C is delivered to it as SIGINT. bash will see SIGINT, then
35# it will see that child exited with 0, and bash will NOT EXIT.
36
37# Therefore we do not implement bash behavior.
38# I'd say that emacs need to put itself into a separate pgrp
39# to isolate shell from getting stray SIGINTs from ^C.
40
41echo Next command after SIGINT was executed
diff --git a/shell/hush_test/hush-misc/tickquote1.right b/shell/hush_test/hush-misc/tickquote1.right
new file mode 100644
index 000000000..56f8515b7
--- /dev/null
+++ b/shell/hush_test/hush-misc/tickquote1.right
@@ -0,0 +1 @@
hush: syntax error: unterminated "
diff --git a/shell/hush_test/hush-misc/tickquote1.tests b/shell/hush_test/hush-misc/tickquote1.tests
new file mode 100755
index 000000000..02e3904f1
--- /dev/null
+++ b/shell/hush_test/hush-misc/tickquote1.tests
@@ -0,0 +1,2 @@
1# UNFIXED BUG: hush does not parse embedded `cmd` at embedding document parse time
2echo _`"pwd`_
diff --git a/shell/hush_test/hush-misc/wait6.right b/shell/hush_test/hush-misc/wait6.right
new file mode 100644
index 000000000..12decc137
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait6.right
@@ -0,0 +1,2 @@
10
23
diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests
new file mode 100755
index 000000000..c23713199
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait6.tests
@@ -0,0 +1,6 @@
1# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
2# It prints 0, then 3:
3(sleep 0; exit 3) & sleep 1
4echo $?
5wait $!
6echo $?
diff --git a/shell/hush_test/hush-parsing/and_or_and_backgrounding.right b/shell/hush_test/hush-parsing/and_or_and_backgrounding.right
new file mode 100644
index 000000000..90ce63e01
--- /dev/null
+++ b/shell/hush_test/hush-parsing/and_or_and_backgrounding.right
@@ -0,0 +1,4 @@
1First
2Second
3Third
4Done
diff --git a/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests b/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests
new file mode 100755
index 000000000..204d44490
--- /dev/null
+++ b/shell/hush_test/hush-parsing/and_or_and_backgrounding.tests
@@ -0,0 +1,30 @@
1# According to this doc, && || have higher precedence than ; &.
2# See example below.
3# Precedence of ; is not a problem in practice. Precedence of & is.
4#
5#http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
6#
7#2.9.3 Lists
8#
9#An AND-OR list is a sequence of one or more pipelines separated by
10#the operators "&&" and "||" .
11#
12#A list is a sequence of one or more AND-OR lists separated by the operators
13#';' and '&' and optionally terminated by ';', '&', or <newline>.
14#
15#The operators "&&" and "||" shall have equal precedence and shall be
16#evaluated with left associativity. For example, both of the following
17#commands write solely bar to standard output:
18#
19# false && echo foo || echo bar
20# true || echo foo && echo bar
21#
22#A ';' or <newline> terminator shall cause the preceding AND-OR list
23#to be executed sequentially; an '&' shall cause asynchronous execution
24#of the preceding AND-OR list.
25
26echo First && sleep 0.2 && echo Third &
27sleep 0.1
28echo Second
29wait
30echo Done
diff --git a/shell/hush_test/hush-quoting/quoted_punct.right b/shell/hush_test/hush-quoting/quoted_punct.right
new file mode 100644
index 000000000..ab66c3ce0
--- /dev/null
+++ b/shell/hush_test/hush-quoting/quoted_punct.right
@@ -0,0 +1,35 @@
1ok
2ok
3ok
4ok
5ok
6ok
7ok
8ok
9ok
10ok
11ok
12ok
13ok
14ok
15ok
16ok
17ok
18ok
19ok
20ok
21ok
22ok
23ok
24ok
25ok
26ok
27ok
28ok
29ok
30ok
31ok
32ok
33ok
34ok
35ok
diff --git a/shell/hush_test/hush-quoting/quoted_punct.tests b/shell/hush_test/hush-quoting/quoted_punct.tests
new file mode 100755
index 000000000..83ee40bf4
--- /dev/null
+++ b/shell/hush_test/hush-quoting/quoted_punct.tests
@@ -0,0 +1,41 @@
1# Testing glob-escaping of every ASCII punctuation char
2# Some chars have more than one test
3# 21..2f
4case '!' in [\!] ) echo ok;; *) echo 'WRONG!';; esac
5case '"' in [\"] ) echo ok;; *) echo 'WRONG"';; esac
6case '#' in [\#] ) echo ok;; *) echo 'WRONG#';; esac
7case '$' in [\$] ) echo ok;; *) echo 'WRONG$';; esac
8case '%' in [\%] ) echo ok;; *) echo 'WRONG%';; esac
9case '&' in [\&] ) echo ok;; *) echo 'WRONG&';; esac
10case "'" in [\'] ) echo ok;; *) echo "WRONG'";; esac
11case '(' in [\(] ) echo ok;; *) echo 'WRONG(';; esac
12case ')' in [\)] ) echo ok;; *) echo 'WRONG)';; esac
13case '*' in [\*] ) echo ok;; *) echo 'WRONG*';; esac
14case '+' in [\+] ) echo ok;; *) echo 'WRONG+';; esac
15case ',' in [\,] ) echo ok;; *) echo 'WRONG,';; esac
16case '-' in [\-] ) echo ok;; *) echo 'WRONG-';; esac
17case '-' in [a\-c]) echo ok;; *) echo 'WRONGa\-c';; esac
18case '.' in [\.] ) echo ok;; *) echo 'WRONG.';; esac
19case '/' in [\/] ) echo ok;; *) echo 'WRONG/';; esac
20# 3a..40
21case ':' in [\:] ) echo ok;; *) echo 'WRONG:';; esac
22case ';' in [\;] ) echo ok;; *) echo 'WRONG;';; esac
23case '<' in [\<] ) echo ok;; *) echo 'WRONG<';; esac
24case '=' in [\=] ) echo ok;; *) echo 'WRONG=';; esac
25case '>' in [\>] ) echo ok;; *) echo 'WRONG>';; esac
26case '?' in [\?] ) echo ok;; *) echo 'WRONG?';; esac
27case '@' in [\@] ) echo ok;; *) echo 'WRONG@';; esac
28# 5b..60
29case '[' in [\[] ) echo ok;; *) echo 'WRONG[';; esac
30case '\' in [\\] ) echo ok;; *) echo 'WRONG\';; esac
31case '\' in \\ ) echo ok;; *) echo 'WRONG\\';; esac
32case ']' in [\]] ) echo ok;; *) echo 'WRONG]';; esac
33case ']' in [a\]]) echo ok;; *) echo 'WRONGa]';; esac
34case '^' in [\^] ) echo ok;; *) echo 'WRONG^';; esac
35case '_' in [\_] ) echo ok;; *) echo 'WRONG_';; esac
36case '`' in [\`] ) echo ok;; *) echo 'WRONG`';; esac
37# 7b..7e
38case '{' in [\{] ) echo ok;; *) echo 'WRONG{';; esac
39case '|' in [\|] ) echo ok;; *) echo 'WRONG|';; esac
40case '}' in [\}] ) echo ok;; *) echo 'WRONG}';; esac
41case '~' in [\~] ) echo ok;; *) echo 'WRONG~';; esac
diff --git a/shell/hush_test/hush-quoting/unicode_8x_chars.right b/shell/hush_test/hush-quoting/unicode_8x_chars.right
new file mode 100644
index 000000000..7780b88b4
--- /dev/null
+++ b/shell/hush_test/hush-quoting/unicode_8x_chars.right
@@ -0,0 +1,6 @@
1ok
2ok
3ok
4ok
5ok
6ok
diff --git a/shell/hush_test/hush-quoting/unicode_8x_chars.tests b/shell/hush_test/hush-quoting/unicode_8x_chars.tests
new file mode 100755
index 000000000..1258745ec
--- /dev/null
+++ b/shell/hush_test/hush-quoting/unicode_8x_chars.tests
@@ -0,0 +1,28 @@
1# Unicode: cf 80
2case π in
3( "π" ) echo ok ;;
4( * ) echo WRONG ;;
5esac
6# Unicode: cf 81
7case ρ in
8( "ρ" ) echo ok ;;
9( * ) echo WRONG ;;
10esac
11# Unicode: cf 82
12case ς in
13( "ς" ) echo ok ;;
14( * ) echo WRONG ;;
15esac
16
17case "π" in
18( π ) echo ok ;;
19( * ) echo WRONG ;;
20esac
21case "ρ" in
22( ρ ) echo ok ;;
23( * ) echo WRONG ;;
24esac
25case "ς" in
26( ς ) echo ok ;;
27( * ) echo WRONG ;;
28esac
diff --git a/shell/hush_test/hush-redir/redir3.right b/shell/hush_test/hush-redir/redir3.right
index 3d20bbf68..e3c878b7d 100644
--- a/shell/hush_test/hush-redir/redir3.right
+++ b/shell/hush_test/hush-redir/redir3.right
@@ -1,14 +1,2 @@
1hush: can't open '/does/not/exist': No such file or directory 1TEST
2One:1 2hush: can't duplicate file descriptor: Bad file descriptor
3hush: can't open '/cant/be/created': No such file or directory
4One:1
5Ok
6hush: can't open '/cant/be/created': No such file or directory
7Zero:0
8hush: can't open '/cant/be/created': No such file or directory
9One:1
10hush: can't open '/cant/be/created': No such file or directory
11One:1
12hush: can't open '/cant/be/created': No such file or directory
13Zero:0
14Done
diff --git a/shell/hush_test/hush-redir/redir3.tests b/shell/hush_test/hush-redir/redir3.tests
index 7c28e4324..e37d5e45a 100755
--- a/shell/hush_test/hush-redir/redir3.tests
+++ b/shell/hush_test/hush-redir/redir3.tests
@@ -1,9 +1,5 @@
1echo Error >/does/not/exist; echo One:$? 1# redirects to closed descriptors should not leave these descriptors
2t=BAD 2# open afterwards
3t=Ok >>/cant/be/created; echo One:$? 3echo TEST 9>/dev/null
4echo $t 4echo MUST ERROR OUT >&9
5! >/cant/be/created; echo Zero:$? 5echo "Output to fd#9: $?"
6exec >/cant/be/created; echo One:$?
7exec /bin/true >/cant/be/created; echo One:$?
8! exec /bin/true >/cant/be/created; echo Zero:$?
9echo Done
diff --git a/shell/hush_test/hush-redir/redir_errors.right b/shell/hush_test/hush-redir/redir_errors.right
new file mode 100644
index 000000000..3d20bbf68
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_errors.right
@@ -0,0 +1,14 @@
1hush: can't open '/does/not/exist': No such file or directory
2One:1
3hush: can't open '/cant/be/created': No such file or directory
4One:1
5Ok
6hush: can't open '/cant/be/created': No such file or directory
7Zero:0
8hush: can't open '/cant/be/created': No such file or directory
9One:1
10hush: can't open '/cant/be/created': No such file or directory
11One:1
12hush: can't open '/cant/be/created': No such file or directory
13Zero:0
14Done
diff --git a/shell/hush_test/hush-redir/redir_errors.tests b/shell/hush_test/hush-redir/redir_errors.tests
new file mode 100755
index 000000000..7c28e4324
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_errors.tests
@@ -0,0 +1,9 @@
1echo Error >/does/not/exist; echo One:$?
2t=BAD
3t=Ok >>/cant/be/created; echo One:$?
4echo $t
5! >/cant/be/created; echo Zero:$?
6exec >/cant/be/created; echo One:$?
7exec /bin/true >/cant/be/created; echo One:$?
8! exec /bin/true >/cant/be/created; echo Zero:$?
9echo Done
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd.right b/shell/hush_test/hush-redir/redir_to_bad_fd.right
new file mode 100644
index 000000000..936911ce5
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd.right
@@ -0,0 +1 @@
hush: can't duplicate file descriptor: Bad file descriptor
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd.tests b/shell/hush_test/hush-redir/redir_to_bad_fd.tests
new file mode 100755
index 000000000..91b0c1ff9
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd.tests
@@ -0,0 +1,3 @@
1# ash uses fd 10 (usually) for reading the script
2echo LOST >&10
3echo OK
diff --git a/shell/hush_test/hush-signals/signal4.right b/shell/hush_test/hush-signals/signal4.right
new file mode 100644
index 000000000..2d0624714
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal4.right
@@ -0,0 +1,4 @@
1hush: trap: BADNAME: invalid signal specification
21
3Trapped
4Ok
diff --git a/shell/hush_test/hush-signals/signal4.tests b/shell/hush_test/hush-signals/signal4.tests
new file mode 100755
index 000000000..6f1c4a950
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal4.tests
@@ -0,0 +1,5 @@
1#!/bin/sh
2
3trap "echo Trapped" BADNAME TERM; echo $?
4kill $$
5echo Ok
diff --git a/shell/hush_test/hush-signals/signal8.right b/shell/hush_test/hush-signals/signal8.right
new file mode 100644
index 000000000..39572f30e
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal8.right
@@ -0,0 +1,3 @@
1Removing traps
2End of exit_func
3Done: 0
diff --git a/shell/hush_test/hush-signals/signal8.tests b/shell/hush_test/hush-signals/signal8.tests
new file mode 100755
index 000000000..731af7477
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal8.tests
@@ -0,0 +1,18 @@
1"$THIS_SH" -c '
2exit_func() {
3 echo "Removing traps"
4 trap - EXIT TERM INT
5 echo "End of exit_func"
6}
7set -e
8trap exit_func EXIT TERM INT
9sleep 2
10exit 77
11' &
12
13sleep 1
14# BUG: ash kills -PGRP, but in non-interactive shell we do not create pgrps!
15# In this case, bash kills by PID, not PGRP.
16kill -TERM %1
17wait
18echo Done: $?
diff --git a/shell/hush_test/hush-signals/signal9.right b/shell/hush_test/hush-signals/signal9.right
new file mode 100644
index 000000000..39572f30e
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal9.right
@@ -0,0 +1,3 @@
1Removing traps
2End of exit_func
3Done: 0
diff --git a/shell/hush_test/hush-signals/signal9.tests b/shell/hush_test/hush-signals/signal9.tests
new file mode 100755
index 000000000..18e71012b
--- /dev/null
+++ b/shell/hush_test/hush-signals/signal9.tests
@@ -0,0 +1,21 @@
1# Note: the inner script is a test which checks for a different bug
2# (ordering between INT handler and exit on "set -e"),
3# but so far I did not figure out how to simulate it non-interactively.
4
5"$THIS_SH" -c '
6exit_func() {
7 echo "Removing traps"
8 trap - EXIT TERM INT
9 echo "End of exit_func"
10}
11set -e
12trap exit_func EXIT TERM INT
13sleep 2
14exit 77
15' &
16
17child=$!
18sleep 1
19kill -TERM $child
20wait
21echo Done: $?
diff --git a/shell/hush_test/hush-vars/readonly0.right b/shell/hush_test/hush-vars/readonly0.right
new file mode 100644
index 000000000..8b750eb5f
--- /dev/null
+++ b/shell/hush_test/hush-vars/readonly0.right
@@ -0,0 +1,20 @@
1readonly a=A
2readonly b=B
3Ok:0
4
5hush: a=A: readonly variable
6Fail:1
7hush: a=A: readonly variable
8Fail:1
9
10hush: a=Z: readonly variable
11Fail:1
12
13hush: a=Z: readonly variable
14b=B
15^^^a is not exported
16hush: a=Z: readonly variable
17Visible:42
18
19hush: a: readonly variable
20Fail:1
diff --git a/shell/hush_test/hush-vars/readonly0.tests b/shell/hush_test/hush-vars/readonly0.tests
new file mode 100755
index 000000000..0833ccf29
--- /dev/null
+++ b/shell/hush_test/hush-vars/readonly0.tests
@@ -0,0 +1,42 @@
1unset a b
2#
3readonly a=A
4b=B
5readonly b
6# readonly on already readonly var is harmless:
7readonly b a
8readonly | grep '^readonly [ab]='
9# this should work:
10export a b
11export -n a b
12echo Ok:$?
13env | grep -e^a= -e^b= # shows nothing
14
15echo
16# these should all fail (despite the same value being assigned)
17# bash does not abort even in non-interactive more (in script)
18true; a=A
19echo Fail:$?
20true; readonly a=A
21echo Fail:$?
22
23echo
24# in bash, assignment in export fails, but export succeeds! :)
25# we don't mimic that!
26true; export a=Z
27echo Fail:$?
28#env | grep '^a='
29#echo "^^^a is exported"
30export -n a # undo that bashism, if it happens
31
32echo
33export b
34# this fails to both set and export a:
35a=Z env | grep '^[ab]='
36echo "^^^a is not exported"
37# but external command does get executed, and $? is not mangled (stays 42):
38(exit 42); a=Z env echo Visible:$?
39
40echo
41true; unset a
42echo Fail:$?
diff --git a/shell/hush_test/hush-vars/readonly2.right b/shell/hush_test/hush-vars/readonly2.right
new file mode 100644
index 000000000..38551d4ad
--- /dev/null
+++ b/shell/hush_test/hush-vars/readonly2.right
@@ -0,0 +1,5 @@
1hush: a=Z: readonly variable
2Visible:42
3
4hush: a=Z: readonly variable
5Visible:42
diff --git a/shell/hush_test/hush-vars/readonly2.tests b/shell/hush_test/hush-vars/readonly2.tests
new file mode 100755
index 000000000..b758d9602
--- /dev/null
+++ b/shell/hush_test/hush-vars/readonly2.tests
@@ -0,0 +1,7 @@
1unset a
2readonly a=A
3
4# external commands and builtins should behave the same:
5(exit 42); a=Z echo "Visible:$?"
6echo
7(exit 42); a=Z env echo "Visible:$?"
diff --git a/shell/hush_test/hush-vars/var-utf8-length.right b/shell/hush_test/hush-vars/var-utf8-length.right
new file mode 100644
index 000000000..6f4247a62
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-utf8-length.right
@@ -0,0 +1 @@
26
diff --git a/shell/hush_test/hush-vars/var-utf8-length.tests b/shell/hush_test/hush-vars/var-utf8-length.tests
new file mode 100755
index 000000000..b6e87f191
--- /dev/null
+++ b/shell/hush_test/hush-vars/var-utf8-length.tests
@@ -0,0 +1,4 @@
1LANG=en_US.UTF-8
2LC_ALL=en_US.UTF-8
3X=abcdÉfghÍjklmnÓpqrstÚvwcyz
4echo ${#X}
diff --git a/shell/hush_test/hush-vars/var_bash1a.right b/shell/hush_test/hush-vars/var_bash1a.right
new file mode 100644
index 000000000..1965b5c6c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1a.right
@@ -0,0 +1,6 @@
1parameter 'abcdef'
2varoffset2 'cdef'
3varoffset-2 'ef'
4literal '2' 'cdef'
5literal '-2' 'abcdef'
6literal ' -2' 'ef'
diff --git a/shell/hush_test/hush-vars/var_bash1a.tests b/shell/hush_test/hush-vars/var_bash1a.tests
new file mode 100755
index 000000000..c75c2dc53
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1a.tests
@@ -0,0 +1,11 @@
1parameter=abcdef
2offset=2
3noffset=-2
4echo "parameter '${parameter}'"
5echo "varoffset2 '${parameter:${offset}}'"
6echo "varoffset-2 '${parameter:${noffset}}'"
7echo "literal '2' '${parameter:2}'"
8# This is not interpreted as ${VAR:POS{:LEN}},
9# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD
10echo "literal '-2' '${parameter:-2}'"
11echo "literal ' -2' '${parameter: -2}'"
diff --git a/shell/hush_test/hush-vars/var_bash1b.right b/shell/hush_test/hush-vars/var_bash1b.right
new file mode 100644
index 000000000..fafc0f07c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1b.right
@@ -0,0 +1,23 @@
1all |0123456
24: |456
34:2 |45
44:-1 |45
54:-2 |4
64:-3 |
7-4: |3456
8-4:2 |34
9-4:-1 |345
10-4:-2 |34
11-4:-3 |3
12-4:-4 |
13-4:i=2 |34
14-4:i=-2|34
15-4:i=-3|3
16-4:i=-4|
17-5: |23456
18-6: |123456
19-7: |0123456
20-8: |
21-9: |
22-9:-99 |
23Ok:0
diff --git a/shell/hush_test/hush-vars/var_bash1b.tests b/shell/hush_test/hush-vars/var_bash1b.tests
new file mode 100755
index 000000000..efbdef35c
--- /dev/null
+++ b/shell/hush_test/hush-vars/var_bash1b.tests
@@ -0,0 +1,24 @@
1set -- 0123456
2 echo "all |"$1
3 echo "4: |"${1:4}
4 echo "4:2 |"${1:4:2}
5 echo "4:-1 |"${1:4:-1}
6 echo "4:-2 |"${1:4:-2}
7 echo "4:-3 |"${1:4:-3}
8 echo "-4: |"${1: -4}
9 echo "-4:2 |"${1: -4:2}
10 echo "-4:-1 |"${1: -4:-1}
11 echo "-4:-2 |"${1: -4:-2}
12 echo "-4:-3 |"${1: -4:-3}
13 echo "-4:-4 |"${1: -4:-4}
14i=2; echo "-4:i=2 |"${1: -4:i}
15i=-2; echo "-4:i=-2|"${1: -4:i}
16i=-3; echo "-4:i=-3|"${1: -4:i}
17i=-4; echo "-4:i=-4|"${1: -4:i}
18 echo "-5: |"${1: -5}
19 echo "-6: |"${1: -6}
20 echo "-7: |"${1: -7}
21 echo "-8: |"${1: -8}
22 echo "-9: |"${1: -9}
23 echo "-9:-99 |"${1: -9:-99}
24echo Ok:$?
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 837b3f7da..1dd0edc39 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -9,6 +9,8 @@ unset LC_NUMERIC
9unset LC_TIME 9unset LC_TIME
10unset LC_ALL 10unset LC_ALL
11 11
12TOPDIR=`pwd`
13
12if test ! -x hush; then 14if test ! -x hush; then
13 if test ! -x ../../busybox; then 15 if test ! -x ../../busybox; then
14 echo "Can't run tests. Put hush binary into this directory (`pwd`)" 16 echo "Can't run tests. Put hush binary into this directory (`pwd`)"
@@ -38,6 +40,8 @@ do_test()
38 test -d "$1" || return 0 40 test -d "$1" || return 0
39 d=${d%/} 41 d=${d%/}
40# echo Running tests in directory "$1" 42# echo Running tests in directory "$1"
43 # $1 but with / replaced by # so that it can be used as filename part
44 noslash=`echo "$1" | sed 's:/:#:g'`
41 ( 45 (
42 tret=0 46 tret=0
43 cd "$1" || { echo "cannot cd $1!"; exit 1; } 47 cd "$1" || { echo "cannot cd $1!"; exit 1; }
@@ -49,34 +53,35 @@ do_test()
49 #*) echo $x ; sh $x ;; 53 #*) echo $x ; sh $x ;;
50 *) 54 *)
51 echo -n "$1/$x:" 55 echo -n "$1/$x:"
52 sh "$x" >"../$1-$x.fail" 2>&1 && \ 56 sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \
53 { { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; } 57 { { echo " ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo " fail"; }
54 ;; 58 ;;
55 esac 59 esac
56 done 60 done
57 # Many bash run-XXX scripts just do this, 61 # Many bash run-XXX scripts just do this,
58 # no point in duplication it all over the place 62 # no point in duplication it all over the place
59 for x in *.tests; do 63 for x in *.tests; do
60 test -x "$x" || continue 64 test -x "$x" || continue
61 name="${x%%.tests}" 65 name="${x%%.tests}"
62 test -f "$name.right" || continue 66 test -f "$name.right" || continue
63# echo Running test: "$x" 67# echo Running test: "$x"
64 echo -n "$1/$x:" 68 echo -n "$1/$x:"
65 ( 69 (
66 "$THIS_SH" "./$x" >"$name.xx" 2>&1 70 "$THIS_SH" "./$x" >"$name.xx" 2>&1
67 r=$? 71 r=$?
68 # filter C library differences 72 # filter C library differences
69 sed -i \ 73 sed -i \
70 -e "/: invalid option /s:'::g" \ 74 -e "/: invalid option /s:'::g" \
71 "$name.xx" 75 "$name.xx"
72 test $r -eq 77 && rm -f "../$1-$x.fail" && exit 77 76 test $r -eq 77 && rm -f "$TOPDIR/$noslash-$x.fail" && exit 77
73 diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" 77 diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \
74 ) 78 && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail"
75 case $? in 79 )
76 0) echo " ok";; 80 case $? in
77 77) echo " skip (feature disabled)";; 81 0) echo " ok";;
78 *) echo " fail"; tret=1;; 82 77) echo " skip (feature disabled)";;
79 esac 83 *) echo " fail"; tret=1;;
84 esac
80 done 85 done
81 exit ${tret} 86 exit ${tret}
82 ) 87 )
@@ -92,7 +97,7 @@ if [ $# -lt 1 ]; then
92 modules=`ls -d hush-*` 97 modules=`ls -d hush-*`
93 98
94 for module in $modules; do 99 for module in $modules; do
95 do_test $module || ret=1 100 do_test $module || ret=1
96 done 101 done
97else 102else
98 while [ $# -ge 1 ]; do 103 while [ $# -ge 1 ]; do
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 55617b167..154b860f8 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -412,9 +412,7 @@ shell_builtin_ulimit(char **argv)
412 */ 412 */
413 GETOPT_RESET(); 413 GETOPT_RESET();
414 414
415 argc = 1; 415 argc = string_array_len(argv);
416 while (argv[argc])
417 argc++;
418 416
419 opts = 0; 417 opts = 0;
420 while (1) { 418 while (1) {
diff --git a/testsuite/msh/msh-supports-underscores-in-variable-names b/testsuite/msh/msh-supports-underscores-in-variable-names
deleted file mode 100644
index 9c7834b37..000000000
--- a/testsuite/msh/msh-supports-underscores-in-variable-names
+++ /dev/null
@@ -1 +0,0 @@
1test "`busybox msh -c 'FOO_BAR=foo; echo $FOO_BAR'`" = foo
diff --git a/util-linux/blkdiscard.c b/util-linux/blkdiscard.c
index af0bc946d..85039c5d0 100644
--- a/util-linux/blkdiscard.c
+++ b/util-linux/blkdiscard.c
@@ -28,6 +28,13 @@
28#include "libbb.h" 28#include "libbb.h"
29#include <linux/fs.h> 29#include <linux/fs.h>
30 30
31#ifndef BLKDISCARD
32#define BLKDISCARD 0x1277
33#endif
34#ifndef BLKSECDISCARD
35#define BLKSECDISCARD 0x127d
36#endif
37
31int blkdiscard_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 38int blkdiscard_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
32int blkdiscard_main(int argc UNUSED_PARAM, char **argv) 39int blkdiscard_main(int argc UNUSED_PARAM, char **argv)
33{ 40{
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index 916d4e30e..4467525c7 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -185,6 +185,8 @@ struct hd_geometry {
185 185
186#define HDIO_GETGEO 0x0301 /* get device geometry */ 186#define HDIO_GETGEO 0x0301 /* get device geometry */
187 187
188/* TODO: #if ENABLE_FEATURE_FDISK_WRITABLE */
189/* (currently fdisk_sun/sgi.c do not have proper WRITABLE #ifs) */
188static const char msg_building_new_label[] ALIGN1 = 190static const char msg_building_new_label[] ALIGN1 =
189"Building a new %s. Changes will remain in memory only,\n" 191"Building a new %s. Changes will remain in memory only,\n"
190"until you decide to write them. After that the previous content\n" 192"until you decide to write them. After that the previous content\n"
@@ -192,6 +194,7 @@ static const char msg_building_new_label[] ALIGN1 =
192 194
193static const char msg_part_already_defined[] ALIGN1 = 195static const char msg_part_already_defined[] ALIGN1 =
194"Partition %u is already defined, delete it before re-adding\n"; 196"Partition %u is already defined, delete it before re-adding\n";
197/* #endif */
195 198
196 199
197struct partition { 200struct partition {
diff --git a/util-linux/mkfs_ext2_test.sh b/util-linux/mkfs_ext2_test.sh
index f5347cc04..df22963f2 100755
--- a/util-linux/mkfs_ext2_test.sh
+++ b/util-linux/mkfs_ext2_test.sh
@@ -47,7 +47,7 @@ test_mke2fs() {
47kilobytes=60 47kilobytes=60
48while true; do 48while true; do
49 test_mke2fs || exit 1 49 test_mke2fs || exit 1
50 : $((kilobytes++)) 50 kilobytes=$((kilobytes+1))
51 test $kilobytes = 200 && break 51 test $kilobytes = 200 && break
52done 52done
53 53
@@ -56,7 +56,7 @@ done
56kilobytes=$((1 * 8*1024 - 50)) 56kilobytes=$((1 * 8*1024 - 50))
57while true; do 57while true; do
58 test_mke2fs || exit 1 58 test_mke2fs || exit 1
59 : $((kilobytes++)) 59 kilobytes=$((kilobytes+1))
60 test $kilobytes = $((1 * 8*1024 + 300)) && break 60 test $kilobytes = $((1 * 8*1024 + 300)) && break
61done 61done
62 62
@@ -65,7 +65,7 @@ done
65kilobytes=$((2 * 8*1024 - 50)) 65kilobytes=$((2 * 8*1024 - 50))
66while true; do 66while true; do
67 test_mke2fs || exit 1 67 test_mke2fs || exit 1
68 : $((kilobytes++)) 68 kilobytes=$((kilobytes+1))
69 test $kilobytes = $((2 * 8*1024 + 400)) && break 69 test $kilobytes = $((2 * 8*1024 + 400)) && break
70done 70done
71 71
@@ -74,7 +74,7 @@ done
74kilobytes=$((3 * 8*1024 - 50)) 74kilobytes=$((3 * 8*1024 - 50))
75while true; do 75while true; do
76 test_mke2fs || exit 1 76 test_mke2fs || exit 1
77 : $((kilobytes++)) 77 kilobytes=$((kilobytes+1))
78 test $kilobytes = $((3 * 8*1024 + 500)) && break 78 test $kilobytes = $((3 * 8*1024 + 500)) && break
79done 79done
80 80
@@ -83,7 +83,7 @@ done
83kilobytes=$((4 * 8*1024 - 50)) 83kilobytes=$((4 * 8*1024 - 50))
84while true; do 84while true; do
85 test_mke2fs || exit 1 85 test_mke2fs || exit 1
86 : $((kilobytes++)) 86 kilobytes=$((kilobytes+1))
87 test $kilobytes = $((4 * 8*1024 + 600)) && break 87 test $kilobytes = $((4 * 8*1024 + 600)) && break
88done 88done
89 89
@@ -92,7 +92,7 @@ done
92kilobytes=$((5 * 8*1024 - 50)) 92kilobytes=$((5 * 8*1024 - 50))
93while true; do 93while true; do
94 test_mke2fs || exit 1 94 test_mke2fs || exit 1
95 : $((kilobytes++)) 95 kilobytes=$((kilobytes+1))
96 test $kilobytes = $((5 * 8*1024 + 700)) && break 96 test $kilobytes = $((5 * 8*1024 + 700)) && break
97done 97done
98exit 98exit
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 6bb18524d..5fcc7958c 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -246,7 +246,8 @@
246/* This is just a warning of a common mistake. Possibly this should be a 246/* This is just a warning of a common mistake. Possibly this should be a
247 * uclibc faq entry rather than in busybox... */ 247 * uclibc faq entry rather than in busybox... */
248# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) 248# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
249# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support" 249# warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
250 /* not #error, since user may be using e.g. libtirpc instead */
250# endif 251# endif
251# include <rpc/rpc.h> 252# include <rpc/rpc.h>
252# include <rpc/pmap_prot.h> 253# include <rpc/pmap_prot.h>
diff --git a/util-linux/setpriv.c b/util-linux/setpriv.c
new file mode 100644
index 000000000..3d8dfea52
--- /dev/null
+++ b/util-linux/setpriv.c
@@ -0,0 +1,451 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * setpriv implementation for busybox based on linux-utils-ng 2.29
4 *
5 * Copyright (C) 2017 by <assafgordon@gmail.com>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 *
9 */
10//config:config SETPRIV
11//config: bool "setpriv"
12//config: default y
13//config: select PLATFORM_LINUX
14//config: select LONG_OPTS
15//config: help
16//config: Run a program with different Linux privilege settings.
17//config: Requires kernel >= 3.5
18//config:
19//config:config FEATURE_SETPRIV_DUMP
20//config: bool "Support dumping current privilege state"
21//config: default y
22//config: depends on SETPRIV
23//config: help
24//config: Enables the "--dump" switch to print out the current privilege
25//config: state. This is helpful for diagnosing problems.
26//config:
27//config:config FEATURE_SETPRIV_CAPABILITIES
28//config: bool "Support capabilities"
29//config: default y
30//config: depends on SETPRIV
31//config: help
32//config: Capabilities can be used to grant processes additional rights
33//config: without the necessity to always execute as the root user.
34//config: Enabling this option enables "--dump" to show information on
35//config: capabilities.
36//config:
37//config:config FEATURE_SETPRIV_CAPABILITY_NAMES
38//config: bool "Support capability names"
39//config: default y
40//config: depends on SETPRIV && FEATURE_SETPRIV_CAPABILITIES
41//config: help
42//config: Capabilities can be either referenced via a human-readble name,
43//config: e.g. "net_admin", or using their index, e.g. "cap_12". Enabling
44//config: this option allows using the human-readable names in addition to
45//config: the index-based names.
46
47//applet:IF_SETPRIV(APPLET(setpriv, BB_DIR_BIN, BB_SUID_DROP))
48
49//kbuild:lib-$(CONFIG_SETPRIV) += setpriv.o
50
51//usage:#define setpriv_trivial_usage
52//usage: "[OPTIONS] PROG [ARGS]"
53//usage:#define setpriv_full_usage "\n\n"
54//usage: "Run PROG with different privilege settings\n"
55//usage: IF_FEATURE_SETPRIV_DUMP(
56//usage: "\n-d,--dump Show current capabilities"
57//usage: )
58//usage: "\n--nnp,--no-new-privs Ignore setuid/setgid bits and file capabilities"
59//usage: IF_FEATURE_SETPRIV_CAPABILITIES(
60//usage: "\n--inh-caps CAP,CAP Set inheritable capabilities"
61//usage: "\n--ambient-caps CAP,CAP Set ambient capabilities"
62//usage: )
63
64//setpriv from util-linux 2.28:
65// -d, --dump show current state (and do not exec anything)
66// --nnp, --no-new-privs disallow granting new privileges
67// --inh-caps <caps,...> set inheritable capabilities
68// --bounding-set <caps> set capability bounding set
69// --ruid <uid> set real uid
70// --euid <uid> set effective uid
71// --rgid <gid> set real gid
72// --egid <gid> set effective gid
73// --reuid <uid> set real and effective uid
74// --regid <gid> set real and effective gid
75// --clear-groups clear supplementary groups
76// --keep-groups keep supplementary groups
77// --groups <group,...> set supplementary groups
78// --securebits <bits> set securebits
79// --selinux-label <label> set SELinux label
80// --apparmor-profile <pr> set AppArmor profile
81
82#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
83#include <linux/capability.h>
84// #include <sys/capability.h>
85// This header is in libcap, but the functions are in libc.
86// Comment in the header says this above capset/capget:
87/* system calls - look to libc for function to system call mapping */
88extern int capset(cap_user_header_t header, cap_user_data_t data);
89extern int capget(cap_user_header_t header, const cap_user_data_t data);
90// so for bbox, let's just repeat the declarations.
91// This way, libcap needs not be installed in build environment.
92#endif
93#include <sys/prctl.h>
94#include "libbb.h"
95
96#ifndef PR_CAPBSET_READ
97#define PR_CAPBSET_READ 23
98#endif
99
100#ifndef PR_SET_NO_NEW_PRIVS
101#define PR_SET_NO_NEW_PRIVS 38
102#endif
103
104#ifndef PR_GET_NO_NEW_PRIVS
105#define PR_GET_NO_NEW_PRIVS 39
106#endif
107
108#ifndef PR_CAP_AMBIENT
109#define PR_CAP_AMBIENT 47
110#define PR_CAP_AMBIENT_IS_SET 1
111#define PR_CAP_AMBIENT_RAISE 2
112#define PR_CAP_AMBIENT_LOWER 3
113#endif
114
115enum {
116 IF_FEATURE_SETPRIV_DUMP(OPTBIT_DUMP,)
117 IF_FEATURE_SETPRIV_CAPABILITIES(OPTBIT_INH,)
118 IF_FEATURE_SETPRIV_CAPABILITIES(OPTBIT_AMB,)
119 OPTBIT_NNP,
120
121 IF_FEATURE_SETPRIV_DUMP(OPT_DUMP = (1 << OPTBIT_DUMP),)
122 IF_FEATURE_SETPRIV_CAPABILITIES(OPT_INH = (1 << OPTBIT_INH),)
123 IF_FEATURE_SETPRIV_CAPABILITIES(OPT_AMB = (1 << OPTBIT_AMB),)
124 OPT_NNP = (1 << OPTBIT_NNP),
125};
126
127#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
128struct caps {
129 struct __user_cap_header_struct header;
130 cap_user_data_t data;
131 int u32s;
132};
133
134# if ENABLE_FEATURE_SETPRIV_CAPABILITY_NAMES
135static const char *const capabilities[] = {
136 "chown",
137 "dac_override",
138 "dac_read_search",
139 "fowner",
140 "fsetid",
141 "kill",
142 "setgid",
143 "setuid",
144 "setpcap",
145 "linux_immutable",
146 "net_bind_service",
147 "net_broadcast",
148 "net_admin",
149 "net_raw",
150 "ipc_lock",
151 "ipc_owner",
152 "sys_module",
153 "sys_rawio",
154 "sys_chroot",
155 "sys_ptrace",
156 "sys_pacct",
157 "sys_admin",
158 "sys_boot",
159 "sys_nice",
160 "sys_resource",
161 "sys_time",
162 "sys_tty_config",
163 "mknod",
164 "lease",
165 "audit_write",
166 "audit_control",
167 "setfcap",
168 "mac_override",
169 "mac_admin",
170 "syslog",
171 "wake_alarm",
172 "block_suspend",
173 "audit_read",
174};
175# endif /* FEATURE_SETPRIV_CAPABILITY_NAMES */
176
177static void getcaps(struct caps *caps)
178{
179 static const uint8_t versions[] = {
180 _LINUX_CAPABILITY_U32S_3, /* = 2 (fits into byte) */
181 _LINUX_CAPABILITY_U32S_2, /* = 2 */
182 _LINUX_CAPABILITY_U32S_1, /* = 1 */
183 };
184 int i;
185
186 caps->header.pid = 0;
187 for (i = 0; i < ARRAY_SIZE(versions); i++) {
188 caps->header.version = versions[i];
189 if (capget(&caps->header, NULL) == 0)
190 goto got_it;
191 }
192 bb_simple_perror_msg_and_die("capget");
193 got_it:
194
195 switch (caps->header.version) {
196 case _LINUX_CAPABILITY_VERSION_1:
197 caps->u32s = _LINUX_CAPABILITY_U32S_1;
198 break;
199 case _LINUX_CAPABILITY_VERSION_2:
200 caps->u32s = _LINUX_CAPABILITY_U32S_2;
201 break;
202 case _LINUX_CAPABILITY_VERSION_3:
203 caps->u32s = _LINUX_CAPABILITY_U32S_3;
204 break;
205 default:
206 bb_error_msg_and_die("unsupported capability version");
207 }
208
209 caps->data = xmalloc(sizeof(caps->data[0]) * caps->u32s);
210 if (capget(&caps->header, caps->data) < 0)
211 bb_simple_perror_msg_and_die("capget");
212}
213
214static void parse_cap(unsigned long *index, const char *cap)
215{
216 unsigned long i;
217
218 switch (cap[0]) {
219 case '-':
220 break;
221 case '+':
222 break;
223 default:
224 bb_error_msg_and_die("invalid capability '%s'", cap);
225 break;
226 }
227
228 cap++;
229 if ((sscanf(cap, "cap_%lu", &i)) == 1) {
230 if (!cap_valid(i))
231 bb_error_msg_and_die("unsupported capability '%s'", cap);
232 *index = i;
233 return;
234 }
235
236# if ENABLE_FEATURE_SETPRIV_CAPABILITY_NAMES
237 for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
238 if (strcmp(capabilities[i], cap) != 0)
239 continue;
240
241 if (!cap_valid(i))
242 bb_error_msg_and_die("unsupported capability '%s'", cap);
243 *index = i;
244 return;
245 }
246# endif
247
248 bb_error_msg_and_die("unknown capability '%s'", cap);
249}
250
251static void set_inh_caps(char *capstring)
252{
253 struct caps caps;
254
255 getcaps(&caps);
256
257 capstring = strtok(capstring, ",");
258 while (capstring) {
259 unsigned long cap;
260
261 parse_cap(&cap, capstring);
262 if (CAP_TO_INDEX(cap) >= caps.u32s)
263 bb_error_msg_and_die("invalid capability cap");
264
265 if (capstring[0] == '+')
266 caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap);
267 else
268 caps.data[CAP_TO_INDEX(cap)].inheritable &= ~CAP_TO_MASK(cap);
269 capstring = strtok(NULL, ",");
270 }
271
272 if ((capset(&caps.header, caps.data)) < 0)
273 bb_perror_msg_and_die("capset");
274
275 if (ENABLE_FEATURE_CLEAN_UP)
276 free(caps.data);
277}
278
279static void set_ambient_caps(char *string)
280{
281 char *cap;
282
283 cap = strtok(string, ",");
284 while (cap) {
285 unsigned long index;
286
287 parse_cap(&index, cap);
288 if (cap[0] == '+') {
289 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, index, 0, 0) < 0)
290 bb_perror_msg("cap_ambient_raise");
291 } else {
292 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, index, 0, 0) < 0)
293 bb_perror_msg("cap_ambient_lower");
294 }
295 cap = strtok(NULL, ",");
296 }
297}
298#endif /* FEATURE_SETPRIV_CAPABILITIES */
299
300#if ENABLE_FEATURE_SETPRIV_DUMP
301# if ENABLE_FEATURE_SETPRIV_CAPABILITY_NAMES
302static void printf_cap(const char *pfx, unsigned cap_no)
303{
304 if (cap_no < ARRAY_SIZE(capabilities)) {
305 printf("%s%s", pfx, capabilities[cap_no]);
306 return;
307 }
308 printf("%scap_%u", pfx, cap_no);
309}
310# else
311# define printf_cap(pfx, cap_no) printf("%scap_%u", (pfx), (cap_no))
312# endif
313
314static int dump(void)
315{
316 IF_FEATURE_SETPRIV_CAPABILITIES(struct caps caps;)
317 const char *fmt;
318 uid_t ruid, euid, suid;
319 gid_t rgid, egid, sgid;
320 gid_t *gids;
321 int i, ngids, nnp;
322
323 getresuid(&ruid, &euid, &suid); /* never fails in Linux */
324 getresgid(&rgid, &egid, &sgid); /* never fails in Linux */
325 ngids = 0;
326 gids = bb_getgroups(&ngids, NULL); /* never fails in Linux */
327
328 nnp = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
329 if (nnp < 0)
330 bb_perror_msg_and_die("prctl: %s", "GET_NO_NEW_PRIVS");
331
332 printf("uid: %u\n", (unsigned)ruid);
333 printf("euid: %u\n", (unsigned)euid);
334 printf("gid: %u\n", (unsigned)rgid);
335 printf("egid: %u\n", (unsigned)egid);
336
337 printf("Supplementary groups: ");
338 if (ngids == 0) {
339 printf("[none]");
340 } else {
341 fmt = ",%u" + 1;
342 for (i = 0; i < ngids; i++) {
343 printf(fmt, (unsigned)gids[i]);
344 fmt = ",%u";
345 }
346 }
347 printf("\nno_new_privs: %d\n", nnp);
348
349# if ENABLE_FEATURE_SETPRIV_CAPABILITIES
350 getcaps(&caps);
351 printf("Inheritable capabilities: ");
352 fmt = "";
353 for (i = 0; cap_valid(i); i++) {
354 unsigned idx = CAP_TO_INDEX(i);
355 if (idx >= caps.u32s) {
356 printf("\nindex: %u u32s: %u capability: %u\n", idx, caps.u32s, i);
357 bb_error_msg_and_die("unsupported capability");
358 }
359 if (caps.data[idx].inheritable & CAP_TO_MASK(i)) {
360 printf_cap(fmt, i);
361 fmt = ",";
362 }
363 }
364 if (!fmt[0])
365 printf("[none]");
366
367 printf("\nAmbient capabilities: ");
368 fmt = "";
369 for (i = 0; cap_valid(i); i++) {
370 int ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, (unsigned long) i, 0UL, 0UL);
371 if (ret < 0)
372 bb_perror_msg_and_die("prctl: %s", "CAP_AMBIENT_IS_SET");
373 if (ret) {
374 printf_cap(fmt, i);
375 fmt = ",";
376 }
377 }
378 if (i == 0)
379 printf("[unsupported]");
380 else if (!fmt[0])
381 printf("[none]");
382
383 printf("\nCapability bounding set: ");
384 fmt = "";
385 for (i = 0; cap_valid(i); i++) {
386 int ret = prctl(PR_CAPBSET_READ, (unsigned long) i, 0UL, 0UL, 0UL);
387 if (ret < 0)
388 bb_perror_msg_and_die("prctl: %s", "CAPBSET_READ");
389 if (ret) {
390 printf_cap(fmt, i);
391 fmt = ",";
392 }
393 }
394 if (!fmt[0])
395 printf("[none]");
396 bb_putchar('\n');
397# endif
398
399 if (ENABLE_FEATURE_CLEAN_UP) {
400 IF_FEATURE_SETPRIV_CAPABILITIES(free(caps.data);)
401 free(gids);
402 }
403 return EXIT_SUCCESS;
404}
405#endif /* FEATURE_SETPRIV_DUMP */
406
407int setpriv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
408int setpriv_main(int argc UNUSED_PARAM, char **argv)
409{
410 static const char setpriv_longopts[] ALIGN1 =
411 IF_FEATURE_SETPRIV_DUMP(
412 "dump\0" No_argument "d"
413 )
414 "nnp\0" No_argument "\xff"
415 "no-new-privs\0" No_argument "\xff"
416 IF_FEATURE_SETPRIV_CAPABILITIES(
417 "inh-caps\0" Required_argument "\xfe"
418 "ambient-caps\0" Required_argument "\xfd"
419 )
420 ;
421 int opts;
422 IF_FEATURE_SETPRIV_CAPABILITIES(char *inh_caps, *ambient_caps;)
423
424 applet_long_options = setpriv_longopts;
425 opts = getopt32(argv, "+"IF_FEATURE_SETPRIV_DUMP("d")
426 IF_FEATURE_SETPRIV_CAPABILITIES("\xfe:\xfd:", &inh_caps, &ambient_caps));
427 argv += optind;
428
429#if ENABLE_FEATURE_SETPRIV_DUMP
430 if (opts & OPT_DUMP) {
431 if (argv[0] || (opts - OPT_DUMP) != 0)
432 bb_show_usage();
433 return dump();
434 }
435#endif
436 if (opts & OPT_NNP) {
437 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
438 bb_perror_msg_and_die("prctl: %s", "SET_NO_NEW_PRIVS");
439 }
440
441#if ENABLE_FEATURE_SETPRIV_CAPABILITIES
442 if (opts & OPT_INH)
443 set_inh_caps(inh_caps);
444 if (opts & OPT_AMB)
445 set_ambient_caps(ambient_caps);
446#endif
447
448 if (!argv[0])
449 bb_show_usage();
450 BB_EXECVP_or_die(argv);
451}
diff --git a/util-linux/unshare.c b/util-linux/unshare.c
index 52e8f1421..875e3f86e 100644
--- a/util-linux/unshare.c
+++ b/util-linux/unshare.c
@@ -9,12 +9,13 @@
9//config:config UNSHARE 9//config:config UNSHARE
10//config: bool "unshare" 10//config: bool "unshare"
11//config: default y 11//config: default y
12//config: depends on LONG_OPTS && !NOMMU 12//config: depends on !NOMMU
13//config: select PLATFORM_LINUX 13//config: select PLATFORM_LINUX
14//config: select LONG_OPTS
14//config: help 15//config: help
15//config: Run program with some namespaces unshared from parent. 16//config: Run program with some namespaces unshared from parent.
16 17
17// depends on LONG_OPTS: it is awkward to exclude code which handles --propagation 18// needs LONG_OPTS: it is awkward to exclude code which handles --propagation
18// and --setgroups based on LONG_OPTS, so instead applet requires LONG_OPTS. 19// and --setgroups based on LONG_OPTS, so instead applet requires LONG_OPTS.
19// depends on !NOMMU: we need fork() 20// depends on !NOMMU: we need fork()
20 21
@@ -32,7 +33,7 @@
32//usage: "\n -p,--pid[=FILE] Unshare PID namespace" 33//usage: "\n -p,--pid[=FILE] Unshare PID namespace"
33//usage: "\n -U,--user[=FILE] Unshare user namespace" 34//usage: "\n -U,--user[=FILE] Unshare user namespace"
34//usage: "\n -f,--fork Fork before execing PROG" 35//usage: "\n -f,--fork Fork before execing PROG"
35//usage: "\n -r,--map-root-user Map current user to root (implies -u)" 36//usage: "\n -r,--map-root-user Map current user to root (implies -U)"
36//usage: "\n --mount-proc[=DIR] Mount /proc filesystem first (implies -m)" 37//usage: "\n --mount-proc[=DIR] Mount /proc filesystem first (implies -m)"
37//usage: "\n --propagation slave|shared|private|unchanged" 38//usage: "\n --propagation slave|shared|private|unchanged"
38//usage: "\n Modify mount propagation in mount namespace" 39//usage: "\n Modify mount propagation in mount namespace"