diff options
157 files changed, 3033 insertions, 1220 deletions
@@ -26,10 +26,14 @@ endchoice | |||
26 | menu "Busybox Settings" | 26 | menu "Busybox Settings" |
27 | 27 | ||
28 | config DESKTOP | 28 | config 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 | ||
59 | config USE_PORTABLE_CODE | 63 | config 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 | ||
67 | config SHOW_USAGE | 70 | config 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 | ||
101 | config BUSYBOX | 104 | config 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 | ||
112 | config FEATURE_INSTALLER | 115 | config 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 | ||
121 | config INSTALL_NO_USR | 127 | config 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 | ||
129 | config PAM | 137 | config 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 | ||
136 | config 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 | |||
143 | config FEATURE_DEVPTS | 144 | config 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 | ||
153 | config 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 | |||
165 | config FEATURE_UTMP | 154 | config 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 | ||
193 | config PID_FILE_PATH | 182 | config 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 | ||
192 | config 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 | |||
207 | config 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 | |||
216 | config 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 | |||
203 | config FEATURE_SUID | 224 | config 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 | ||
233 | config FEATURE_SUID_CONFIG | 254 | config 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 | ||
286 | config 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 | |||
304 | config FEATURE_PREFER_APPLETS | 307 | config 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 | ||
332 | config 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 | |||
351 | config 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 | ||
330 | config FEATURE_SYSLOG | 365 | config 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 | 450 | config 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 | ||
427 | config FEATURE_INDIVIDUAL | 458 | config 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 | ||
473 | config 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 | |||
484 | config 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 | |||
496 | config 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 | |||
506 | config CROSS_COMPILER_PREFIX | 504 | config 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 | ||
550 | config 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 | |||
552 | comment 'Installation Options ("make install" behavior)' | 558 | comment 'Installation Options ("make install" behavior)' |
553 | 559 | ||
554 | choice | 560 | choice |
@@ -709,10 +715,10 @@ config EFENCE | |||
709 | 715 | ||
710 | endchoice | 716 | endchoice |
711 | 717 | ||
712 | endmenu | ||
713 | |||
714 | source libbb/Config.in | 718 | source libbb/Config.in |
715 | 719 | ||
720 | endmenu | ||
721 | |||
716 | comment "Applets" | 722 | comment "Applets" |
717 | 723 | ||
718 | source archival/Config.in | 724 | source archival/Config.in |
@@ -1,5 +1,5 @@ | |||
1 | VERSION = 1 | 1 | VERSION = 1 |
2 | PATCHLEVEL = 27 | 2 | PATCHLEVEL = 28 |
3 | SUBLEVEL = 0 | 3 | SUBLEVEL = 0 |
4 | EXTRAVERSION = .git | 4 | EXTRAVERSION = .git |
5 | NAME = Unnamed | 5 | NAME = 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 |
514 | include $(srctree)/Makefile.flags | 516 | include $(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 |
534 | all: busybox$(EXEEXT) doc | 536 | all: 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) |
540 | CHECKFLAGS += $(NOSTDINC_FLAGS) | 540 | CHECKFLAGS += $(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 | ||
21 | CFLAGS += $(call cc-option,-Wall,) | 20 | CFLAGS += $(call cc-option,-Wall,) |
22 | CFLAGS += $(call cc-option,-Wshadow,) | 21 | CFLAGS += $(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 */ |
20 | enum { | 25 | enum { |
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) | |||
242 | int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 251 | int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
243 | int uncompress_main(int argc UNUSED_PARAM, char **argv) | 252 | int 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 |
434 | int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 454 | int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
435 | int bunzip2_main(int argc UNUSED_PARAM, char **argv) | 455 | int 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) | |||
522 | int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 544 | int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
523 | int unlzma_main(int argc UNUSED_PARAM, char **argv) | 545 | int 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) | |||
588 | int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 612 | int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
589 | int unxz_main(int argc UNUSED_PARAM, char **argv) | 613 | int 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 | ||
315 | struct globals { | 316 | struct 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 */ |
478 | enum { | 485 | enum { |
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 | |||
891 | CONFIG_HUSH_LOCAL=y | 891 | CONFIG_HUSH_LOCAL=y |
892 | CONFIG_HUSH_EXPORT_N=y | 892 | CONFIG_HUSH_EXPORT_N=y |
893 | CONFIG_HUSH_RANDOM_SUPPORT=y | 893 | CONFIG_HUSH_RANDOM_SUPPORT=y |
894 | CONFIG_MSH=y | ||
895 | CONFIG_SH_MATH_SUPPORT=y | 894 | CONFIG_SH_MATH_SUPPORT=y |
896 | CONFIG_SH_MATH_SUPPORT_64=y | 895 | CONFIG_SH_MATH_SUPPORT_64=y |
897 | CONFIG_FEATURE_SH_EXTRA_QUIET=y | 896 | CONFIG_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 |
900 | CONFIG_FEATURE_BASH_IS_NONE=y | 900 | CONFIG_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 | |||
905 | CONFIG_HUSH_LOCAL=y | 905 | CONFIG_HUSH_LOCAL=y |
906 | CONFIG_HUSH_EXPORT_N=y | 906 | CONFIG_HUSH_EXPORT_N=y |
907 | CONFIG_HUSH_RANDOM_SUPPORT=y | 907 | CONFIG_HUSH_RANDOM_SUPPORT=y |
908 | CONFIG_MSH=y | ||
909 | CONFIG_SH_MATH_SUPPORT=y | 908 | CONFIG_SH_MATH_SUPPORT=y |
910 | CONFIG_SH_MATH_SUPPORT_64=y | 909 | CONFIG_SH_MATH_SUPPORT_64=y |
911 | CONFIG_FEATURE_SH_EXTRA_QUIET=y | 910 | CONFIG_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 |
958 | CONFIG_FEATURE_SH_IS_NONE=y | 957 | CONFIG_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 | ||
1102 | CONFIG_FEATURE_SH_IS_ASH=y | 1101 | CONFIG_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 |
990 | CONFIG_FEATURE_SH_IS_NONE=y | 989 | CONFIG_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 |
1019 | CONFIG_FEATURE_SH_IS_NONE=y | 1018 | CONFIG_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 | |||
955 | CONFIG_HUSH_RANDOM_SUPPORT=y | 955 | CONFIG_HUSH_RANDOM_SUPPORT=y |
956 | CONFIG_HUSH_EXPORT_N=y | 956 | CONFIG_HUSH_EXPORT_N=y |
957 | CONFIG_HUSH_MODE_X=y | 957 | CONFIG_HUSH_MODE_X=y |
958 | # CONFIG_MSH is not set | ||
959 | CONFIG_FEATURE_SH_IS_ASH=y | 958 | CONFIG_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 | ||
935 | CONFIG_FEATURE_SH_IS_ASH=y | 934 | CONFIG_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) | ||
93 | static int catv(unsigned opts, char **argv) | 110 | static 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 | ||
142 | int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 168 | int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
143 | int cat_main(int argc UNUSED_PARAM, char **argv) | 169 | int 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 | ||
141 | static void dd_output_status(int UNUSED_PARAM cur_signal) | 140 | static 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 | ||
38 | void 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 | |||
58 | int nl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 38 | int nl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
59 | int nl_main(int argc UNUSED_PARAM, char **argv) | 39 | int 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 | |||
567 | static void initialize_group_array(void) | 566 | static 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. */ |
613 | static int test_eaccess(char *path, int mode) | 598 | static 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 | ||
95 | static const char options[] ALIGN1 = "snrvmpioa"; | 110 | #if ENABLE_UNAME |
111 | #define options "snrvmpioa" | ||
96 | static const unsigned short utsname_offset[] = { | 112 | static 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 | ||
107 | int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 124 | int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
108 | int uname_main(int argc UNUSED_PARAM, char **argv) | 125 | int 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 | ||
15 | while sleep 1; test $cnt != 0; do | 15 | while 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" | |||
30 | failcnt=0 | 30 | failcnt=0 |
31 | while true; do | 31 | while 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 | ||
802 | uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; | 802 | uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC; |
803 | 803 | ||
804 | /* 0 if argv[0] is NULL: */ | ||
805 | unsigned string_array_len(char **argv) FAST_FUNC; | ||
804 | void overlapping_strcpy(char *dst, const char *src) FAST_FUNC; | 806 | void overlapping_strcpy(char *dst, const char *src) FAST_FUNC; |
805 | char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC; | 807 | char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC; |
806 | char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC; | 808 | char *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 | */ | ||
1070 | gid_t *bb_getgroups(int *ngroups, gid_t *group_array) FAST_FUNC; | ||
1060 | 1071 | ||
1061 | #if ENABLE_FEATURE_UTMP | 1072 | #if ENABLE_FEATURE_UTMP |
1062 | void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); | 1073 | void 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; | |||
1132 | int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; | 1143 | int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; |
1133 | #ifndef BUILD_INDIVIDUAL | 1144 | #ifndef BUILD_INDIVIDUAL |
1134 | extern int find_applet_by_name(const char *name) FAST_FUNC; | 1145 | extern int find_applet_by_name(const char *name) FAST_FUNC; |
1135 | extern void run_applet_no_and_exit(int a, char **argv) NORETURN FAST_FUNC; | 1146 | extern 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 | ||
6 | menu "Busybox Library Tuning" | 6 | comment "Library Tuning" |
7 | 7 | ||
8 | INSERT | 8 | INSERT |
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 | ||
72 | config FEATURE_ETC_NETWORKS | 72 | config 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 | ||
314 | config FEATURE_USE_SENDFILE | 318 | config 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 | |||
382 | endmenu | ||
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 | ||
81 | unsigned 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 |
82 | static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; | 93 | static 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 |
922 | void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv) | 946 | void 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 | |||
13 | gid_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 | ||
314 | static void do_skip(priv_dumper_t *dumper, const char *fname, int statok) | 314 | static 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 | ||
337 | static NOINLINE int next(priv_dumper_t *dumper) | 335 | static 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 | |||
14 | int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) | 20 | int 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) | |||
183 | char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) | 177 | char* 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 ")" | 20 | const char bb_banner[] ALIGN1 = "BusyBox v" BB_VER BB_EXTRA_VERSION; |
21 | |||
22 | const char bb_banner[] ALIGN1 = BANNER; | ||
23 | 21 | ||
24 | 22 | ||
25 | const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory"; | 23 | const 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) | |||
158 | int FAST_FUNC spawn_and_wait(char **argv) | 159 | int 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 | ||
667 | static void start_one_job(const char *user, CronLine *line) | 750 | static 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 | ||
751 | static void start_one_job(const char *user, CronLine *line) | 836 | static 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 | ||
837 | static void start_jobs(void) | 923 | #if ENABLE_FEATURE_CROND_SPECIAL_TIMES |
924 | static 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 | |||
936 | static 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 | ||
45 | static void watchdog_shutdown(int sig UNUSED_PARAM) | 58 | static 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 | ||
65 | static 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 | ||
72 | static 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 | |||
56 | int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 88 | int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
57 | int watchdog_main(int argc, char **argv) | 89 | int 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 | ||
78 | char* FAST_FUNC replace_underscores(char *s) | ||
79 | { | ||
80 | replace(s, '-', '_'); | ||
81 | return s; | ||
82 | } | ||
83 | |||
84 | int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim) | 78 | int 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; | |||
48 | void moddb_free(module_db *db) FAST_FUNC; | 48 | void moddb_free(module_db *db) FAST_FUNC; |
49 | 49 | ||
50 | void replace(char *s, char what, char with) FAST_FUNC; | 50 | void replace(char *s, char what, char with) FAST_FUNC; |
51 | char *replace_underscores(char *s) FAST_FUNC; | ||
52 | int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; | 51 | int string_to_llist(char *string, llist_t **llist, const char *delim) FAST_FUNC; |
53 | char *filename2modname(const char *filename, char *modname) FAST_FUNC; | 52 | char *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 | ||
1517 | static const char *const cat_args[] = { "cat", NULL }; | 1520 | static 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 */ |
1526 | static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM) | 1530 | static 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 | } |
1547 | static void FAST_FUNC echo_dg(int s, servtab_t *sep) | 1551 | static 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 */ |
1567 | static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM) | 1571 | static 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 */ |
1586 | static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM) | 1590 | static 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 */ |
25 | static void usage(void) __attribute__((noreturn)); | 29 | static const char keywords[] ALIGN1 = |
26 | 30 | "from\0""to\0""preference\0""order\0""priority\0" | |
27 | static 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); | 40 | enum { |
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 | ||
40 | static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM, | 47 | static 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 */ |
175 | static int iprule_modify(int cmd, char **argv) | 193 | static 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( \ | |||
228 | static int32 pstm_mul_comba_gen(psPool_t *pool, pstm_int *A, pstm_int *B, | 229 | static 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) \ |
84 | asm( \ | 85 | asm( \ |
@@ -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) \ |
96 | asm( \ | 98 | asm( \ |
@@ -445,7 +447,7 @@ asm( \ | |||
445 | static int32 pstm_sqr_comba_gen(psPool_t *pool, pstm_int *A, pstm_int *B, | 447 | static 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 | ||
193 | unsigned FAST_FUNC udhcp_option_idx(const char *name) | 195 | unsigned 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 */ |
319 | void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) | 327 | void 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 */ |
343 | struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code) | 352 | struct 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 | ||
454 | int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg) | 463 | int 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 | ||
192 | extern const struct dhcp_optflag dhcp_optflags[]; | 195 | extern const struct dhcp_optflag dhcp_optflags[]; |
193 | extern const char dhcp_option_strings[] ALIGN1; | 196 | extern const char dhcp_option_strings[] ALIGN1; |
197 | #endif | ||
194 | extern const uint8_t dhcp_option_lengths[] ALIGN1; | 198 | extern const uint8_t dhcp_option_lengths[] ALIGN1; |
195 | 199 | ||
196 | unsigned FAST_FUNC udhcp_option_idx(const char *name); | 200 | unsigned FAST_FUNC udhcp_option_idx(const char *name, const char *option_strings); |
197 | 201 | ||
198 | uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC; | 202 | uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC; |
199 | int udhcp_end_option(uint8_t *optionptr) FAST_FUNC; | 203 | int udhcp_end_option(uint8_t *optionptr) FAST_FUNC; |
200 | void udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt) FAST_FUNC; | 204 | void udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt) FAST_FUNC; |
205 | #if ENABLE_UDHCPC || ENABLE_UDHCPD | ||
201 | void udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) FAST_FUNC; | 206 | void 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 | ||
203 | char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC; | 209 | char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC; |
204 | uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) FAST_FUNC; | 210 | uint8_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*" */ |
285 | int FAST_FUNC udhcp_str2nip(const char *str, void *arg); | 291 | int FAST_FUNC udhcp_str2nip(const char *str, void *arg); |
286 | /* 2nd param is "struct option_set**" */ | 292 | /* 2nd param is "struct option_set**" */ |
287 | int FAST_FUNC udhcp_str2optset(const char *str, void *arg); | 293 | int 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 | ||
289 | void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC; | 299 | void udhcp_init_header(struct dhcp_packet *packet, char type) FAST_FUNC; |
300 | #endif | ||
290 | 301 | ||
291 | int udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd) FAST_FUNC; | 302 | int 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 | ||
94 | struct client6_data_t { | 133 | struct 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 | ||
63 | static 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 */ | ||
78 | static 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 |
42 | static const char udhcpc6_longopts[] ALIGN1 = | 93 | static 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 | ||
91 | static 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 | |||
99 | static const char opt_fqdn_req[] = { | 143 | static 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 | ||
139 | static 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 */ |
155 | static void option_to_env(uint8_t *option, uint8_t *option_end) | 198 | static 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 | ||
352 | static uint8_t *add_d6_client_options(uint8_t *ptr) | 426 | static 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 | ||
367 | static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end) | 457 | static 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 | ||
364 | static int FAST_FUNC read_optset(const char *line, void *arg) { | ||
365 | return udhcp_str2optset(line, arg, dhcp_optflags, dhcp_option_strings); | ||
366 | } | ||
367 | |||
364 | struct config_keyword { | 368 | struct 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 | ||
15 | void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type) | 16 | void 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 |
34 | void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet) | 36 | void 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 | ||
61 | enum { | 62 | enum { |
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 { | |||
82 | static void act(unsigned pid, char *cmd, int signo) | 85 | static 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 | ||
35 | struct child; | 35 | struct 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 | ||
99 | fi | 105 | fi |
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 | ||
3 | run_testsuite=false | ||
3 | run_testsuite=true | 4 | run_testsuite=true |
4 | 5 | ||
6 | run_single_test=false | ||
7 | run_single_test=true | ||
8 | |||
5 | test -d "$1" || { echo "'$1' is not a directory"; exit 1; } | 9 | test -d "$1" || { echo "'$1' is not a directory"; exit 1; } |
6 | test -x "$1/scripts/randomtest" || { echo "No scripts/randomtest in '$1'"; exit 1; } | 10 | test -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++ |
45 | done | 71 | done |
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 | ||
92 | START_GROUP="-Wl,--start-group" | 92 | START_GROUP="-Wl,--start-group" |
93 | END_GROUP="-Wl,--end-group" | 93 | END_GROUP="-Wl,--end-group" |
94 | INFO_OPTS="-Wl,--warn-common -Wl,-Map,$EXE.map -Wl,--verbose" | 94 | INFO_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) |
97 | SORT_COMMON="-Wl,--sort-common" | 99 | SORT_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... */ |
2571 | static const char *expandstr(const char *ps); | 2570 | static const char *expandstr(const char *ps); |
2572 | #else | ||
2573 | #define expandstr(s) s | ||
2574 | #endif | ||
2575 | 2571 | ||
2576 | static void | 2572 | static void |
2577 | setprompt_if(smallint do_set, int whichprompt) | 2573 | setprompt_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) | |||
7970 | static int | 8009 | static int |
7971 | patmatch(char *pattern, const char *string) | 8010 | patmatch(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 | ||
12937 | static const char * | 12971 | static const char * |
12938 | expandstr(const char *ps) | 12972 | expandstr(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" | 1 | qwe |
2 | asd | ||
3 | 123 | ||
4 | 456 | ||
5 | Ok | ||
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: | 1 | cat <<000; cat <<www; cat <<eee |
2 | 2 | 000 | |
3 | <<EOF; then <W | 3 | qwe |
4 | asd | ||
5 | www | ||
6 | 123 | ||
7 | 456 | ||
8 | eee | ||
9 | echo 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 @@ | |||
1 | hello | 1 | exit EOF-f |
2 | " | ||
3 | echo $f | ||
4 | echo `echo Hello World` | ||
5 | moo | ||
6 | EOF-f | ||
7 | EOF-f f | ||
8 | EOF-f | ||
9 | Ok | ||
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 @@ | |||
1 | echo hello >greeting | 1 | f=1 |
2 | cat <<EOF && | 2 | cat <<- EOF-f"" |
3 | $(cat greeting) | 3 | exit EOF-f |
4 | EOF | 4 | " |
5 | { | 5 | echo $f |
6 | echo $? | 6 | echo `echo Hello World` |
7 | cat greeting | 7 | moo |
8 | } >/dev/null | 8 | EOF-f |
9 | rm greeting | 9 | EOF-f f |
10 | EOF-f | ||
11 | EOF-f | ||
12 | echo 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 @@ | |||
1 | echo hello >greeting | ||
2 | cat <<EOF && | ||
3 | $(cat greeting) | ||
4 | EOF | ||
5 | { | ||
6 | echo $? | ||
7 | cat greeting | ||
8 | } >/dev/null | ||
9 | rm 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 @@ | |||
1 | Sending 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 | |||
41 | echo 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 @@ | |||
1 | 0 | ||
2 | 3 | ||
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 | ||
4 | echo $? | ||
5 | wait $! | ||
6 | echo $? | ||
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 @@ | |||
1 | ok | ||
2 | ok | ||
3 | ok | ||
4 | ok | ||
5 | ok | ||
6 | ok | ||
7 | ok | ||
8 | ok | ||
9 | ok | ||
10 | ok | ||
11 | ok | ||
12 | ok | ||
13 | ok | ||
14 | ok | ||
15 | ok | ||
16 | ok | ||
17 | ok | ||
18 | ok | ||
19 | ok | ||
20 | ok | ||
21 | ok | ||
22 | ok | ||
23 | ok | ||
24 | ok | ||
25 | ok | ||
26 | ok | ||
27 | ok | ||
28 | ok | ||
29 | ok | ||
30 | ok | ||
31 | ok | ||
32 | ok | ||
33 | ok | ||
34 | ok | ||
35 | ok | ||
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 | ||
4 | case '!' in [\!] ) echo ok;; *) echo 'WRONG!';; esac | ||
5 | case '"' in [\"] ) echo ok;; *) echo 'WRONG"';; esac | ||
6 | case '#' in [\#] ) echo ok;; *) echo 'WRONG#';; esac | ||
7 | case '$' in [\$] ) echo ok;; *) echo 'WRONG$';; esac | ||
8 | case '%' in [\%] ) echo ok;; *) echo 'WRONG%';; esac | ||
9 | case '&' in [\&] ) echo ok;; *) echo 'WRONG&';; esac | ||
10 | case "'" in [\'] ) echo ok;; *) echo "WRONG'";; esac | ||
11 | case '(' in [\(] ) echo ok;; *) echo 'WRONG(';; esac | ||
12 | case ')' in [\)] ) echo ok;; *) echo 'WRONG)';; esac | ||
13 | case '*' in [\*] ) echo ok;; *) echo 'WRONG*';; esac | ||
14 | case '+' in [\+] ) echo ok;; *) echo 'WRONG+';; esac | ||
15 | case ',' in [\,] ) echo ok;; *) echo 'WRONG,';; esac | ||
16 | case '-' in [\-] ) echo ok;; *) echo 'WRONG-';; esac | ||
17 | case '-' in [a\-c]) echo ok;; *) echo 'WRONGa\-c';; esac | ||
18 | case '.' in [\.] ) echo ok;; *) echo 'WRONG.';; esac | ||
19 | case '/' in [\/] ) echo ok;; *) echo 'WRONG/';; esac | ||
20 | # 3a..40 | ||
21 | case ':' in [\:] ) echo ok;; *) echo 'WRONG:';; esac | ||
22 | case ';' in [\;] ) echo ok;; *) echo 'WRONG;';; esac | ||
23 | case '<' in [\<] ) echo ok;; *) echo 'WRONG<';; esac | ||
24 | case '=' in [\=] ) echo ok;; *) echo 'WRONG=';; esac | ||
25 | case '>' in [\>] ) echo ok;; *) echo 'WRONG>';; esac | ||
26 | case '?' in [\?] ) echo ok;; *) echo 'WRONG?';; esac | ||
27 | case '@' in [\@] ) echo ok;; *) echo 'WRONG@';; esac | ||
28 | # 5b..60 | ||
29 | case '[' in [\[] ) echo ok;; *) echo 'WRONG[';; esac | ||
30 | case '\' in [\\] ) echo ok;; *) echo 'WRONG\';; esac | ||
31 | case '\' in \\ ) echo ok;; *) echo 'WRONG\\';; esac | ||
32 | case ']' in [\]] ) echo ok;; *) echo 'WRONG]';; esac | ||
33 | case ']' in [a\]]) echo ok;; *) echo 'WRONGa]';; esac | ||
34 | case '^' in [\^] ) echo ok;; *) echo 'WRONG^';; esac | ||
35 | case '_' in [\_] ) echo ok;; *) echo 'WRONG_';; esac | ||
36 | case '`' in [\`] ) echo ok;; *) echo 'WRONG`';; esac | ||
37 | # 7b..7e | ||
38 | case '{' in [\{] ) echo ok;; *) echo 'WRONG{';; esac | ||
39 | case '|' in [\|] ) echo ok;; *) echo 'WRONG|';; esac | ||
40 | case '}' in [\}] ) echo ok;; *) echo 'WRONG}';; esac | ||
41 | case '~' 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 @@ | |||
1 | ok | ||
2 | ok | ||
3 | ok | ||
4 | ok | ||
5 | ok | ||
6 | ok | ||
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 | ||
2 | case π in | ||
3 | ( "π" ) echo ok ;; | ||
4 | ( * ) echo WRONG ;; | ||
5 | esac | ||
6 | # Unicode: cf 81 | ||
7 | case ρ in | ||
8 | ( "ρ" ) echo ok ;; | ||
9 | ( * ) echo WRONG ;; | ||
10 | esac | ||
11 | # Unicode: cf 82 | ||
12 | case ς in | ||
13 | ( "ς" ) echo ok ;; | ||
14 | ( * ) echo WRONG ;; | ||
15 | esac | ||
16 | |||
17 | case "π" in | ||
18 | ( π ) echo ok ;; | ||
19 | ( * ) echo WRONG ;; | ||
20 | esac | ||
21 | case "ρ" in | ||
22 | ( ρ ) echo ok ;; | ||
23 | ( * ) echo WRONG ;; | ||
24 | esac | ||
25 | case "ς" in | ||
26 | ( ς ) echo ok ;; | ||
27 | ( * ) echo WRONG ;; | ||
28 | esac | ||
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 | 1 | Backgrounded pipes shall have their stdin redirected to /dev/null |
2 | OK | 2 | Zero:0 |
3 | Zero:0 | ||
4 | Done | ||
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 | 1 | echo "Backgrounded pipes shall have their stdin redirected to /dev/null" |
2 | echo LOST >&10 | 2 | |
3 | echo 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 | |||
8 | cat & wait; echo Zero:$? | ||
9 | |||
10 | # This does not work for bash! bash bug? | ||
11 | cat | cat & wait; echo Zero:$? | ||
12 | |||
13 | echo 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 | ||
2 | OK | ||
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 | ||
2 | echo LOST >&10 | ||
3 | echo 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 @@ | |||
1 | LANG=en_US.UTF-8 | ||
2 | LC_ALL=en_US.UTF-8 | ||
1 | X=abcdÉfghÍjklmnÓpqrstÚvwcyz | 3 | X=abcdÉfghÍjklmnÓpqrstÚvwcyz |
2 | echo ${#X} | 4 | echo ${#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}'" | |||
5 | echo "varoffset2 '${parameter:${offset}}'" | 5 | echo "varoffset2 '${parameter:${offset}}'" |
6 | echo "varoffset-2 '${parameter:${noffset}}'" | 6 | echo "varoffset-2 '${parameter:${noffset}}'" |
7 | echo "literal '2' '${parameter:2}'" | 7 | echo "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 |
10 | echo "literal '-2' '${parameter:-2}'" | 10 | echo "literal '-2' '${parameter:-2}'" |
11 | echo "literal ' -2' '${parameter: -2}'" | 11 | echo "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 @@ | |||
1 | all |0123456 | ||
2 | 4: |456 | ||
3 | 4:2 |45 | ||
4 | 4:-1 |45 | ||
5 | 4:-2 |4 | ||
6 | 4:-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 | | ||
23 | Ok: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 @@ | |||
1 | set -- 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} | ||
14 | i=2; echo "-4:i=2 |"${1: -4:i} | ||
15 | i=-2; echo "-4:i=-2|"${1: -4:i} | ||
16 | i=-3; echo "-4:i=-3|"${1: -4:i} | ||
17 | i=-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} | ||
24 | echo 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 @@ | |||
1 | Expected Actual | ||
2 | a*z : a*z | ||
3 | \z : \z | ||
4 | a1z a2z: a1z a2z | ||
5 | z : 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' | ||
5 | v='a bz'; echo 'a*z :' "${v/a*z/a*z}" | ||
6 | v='a bz'; echo '\z :' "${v/a*z/\z}" | ||
7 | v='a bz'; echo 'a1z a2z:' ${v/a*z/a*z} | ||
8 | v='a bz'; echo 'z :' ${v/a*z/\z} | ||
9 | rm 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 | ||
3 | TOPDIR=$PWD | 3 | TOPDIR=`pwd` |
4 | 4 | ||
5 | test -x ash || { | 5 | test -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 $? | |||
10 | test -x recho || gcc -O2 -o recho recho.c || exit $? | 10 | test -x recho || gcc -O2 -o recho recho.c || exit $? |
11 | test -x zecho || gcc -O2 -o zecho zecho.c || exit $? | 11 | test -x zecho || gcc -O2 -o zecho zecho.c || exit $? |
12 | 12 | ||
13 | PATH="$PWD:$PATH" # for ash and recho/zecho/printenv | 13 | PATH="`pwd`:$PATH" # for ash and recho/zecho/printenv |
14 | export PATH | 14 | export PATH |
15 | 15 | ||
16 | THIS_SH="$PWD/ash" | 16 | THIS_SH="`pwd`/ash" |
17 | export THIS_SH | 17 | export THIS_SH |
18 | 18 | ||
19 | do_test() | 19 | do_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 | ||
61 | ret=0 | ||
62 | |||
59 | if [ $# -lt 1 ]; then | 63 | if [ $# -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 |
69 | else | 73 | else |
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 |
76 | fi | 80 | fi |
81 | |||
82 | exit ${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 { | |||
753 | static const char o_opt_strings[] ALIGN1 = | 772 | static 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 = | |||
760 | enum { | 780 | enum { |
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 |
930 | static int builtin_export(char **argv) FAST_FUNC; | 971 | static int builtin_export(char **argv) FAST_FUNC; |
931 | #endif | 972 | #endif |
973 | #if ENABLE_HUSH_READONLY | ||
974 | static int builtin_readonly(char **argv) FAST_FUNC; | ||
975 | #endif | ||
932 | #if ENABLE_HUSH_JOB | 976 | #if ENABLE_HUSH_JOB |
933 | static int builtin_fg_bg(char **argv) FAST_FUNC; | 977 | static int builtin_fg_bg(char **argv) FAST_FUNC; |
934 | static int builtin_jobs(char **argv) FAST_FUNC; | 978 | static 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 | ||
1443 | static 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 | ||
1393 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC) | 1457 | static 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 | } |
1435 | static int save_FILEs_on_redirect(int fd) | 1504 | static 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 |
1461 | static void close_all_FILE_list(void) | 1532 | static 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 | ||
1487 | static void save_and_replace_G_args(save_arg_t *sv, char **argv) | 1558 | static 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 | ||
1506 | static void restore_G_args(save_arg_t *sv, char **argv) | 1572 | static 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) | 2065 | static 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 | ||
2013 | static 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 */ |
2133 | static void set_pwd_var(int exp) | 2185 | static 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 | ||
2139 | static int unset_local_var_len(const char *name, int name_len) | 2190 | static int unset_local_var_len(const char *name, int name_len) |
@@ -2191,7 +2242,7 @@ static void unset_vars(char **strings) | |||
2191 | static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val) | 2242 | static 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 |
5331 | static char *encode_then_expand_string(const char *str, int process_bkslash, int do_unbackslash) | 5436 | static 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 | */ |
5950 | static char *expand_string_to_string(const char *str, int do_unbackslash) | 6065 | static 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 */ |
5986 | static char* expand_strvec_to_string(char **argv) | 6101 | static 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). | 6642 | struct 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 */ |
6531 | static 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 | |||
6649 | static 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 | */ | ||
6685 | static 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 | ||
6579 | static void restore_redirects(int squirrel[3]) | 6716 | static 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. */ |
6597 | static int setup_redirects(struct command *prog, int squirrel[]) | 6743 | static 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 | ||
7171 | static void insert_bg_job(struct pipe *pi) | 7315 | static 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 | |||
7332 | static void delete_finished_job(struct pipe *pi) | ||
7333 | { | ||
7334 | remove_job_from_table(pi); | ||
7335 | free_pipe(pi); | ||
7336 | } | ||
7337 | |||
7338 | static 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 | |||
7344 | static 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 | |||
7203 | static 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 */ | ||
7222 | static 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 | ||
7229 | static int job_exited_or_stopped(struct pipe *pi) | 7378 | static 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) | |||
7505 | static int redirect_and_varexp_helper(char ***new_env_p, | 7669 | static 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 | ||
8810 | int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
8811 | int 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 |
8828 | static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) | 9006 | static 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 | 9325 | static int helper_export_local(char **argv, unsigned flags) |
9152 | #define helper_export_local(argv, exp, lvl) \ | ||
9153 | helper_export_local(argv, exp) | ||
9154 | # endif | ||
9155 | static 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 | ||
9458 | static 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 @@ | |||
1 | set -e | ||
2 | (true) | ||
3 | echo OK | ||
4 | (false) | ||
5 | echo 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 @@ | |||
1 | 2 3 4 | ||
2 | hush: Illegal number: -1 | ||
3 | 1 2 3 4 | ||
4 | 1 2 3 4 | ||
5 | 2 3 4 | ||
6 | 3 4 | ||
7 | 4 | ||
8 | |||
9 | 1 2 3 4 | ||
10 | 1 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 @@ | |||
1 | Sending 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 | |||
41 | echo 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 | ||
2 | echo _`"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 @@ | |||
1 | 0 | ||
2 | 3 | ||
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 | ||
4 | echo $? | ||
5 | wait $! | ||
6 | echo $? | ||
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 @@ | |||
1 | First | ||
2 | Second | ||
3 | Third | ||
4 | Done | ||
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 | |||
26 | echo First && sleep 0.2 && echo Third & | ||
27 | sleep 0.1 | ||
28 | echo Second | ||
29 | wait | ||
30 | echo 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 @@ | |||
1 | ok | ||
2 | ok | ||
3 | ok | ||
4 | ok | ||
5 | ok | ||
6 | ok | ||
7 | ok | ||
8 | ok | ||
9 | ok | ||
10 | ok | ||
11 | ok | ||
12 | ok | ||
13 | ok | ||
14 | ok | ||
15 | ok | ||
16 | ok | ||
17 | ok | ||
18 | ok | ||
19 | ok | ||
20 | ok | ||
21 | ok | ||
22 | ok | ||
23 | ok | ||
24 | ok | ||
25 | ok | ||
26 | ok | ||
27 | ok | ||
28 | ok | ||
29 | ok | ||
30 | ok | ||
31 | ok | ||
32 | ok | ||
33 | ok | ||
34 | ok | ||
35 | ok | ||
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 | ||
4 | case '!' in [\!] ) echo ok;; *) echo 'WRONG!';; esac | ||
5 | case '"' in [\"] ) echo ok;; *) echo 'WRONG"';; esac | ||
6 | case '#' in [\#] ) echo ok;; *) echo 'WRONG#';; esac | ||
7 | case '$' in [\$] ) echo ok;; *) echo 'WRONG$';; esac | ||
8 | case '%' in [\%] ) echo ok;; *) echo 'WRONG%';; esac | ||
9 | case '&' in [\&] ) echo ok;; *) echo 'WRONG&';; esac | ||
10 | case "'" in [\'] ) echo ok;; *) echo "WRONG'";; esac | ||
11 | case '(' in [\(] ) echo ok;; *) echo 'WRONG(';; esac | ||
12 | case ')' in [\)] ) echo ok;; *) echo 'WRONG)';; esac | ||
13 | case '*' in [\*] ) echo ok;; *) echo 'WRONG*';; esac | ||
14 | case '+' in [\+] ) echo ok;; *) echo 'WRONG+';; esac | ||
15 | case ',' in [\,] ) echo ok;; *) echo 'WRONG,';; esac | ||
16 | case '-' in [\-] ) echo ok;; *) echo 'WRONG-';; esac | ||
17 | case '-' in [a\-c]) echo ok;; *) echo 'WRONGa\-c';; esac | ||
18 | case '.' in [\.] ) echo ok;; *) echo 'WRONG.';; esac | ||
19 | case '/' in [\/] ) echo ok;; *) echo 'WRONG/';; esac | ||
20 | # 3a..40 | ||
21 | case ':' in [\:] ) echo ok;; *) echo 'WRONG:';; esac | ||
22 | case ';' in [\;] ) echo ok;; *) echo 'WRONG;';; esac | ||
23 | case '<' in [\<] ) echo ok;; *) echo 'WRONG<';; esac | ||
24 | case '=' in [\=] ) echo ok;; *) echo 'WRONG=';; esac | ||
25 | case '>' in [\>] ) echo ok;; *) echo 'WRONG>';; esac | ||
26 | case '?' in [\?] ) echo ok;; *) echo 'WRONG?';; esac | ||
27 | case '@' in [\@] ) echo ok;; *) echo 'WRONG@';; esac | ||
28 | # 5b..60 | ||
29 | case '[' in [\[] ) echo ok;; *) echo 'WRONG[';; esac | ||
30 | case '\' in [\\] ) echo ok;; *) echo 'WRONG\';; esac | ||
31 | case '\' in \\ ) echo ok;; *) echo 'WRONG\\';; esac | ||
32 | case ']' in [\]] ) echo ok;; *) echo 'WRONG]';; esac | ||
33 | case ']' in [a\]]) echo ok;; *) echo 'WRONGa]';; esac | ||
34 | case '^' in [\^] ) echo ok;; *) echo 'WRONG^';; esac | ||
35 | case '_' in [\_] ) echo ok;; *) echo 'WRONG_';; esac | ||
36 | case '`' in [\`] ) echo ok;; *) echo 'WRONG`';; esac | ||
37 | # 7b..7e | ||
38 | case '{' in [\{] ) echo ok;; *) echo 'WRONG{';; esac | ||
39 | case '|' in [\|] ) echo ok;; *) echo 'WRONG|';; esac | ||
40 | case '}' in [\}] ) echo ok;; *) echo 'WRONG}';; esac | ||
41 | case '~' 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 @@ | |||
1 | ok | ||
2 | ok | ||
3 | ok | ||
4 | ok | ||
5 | ok | ||
6 | ok | ||
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 | ||
2 | case π in | ||
3 | ( "π" ) echo ok ;; | ||
4 | ( * ) echo WRONG ;; | ||
5 | esac | ||
6 | # Unicode: cf 81 | ||
7 | case ρ in | ||
8 | ( "ρ" ) echo ok ;; | ||
9 | ( * ) echo WRONG ;; | ||
10 | esac | ||
11 | # Unicode: cf 82 | ||
12 | case ς in | ||
13 | ( "ς" ) echo ok ;; | ||
14 | ( * ) echo WRONG ;; | ||
15 | esac | ||
16 | |||
17 | case "π" in | ||
18 | ( π ) echo ok ;; | ||
19 | ( * ) echo WRONG ;; | ||
20 | esac | ||
21 | case "ρ" in | ||
22 | ( ρ ) echo ok ;; | ||
23 | ( * ) echo WRONG ;; | ||
24 | esac | ||
25 | case "ς" in | ||
26 | ( ς ) echo ok ;; | ||
27 | ( * ) echo WRONG ;; | ||
28 | esac | ||
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 @@ | |||
1 | hush: can't open '/does/not/exist': No such file or directory | 1 | TEST |
2 | One:1 | 2 | hush: can't duplicate file descriptor: Bad file descriptor |
3 | hush: can't open '/cant/be/created': No such file or directory | ||
4 | One:1 | ||
5 | Ok | ||
6 | hush: can't open '/cant/be/created': No such file or directory | ||
7 | Zero:0 | ||
8 | hush: can't open '/cant/be/created': No such file or directory | ||
9 | One:1 | ||
10 | hush: can't open '/cant/be/created': No such file or directory | ||
11 | One:1 | ||
12 | hush: can't open '/cant/be/created': No such file or directory | ||
13 | Zero:0 | ||
14 | Done | ||
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 @@ | |||
1 | echo Error >/does/not/exist; echo One:$? | 1 | # redirects to closed descriptors should not leave these descriptors |
2 | t=BAD | 2 | # open afterwards |
3 | t=Ok >>/cant/be/created; echo One:$? | 3 | echo TEST 9>/dev/null |
4 | echo $t | 4 | echo MUST ERROR OUT >&9 |
5 | ! >/cant/be/created; echo Zero:$? | 5 | echo "Output to fd#9: $?" |
6 | exec >/cant/be/created; echo One:$? | ||
7 | exec /bin/true >/cant/be/created; echo One:$? | ||
8 | ! exec /bin/true >/cant/be/created; echo Zero:$? | ||
9 | echo 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 @@ | |||
1 | hush: can't open '/does/not/exist': No such file or directory | ||
2 | One:1 | ||
3 | hush: can't open '/cant/be/created': No such file or directory | ||
4 | One:1 | ||
5 | Ok | ||
6 | hush: can't open '/cant/be/created': No such file or directory | ||
7 | Zero:0 | ||
8 | hush: can't open '/cant/be/created': No such file or directory | ||
9 | One:1 | ||
10 | hush: can't open '/cant/be/created': No such file or directory | ||
11 | One:1 | ||
12 | hush: can't open '/cant/be/created': No such file or directory | ||
13 | Zero:0 | ||
14 | Done | ||
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 @@ | |||
1 | echo Error >/does/not/exist; echo One:$? | ||
2 | t=BAD | ||
3 | t=Ok >>/cant/be/created; echo One:$? | ||
4 | echo $t | ||
5 | ! >/cant/be/created; echo Zero:$? | ||
6 | exec >/cant/be/created; echo One:$? | ||
7 | exec /bin/true >/cant/be/created; echo One:$? | ||
8 | ! exec /bin/true >/cant/be/created; echo Zero:$? | ||
9 | echo 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 | ||
2 | echo LOST >&10 | ||
3 | echo 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 @@ | |||
1 | hush: trap: BADNAME: invalid signal specification | ||
2 | 1 | ||
3 | Trapped | ||
4 | Ok | ||
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 | |||
3 | trap "echo Trapped" BADNAME TERM; echo $? | ||
4 | kill $$ | ||
5 | echo 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 @@ | |||
1 | Removing traps | ||
2 | End of exit_func | ||
3 | Done: 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 ' | ||
2 | exit_func() { | ||
3 | echo "Removing traps" | ||
4 | trap - EXIT TERM INT | ||
5 | echo "End of exit_func" | ||
6 | } | ||
7 | set -e | ||
8 | trap exit_func EXIT TERM INT | ||
9 | sleep 2 | ||
10 | exit 77 | ||
11 | ' & | ||
12 | |||
13 | sleep 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. | ||
16 | kill -TERM %1 | ||
17 | wait | ||
18 | echo 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 @@ | |||
1 | Removing traps | ||
2 | End of exit_func | ||
3 | Done: 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 ' | ||
6 | exit_func() { | ||
7 | echo "Removing traps" | ||
8 | trap - EXIT TERM INT | ||
9 | echo "End of exit_func" | ||
10 | } | ||
11 | set -e | ||
12 | trap exit_func EXIT TERM INT | ||
13 | sleep 2 | ||
14 | exit 77 | ||
15 | ' & | ||
16 | |||
17 | child=$! | ||
18 | sleep 1 | ||
19 | kill -TERM $child | ||
20 | wait | ||
21 | echo 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 @@ | |||
1 | readonly a=A | ||
2 | readonly b=B | ||
3 | Ok:0 | ||
4 | |||
5 | hush: a=A: readonly variable | ||
6 | Fail:1 | ||
7 | hush: a=A: readonly variable | ||
8 | Fail:1 | ||
9 | |||
10 | hush: a=Z: readonly variable | ||
11 | Fail:1 | ||
12 | |||
13 | hush: a=Z: readonly variable | ||
14 | b=B | ||
15 | ^^^a is not exported | ||
16 | hush: a=Z: readonly variable | ||
17 | Visible:42 | ||
18 | |||
19 | hush: a: readonly variable | ||
20 | Fail: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 @@ | |||
1 | unset a b | ||
2 | # | ||
3 | readonly a=A | ||
4 | b=B | ||
5 | readonly b | ||
6 | # readonly on already readonly var is harmless: | ||
7 | readonly b a | ||
8 | readonly | grep '^readonly [ab]=' | ||
9 | # this should work: | ||
10 | export a b | ||
11 | export -n a b | ||
12 | echo Ok:$? | ||
13 | env | grep -e^a= -e^b= # shows nothing | ||
14 | |||
15 | echo | ||
16 | # these should all fail (despite the same value being assigned) | ||
17 | # bash does not abort even in non-interactive more (in script) | ||
18 | true; a=A | ||
19 | echo Fail:$? | ||
20 | true; readonly a=A | ||
21 | echo Fail:$? | ||
22 | |||
23 | echo | ||
24 | # in bash, assignment in export fails, but export succeeds! :) | ||
25 | # we don't mimic that! | ||
26 | true; export a=Z | ||
27 | echo Fail:$? | ||
28 | #env | grep '^a=' | ||
29 | #echo "^^^a is exported" | ||
30 | export -n a # undo that bashism, if it happens | ||
31 | |||
32 | echo | ||
33 | export b | ||
34 | # this fails to both set and export a: | ||
35 | a=Z env | grep '^[ab]=' | ||
36 | echo "^^^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 | |||
40 | echo | ||
41 | true; unset a | ||
42 | echo 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 @@ | |||
1 | hush: a=Z: readonly variable | ||
2 | Visible:42 | ||
3 | |||
4 | hush: a=Z: readonly variable | ||
5 | Visible: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 @@ | |||
1 | unset a | ||
2 | readonly a=A | ||
3 | |||
4 | # external commands and builtins should behave the same: | ||
5 | (exit 42); a=Z echo "Visible:$?" | ||
6 | echo | ||
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 @@ | |||
1 | LANG=en_US.UTF-8 | ||
2 | LC_ALL=en_US.UTF-8 | ||
3 | X=abcdÉfghÍjklmnÓpqrstÚvwcyz | ||
4 | echo ${#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 @@ | |||
1 | parameter 'abcdef' | ||
2 | varoffset2 'cdef' | ||
3 | varoffset-2 'ef' | ||
4 | literal '2' 'cdef' | ||
5 | literal '-2' 'abcdef' | ||
6 | literal ' -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 @@ | |||
1 | parameter=abcdef | ||
2 | offset=2 | ||
3 | noffset=-2 | ||
4 | echo "parameter '${parameter}'" | ||
5 | echo "varoffset2 '${parameter:${offset}}'" | ||
6 | echo "varoffset-2 '${parameter:${noffset}}'" | ||
7 | echo "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 | ||
10 | echo "literal '-2' '${parameter:-2}'" | ||
11 | echo "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 @@ | |||
1 | all |0123456 | ||
2 | 4: |456 | ||
3 | 4:2 |45 | ||
4 | 4:-1 |45 | ||
5 | 4:-2 |4 | ||
6 | 4:-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 | | ||
23 | Ok: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 @@ | |||
1 | set -- 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} | ||
14 | i=2; echo "-4:i=2 |"${1: -4:i} | ||
15 | i=-2; echo "-4:i=-2|"${1: -4:i} | ||
16 | i=-3; echo "-4:i=-3|"${1: -4:i} | ||
17 | i=-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} | ||
24 | echo 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 | |||
9 | unset LC_TIME | 9 | unset LC_TIME |
10 | unset LC_ALL | 10 | unset LC_ALL |
11 | 11 | ||
12 | TOPDIR=`pwd` | ||
13 | |||
12 | if test ! -x hush; then | 14 | if 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 |
97 | else | 102 | else |
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 @@ | |||
1 | test "`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 | |||
31 | int blkdiscard_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 38 | int blkdiscard_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
32 | int blkdiscard_main(int argc UNUSED_PARAM, char **argv) | 39 | int 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) */ | ||
188 | static const char msg_building_new_label[] ALIGN1 = | 190 | static 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 | ||
193 | static const char msg_part_already_defined[] ALIGN1 = | 195 | static 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 | ||
197 | struct partition { | 200 | struct 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() { | |||
47 | kilobytes=60 | 47 | kilobytes=60 |
48 | while true; do | 48 | while 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 |
52 | done | 52 | done |
53 | 53 | ||
@@ -56,7 +56,7 @@ done | |||
56 | kilobytes=$((1 * 8*1024 - 50)) | 56 | kilobytes=$((1 * 8*1024 - 50)) |
57 | while true; do | 57 | while 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 |
61 | done | 61 | done |
62 | 62 | ||
@@ -65,7 +65,7 @@ done | |||
65 | kilobytes=$((2 * 8*1024 - 50)) | 65 | kilobytes=$((2 * 8*1024 - 50)) |
66 | while true; do | 66 | while 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 |
70 | done | 70 | done |
71 | 71 | ||
@@ -74,7 +74,7 @@ done | |||
74 | kilobytes=$((3 * 8*1024 - 50)) | 74 | kilobytes=$((3 * 8*1024 - 50)) |
75 | while true; do | 75 | while 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 |
79 | done | 79 | done |
80 | 80 | ||
@@ -83,7 +83,7 @@ done | |||
83 | kilobytes=$((4 * 8*1024 - 50)) | 83 | kilobytes=$((4 * 8*1024 - 50)) |
84 | while true; do | 84 | while 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 |
88 | done | 88 | done |
89 | 89 | ||
@@ -92,7 +92,7 @@ done | |||
92 | kilobytes=$((5 * 8*1024 - 50)) | 92 | kilobytes=$((5 * 8*1024 - 50)) |
93 | while true; do | 93 | while 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 |
97 | done | 97 | done |
98 | exit | 98 | exit |
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 */ | ||
88 | extern int capset(cap_user_header_t header, cap_user_data_t data); | ||
89 | extern 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 | |||
115 | enum { | ||
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 | ||
128 | struct 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 | ||
135 | static 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 | |||
177 | static 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 | |||
214 | static 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 | |||
251 | static 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 | |||
279 | static 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 | ||
302 | static 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 | |||
314 | static 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 | |||
407 | int setpriv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
408 | int 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" |