diff options
| author | Ron Yorston <rmy@pobox.com> | 2017-07-18 15:58:52 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2017-07-18 15:58:52 +0100 |
| commit | b680f05ad449505e3d914bebd4c8d83bf768c094 (patch) | |
| tree | c08ded13d430b0e7e0104f2eb594fad190ce98a3 | |
| parent | 258200ff81d5a9da54dab35acf36213eff1e399b (diff) | |
| parent | 513a2457b65894b10b9fd6aa8753fca59eced08c (diff) | |
| download | busybox-w32-b680f05ad449505e3d914bebd4c8d83bf768c094.tar.gz busybox-w32-b680f05ad449505e3d914bebd4c8d83bf768c094.tar.bz2 busybox-w32-b680f05ad449505e3d914bebd4c8d83bf768c094.zip | |
Merge branch 'busybox' into merge
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" |
